From 99748deb93701c3ccc35e8b1b5554d6f5316451b Mon Sep 17 00:00:00 2001 From: taynpg Date: Fri, 8 Mar 2024 14:07:44 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0qt=E7=9B=B8=E5=85=B3=E5=86=85?= =?UTF-8?q?=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qt/demo/CVlcKit.cpp | 134 +++++++++++++++++++++++++++++++++++ qt/demo/CVlcKit.h | 52 ++++++++++++++ qt/qt-knowledge.txt | 167 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 353 insertions(+) create mode 100644 qt/demo/CVlcKit.cpp create mode 100644 qt/demo/CVlcKit.h create mode 100644 qt/qt-knowledge.txt diff --git a/qt/demo/CVlcKit.cpp b/qt/demo/CVlcKit.cpp new file mode 100644 index 0000000..d2f5b02 --- /dev/null +++ b/qt/demo/CVlcKit.cpp @@ -0,0 +1,134 @@ +// +// Created by Administrator on 2023-09-17. +// + +#include "CVlcKit.h" + +CVlcKit::CVlcKit() = default; + +CVlcKit::~CVlcKit() +{ + libvlc_media_player_release(m_pMediaPlayer); + libvlc_release(m_pInstance); +} + +libvlc_media_player_t *CVlcKit::getPlayerPoint() const { + return m_pMediaPlayer; +} + +bool CVlcKit::InitVLC() { + + m_pInstance = libvlc_new(0, nullptr); + if (!m_pInstance) { + m_szError = u8"libvlc new failed!"; + return false; + } + + m_pMediaPlayer = libvlc_media_player_new(m_pInstance); + if (!m_pMediaPlayer) { + libvlc_release(m_pInstance); + m_szError = u8"libvlc_media_player_new failed!"; + return false; + } + m_event = libvlc_media_player_event_manager(m_pMediaPlayer); + libvlc_event_attach(m_event, libvlc_MediaPlayerPositionChanged, vlc_call, this); + libvlc_event_attach(m_event, libvlc_MediaPlayerAudioVolume, vlc_call, this); + + return true; +} + +void CVlcKit::vlc_call(const struct libvlc_event_t *p_event, void *p_data) { + auto* pThis = static_cast(p_data); + if (!pThis) { + return; + } + switch (p_event->type) { + case libvlc_MediaPlayerPositionChanged: + { + float pos = libvlc_media_player_get_position(pThis->m_pMediaPlayer); + emit pThis->sigSetTimeSlider(static_cast(pos * 100)); + long long current = libvlc_media_player_get_time(pThis->m_pMediaPlayer) / 1000; + emit pThis->sigSetStringTime(current); + break; + } + case libvlc_MediaPlayerAudioVolume: + { + int vol = libvlc_audio_get_volume(pThis->m_pMediaPlayer); + emit pThis->sigSetVolumeSlider(vol); + break; + } + default: + break; + } +} + +void CVlcKit::stopVideo() { + if (!m_pMediaPlayer) { + return; + } + if (libvlc_media_player_get_state(m_pMediaPlayer) == libvlc_state_t::libvlc_Playing) { + libvlc_media_player_stop(m_pMediaPlayer); + } +} + +void CVlcKit::pauseVideo() { + if (!m_pMediaPlayer) { + return; + } + if (libvlc_media_player_get_state(m_pMediaPlayer) == libvlc_state_t::libvlc_Playing) { + libvlc_media_player_pause(m_pMediaPlayer); + } +} + +void CVlcKit::playVideo() { + if (!m_pMediaPlayer) { + return; + } + if (libvlc_media_player_get_state(m_pMediaPlayer) == libvlc_state_t::libvlc_Paused + || libvlc_media_player_get_state(m_pMediaPlayer) == libvlc_state_t::libvlc_Stopped) { + libvlc_media_player_play(m_pMediaPlayer); + } +} + +bool CVlcKit::openVideo(const char* path, void* pWindow) { + // 设置路径 + m_pMedia = libvlc_media_new_path(m_pInstance, path); + if (!m_pMedia) { + m_szError = u8"libvlc_media_new_path failed!"; + return false; + } + // 解析media + libvlc_media_parse(m_pMedia); + // 获取总时长 (ms) + m_totalSec = libvlc_media_get_duration(m_pMedia); + emit sigSetTotalSecond(m_totalSec / 1000); + // 设置media + libvlc_media_player_set_media(m_pMediaPlayer, m_pMedia); + // 设置播放窗口句柄 + libvlc_media_player_set_hwnd(m_pMediaPlayer, pWindow); + // 释放media + libvlc_media_release(m_pMedia); + m_pMedia = nullptr; + // 播放视频 + libvlc_media_player_play(m_pMediaPlayer); + return true; +} + +std::string CVlcKit::getLastError() const { + return m_szError; +} + +void CVlcKit::setPlayPostion(float pos) { + if (!m_pMediaPlayer) { + return ; + } + libvlc_media_player_set_position(m_pMediaPlayer, static_cast(pos / 100.0)); +} + +void CVlcKit::setVolume(int vol) { + if (!m_pMediaPlayer) { + return ; + } + libvlc_audio_set_volume(m_pMediaPlayer, vol); +} + diff --git a/qt/demo/CVlcKit.h b/qt/demo/CVlcKit.h new file mode 100644 index 0000000..c8b4cdf --- /dev/null +++ b/qt/demo/CVlcKit.h @@ -0,0 +1,52 @@ +// +// Created by Administrator on 2023-09-17. +// + +#ifndef VLCPLAYER_CVLCKIT_H +#define VLCPLAYER_CVLCKIT_H + +#ifdef _WIN32 +#include +typedef SSIZE_T ssize_t; +#endif +#include +#include +#include + +class CVlcKit : public QObject +{ + Q_OBJECT +public: + CVlcKit(); + ~CVlcKit() override; +public: + bool InitVLC(); + [[nodiscard]] std::string getLastError() const; + [[nodiscard]] libvlc_media_player_t* getPlayerPoint() const; +public: + void pauseVideo(); + void playVideo(); + void stopVideo(); + bool openVideo(const char* path, void* pWindow); + void setPlayPostion(float pos); + void setVolume(int vol); +signals: + void sigSetTimeSlider(int value); + void sigSetVolumeSlider(int value); + void sigSetStringTime(long long value); + void sigSetTotalSecond(long long value); + +private: + static void vlc_call( const struct libvlc_event_t *p_event, void *p_data ); +private: + libvlc_instance_t* m_pInstance{}; + libvlc_media_player_t* m_pMediaPlayer{}; + libvlc_media_t* m_pMedia{}; + libvlc_event_manager_t* m_event{}; + + libvlc_time_t m_totalSec = -1; + std::string m_szError; +}; + + +#endif //VLCPLAYER_CVLCKIT_H diff --git a/qt/qt-knowledge.txt b/qt/qt-knowledge.txt new file mode 100644 index 0000000..6ad59fb --- /dev/null +++ b/qt/qt-knowledge.txt @@ -0,0 +1,167 @@ +1.使用信号槽发送结构体信息,使用 qRegisterMetaType 注册一下。 +qRegisterMetaType("SParam"); +2.sendParamValue 尽管设置成了 const reference,但是还会有一次构造 +因为 Qt 一下两个原因: +1.线程安全性:Qt 的信号槽机制支持多线程,在多线程环境中,如果参数是引用,可能会引发线程安全问题。 + 2.松耦合性:通过传值而不是引用,信号发送方和接收方可以更独立,不必担心在信号槽连接时传递的对象是否仍然有效。 + +1.Qt的三个窗口类的区别 +(1)QMainWindow:包含菜单栏、工具栏、状态栏。 +(2)QWidget:普通窗口、大部分窗口要做成无边框的,这个使用较多。 +(3)QDialog:对话框,常用于做登录窗口、弹出窗口。 + +2.类似保存上次访问信息这存储 +(1)使用QSettings +示例:ini, /LastPath/path +(2)一些常用的位置在 QStandardPaths::writableLocation(enum); + +3.信号 +signals: void xxx(int value); --> emit xxx(nValue); +private slots: void yyy(); + +4.子线程无法直接修改UI,需要使用信号处理。 +5.在VS中写QT时开启cmd窗口: +项目--属性--链接器--系统--子系统--控制台 (/SUBSYSTEM:CONSOLE) + +6.信号重载: +(1)使用 Qt4 的写法 +(2)Qt5 的写法,参数二 => QOverlaod::of(xx) + +7.moc全称是Meta-Object Compiler(元对象编译器) +moc -> 分析C++(若有Q_OBJECT) -> moc_xx.cpp(包含QOBJECT实现) +--> 进入编译(预处理器之前,与源文件一起编译而不是替换) +可以理解为:moc把Qt中不是C++的关键字解析让其认识。 + +8.界面布局 +(1)设定min和max就是固定 +(2)配合弹簧布局 +(3)无边框:setWindowFlags(Qt::FramelessWindowHint) +(4)任务栏单机隐藏显示 Qt::WindowMinMaxButtonsHint +(5)Layout: +setMargin: 与边界间隙。 +setSpacing: 与相邻空间间隙(默认大概是7)。 +addSpacing: 在 setSpacing 基础上加减。 +addStretch: 添加一个 QSpacerItem +(6)QSplitter分裂器布局 +setOpaqueResize(false): 鼠标松开才变换。 +(7)全局setLayout如果不清除的话仅第一次生效。 +(8)清空布局内的所有元素: +QLayoutItem* pChild; layout = this->layout(); +while((pChild = layout->takeAt(0)) != 0) if (pChild->widget()) xx->setParent(nullptr) delete; +(9)Widget也可以作为一个容器用于布局。 +(10) resize 可以设定首次启动时的界面大小。 + +9.QSS相关 +(1)给QPushButton设置背景图: +QPushButton { + background-image: url(:/resource/xx.png); + border: none // 无边框; + QPushButton::hover { + background-color: rgb(192, 192, 192); // 鼠标悬浮时 + } +} +(2)QTabWidget +QTabWidget::pane { + border-top: 1px solid #EAEAEA; // 上边框 + position: absolute; + top: -0.1px; +} +QTabBar::tab { + width: 100px; + height: 32px; + font-size: 16px; + font-family: Microsoft YaHei; + font-weight: 400; + background: #FFFFFF; + border: 2px solid #FFFFFF; + border-bottom-color: #FFFFFF; + border-top-left-radius: 4px; + border-top-right-radium: 4px; + padding: 2px; +} +QTabBar::tab:selected { + color: #333333; + border-color: #FFFFFF; + border-bottom-color: #4BA4F2; +} +QTabBar::tab:!selected { + color: #B2B2B2; + border-color: #FFFFFF; + border-bottom-color: #4BA4F2; +} + +10.基本控件简记 +(1)QToolButton可以图片文字一起显示 +QToolButton->setIcon, setIconSize, setToolButtonStyle +(2)QLineEidt +setPlacceholderText: 未填写内容的提示信息 +setEchoMode: 显示形式,比如密码形式。 +(3)右键菜单, menu->exec(QCursor::pos()) +setContextMenuPolicy(Qt::CustomContextMenu); +void contextMenuEvent(QContextMenuEvent* event) override; +(4)QMenu可以添加QAction +(5)QTableWiget可以直接insert Wiget作为一个新的tab页。 +setTabPostion: 位置 +setTabShape: 形状 +setMovable: 可移动 +(6)QListWidget +Item项也可以是一个widget。 +ListeItem 怎么添加右键菜单: +①必须设置菜单策略 listWidget->setContextMenuPolicy(Qt::CustomContextMenu); +connect(xxListwidget, &QListWidget::customContextMenuRequested, this, xxx); +图标模式:setViewMode +焦点:setFocusPolicy(Qt::NoFocus) +(7)QScrollArea +setFrameShape(QFrame::NoFrame) +setHorizontalScrollBarPolicy() + +11.无边框窗口 +Qt很多问题都可以从QWidget中找到答案,学习Qt大部分就是在与QWidget打交道。 +(1)无边框界面移动和拉伸、大小改变 +重写 +mousePressEvent +mouseMoveEvent +this->pos() 窗口左上角相对桌面位置 +event->pos() 鼠标相对exe位置 +event->globalPos() 鼠标相对桌面位置 +使用 this->move(pos) +重写nativeEvent() +setAttributte(Qt::WA_Hover)防止鼠标边无法选中。 +(2)自定义标题栏(自己布局) +setAttribute(Qt::WA_StyleBackground)禁止父窗口影响子窗口样式。 +qobject_cast(sender())可以获取发送者。 +(3)实现窗口阴影 +需要两层窗口 +$设置窗口透明 this->setAttribute(Qt::WA_TranslucentBackground, true); +$设置无边框 Qt::Windows +$给顶层widget设置背景颜色,不然看不见。 +Qt窗口阴影类:QGraphicsDropShadowEffect -> shadow +设置阴影距离: setOffset +设置阴影颜色: setColor +设置区域: setBlurRadius +给顶层widget设置阴影:setGraphicsEffect(shadow); +相当于是:将实际窗口外包了一层,外面层用于透明。 +(4)圆角窗口(也要透明,无边框) +$1: 重写paintEvent +QPainter painter(this); +painter.setRenderHint(QPainter::Antialiasing); // 抗锯齿 +painter.setBrush(QBrush(QColor(255, 255, 255)); +painter.setPen(Qt::transparent); +auto rect = this->rect(); +painter.drawRoundedRect(rect, 15, 15); // 可以画圆 +$2: 使用 qss +this->setStyleSheet("QWidget{background-color:gray; border-radius: 30px}"); +// 窗口透明后,需要重写该方法 +void paintEvent(xxx) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + QWidget::paintEvent(event); +} + +12.QSS样式表(Qt Style Sheet) 类似css,但是没有css强大。 + +13.Qt CEF, QCefView +