# Qt-Project **Repository Path**: flykhan/qt-project ## Basic Information - **Project Name**: Qt-Project - **Description**: qt 控件,mvs 框架(qt 相册小项目),qt 多线程模块,qt 网络编程模块,网上的一个qt斗地主项目,多媒体模块,json 解析模块的初识 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 6 - **Created**: 2024-02-06 - **Last Updated**: 2024-02-06 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # `Qt-Project` ## 介绍 `Qt` 相关知识的学习 **用 `QT` 写了 `QT`** ## 学习流程 `qt` 控件,`mvs` 框架(`qt` 相册小项目),`qt` 多线程模块,`qt` 网络编程模块,网上的一个 `qt` 单机斗地主项目,多媒体模块,`json` 解析模块的初识 ### 第一部分:`qt` 控件 `part01`: 创建 `Qt` 项目 `part02`: `QDialog` 的模态对话框、非模态对话框以及将对话框置顶 `part03`: `Qt MVC` 结构之 `QItemDelegate` 介绍 `part04`: 信号和槽实现:`MainWindow` 和 `childDialog` 两个界面来回跳转 `part05`: `Qt` 的模态对话框消息传递 `part06`: `Qt` 的几种标准对话框 `part07`: `Qt` 的 `QLineEdit` 介绍 - qt 中 new 了一个对象设置了 parent,那么自己的空间是交给了 parent 去管理了,不用担心内存没有释放的问题 `part08`: `Qt` 的布局(通过 `ui` 或者代码设置布局) - `std::shared_ptr` 互引用问题 - 在自己的类里面,拿到已创建的 `std::shared_ptr`,共享引用计数(`Login` 类里使用) - `std::weak_ptr` 的 `lock` 成员函数可以拿到对应的 `std::shared_ptr` 对象(`register` 类里使用) `part09`: `Qt` 主窗口 - 所有的菜单触发的信号都是 `triggered` 信号 `part10`: `Qt` `QTextEdit` 的几个功能:文本块、遍历文本块、设置文本块样式、插入表格列表图片、实现查找功能 `part11`: `Qt` 文本高亮 - 注意:Qt5 的正则表达式头文件是 QRegExp,qt6 的正则表达式头文件是 QRegularExpression - 重新实现高亮(因为官方库 class QSyntaxHighlighter 里面的 virtual void highlightBlock(const QString &text) = 0; 纯虚函数是一个参数,所以子类重写的话,也得是一个参数的哦) `part12`: 实现高亮代码编辑器 `codeEditor` `part13`: `Qt` 的拖拽事件 - 在 Qt6 中,需要使用 QTextDecoder 和 QTextEncoder 来代替 QTextCodec 实现不同的字符编码 **后面改用 `Qt5` 编辑器,因为初学加上视频用的也是 `Qt5` 环境,`Qt5` 和 `Qt6` 我感觉改动还是比较大的,因为之前有几次碰到有些头文件已经改动了的,哈哈哈** `part14`: `Qt` 的 `QPrinter` 类的简单使用 - 关于找不到 QPrinter 头文件:项目的 .pro 文件得加上 QT += printsupport - 改写 DoPrint 槽函数的实现的时候有一些问题 `part15`: `Qt` 的事件系统 - 关于 eventfilter 和 event 函数返回值 true:继续抛给父节点处理和 false:不抛了 - QObject::eventFilter 返回值的官方解释:stop it being handled further, return true; otherwise return false - 消息捕获的顺序是 Widget::eventfilter --> MyEdit::event(->QLineEdit::event) --> MyEdit::keyPressEvent(->QLineEdit::keyPressEvent) --> Widget::keyPressEvent `part16`: `Qt` 的鼠标事件 `part17`: `Qt` 的按键事件 `part18`: `Qt` 的定时器事件 - 法一:重写 timerEvent 实现定时器 - 法二:QTimer 信号和槽实现定时器 **Qt 的 Model 就是 MVC 结构的应用** `part19`: `Qt MVC` 结构之 `QFileSystemModel` - QFileSystemModel 是 Qt 给我们提供的处理本地文件系统的文件和目录 `part20`: `Qt MVC` 结构之使用 `QStandardItemModel` 实现树形模型 `part21`: `Qt MVC` 结构之继承 `QAbstractListModel` 实现自定义列表模型 - 想实现列表模型就子类化 QAbstractListModel,如果像实现表格模型就子类化 QAbstractTableModel `part22`: `Qt MVC` 结构之 `QItemSelectionModel` 模型介绍 - QItemSelectionModel - 捕获选择条目的变化 - 多个 View 共享 model 模型 `part23`: `Qt` 项目视图便捷类 - Qt 项目视图提供了一些便捷类,包括 QListWidget,QTableWidget,QTreeWidget 等 ### 第二部分:基于上面部分内容的一个 `Qt` 相册小项目(非 `MVC` 结构) #### `AlbumProject` **`part01`: 向导和目录树** - 两个垂直布局放在同一个水平布局中的设置(反之亦然) - 子类重写了基类的虚函数,如果还想调用基类的虚函数,可以在 `return` 的时候调用 - 文件的包含尽量放在 `.cpp` 中,避免互引用 - 基类转子类得用 `dynamic_cast` 安全的父子类之间的转化(子类转基类是直接转化的,有隐式类型转化 --> 切片) - 细节:`.h` 文件新加入的 `Q_OBJECT` 需要右击项目,执行 `qmake` 是得其生效(一个类的没有 `Q_OBJECT` 是不能发送信号的) - 注意:线程的参数是引用的话得使用右值来传递的 ---> 使用 `std::ref` 来转化一下 - 注意 `Windows` 平台的路径`\`得用`\\` (转义) - `std::shared_ptr _thread_create_pro` 的 `get` 函数可以取得 `_thread_create_pro` 的原生指针 <--- `connect` 的第 `1` 和第 `3` 个参数的要求(原生指针) - `_thread_create_pro`(`QThread`) 退出的两种方式: `void exit(int returnCode = 0)` 和 `void terminate()`,因为退出的方式比较生硬,不会顾及我们自己的逻辑,所以手动控制线程的退出 - `qt` 的 `signals` 调用可以降低耦合性并且可以跨线程 - 关于"创建项目"和"打开项目"需要的线程和进度框都是不重用的,但高度相似 ---> 解耦和 **`part02`: 双击展示图片与切换** - `QPixmap` 主要用来显示图片(`QImage` 主要用来对图片进行传输和修改像素) - `PciShow.cpp` 成员函数都写好了之后可以去 `picshow.ui` 文件里面去把对应的两个 `QPushButton` 都提升为当前的这个 `PicShow` 类的哦 - 写的 `qss` 失效,可以直接使用 `setStyleSheet` 函数来设置样式。例:`setStyleSheet("QPushButton { border: 0px; }");` - 对于 `C/C++` 中的 `switch-case` 语句得使用 `break`,不然会一直执行下去的哦(`golang` 的话是默认自动 `break` 的) - `ProTree*(QWidget*)` 的 `_protree` 和 `PicShow*(QWidget*)` 的 `_picshow` 在 `mainwindow.h` 中都是基类的指针类型的 - 为了不让 `ProTree` 类和 `PicShow` 类耦合(有头文件包含的情况),`MainWindow.h` 里面正好包含了它们两个头文件,就可以在 `MainWindow.cpp` 文件中做一个 `connect` 的连接 - `MainWindow` 的拉伸,缩小,拖动会触发 `MainWindow::resizeEvent` 图像重绘事件,再让 `PicShow` 通过 `ReloadPic` 函数去重新加载 **`part03`: 幻灯片 `ui` 设计以及实现幻灯片的暂停和切换** - `SlideShowDlg(QWidget *parent = nullptr, QTreeWidgetItem* first_item, QTreeWidgetItem* last_item);` // `error` (注意默认参数的设置) - `_slide_show_dlg = std::make_shared()`,尽量不要用 `new` 来构造一个 `std::shared_ptr` 类型(防止其他的 `std::shared_ptr` 也指向它,但是可以交给 `std::weak_ptr` 来共享引用计数,来指向) - 防御性编程:对一些函数参数进行必要的判断 (有容错性,指针参数用 `if` 判断是否为空的,输出到日志文件中......) - 进攻性编程:直接使用 `assert` (没有容错性,有问题就直接崩溃) - 前面也有讲到文件互引用的问题 ---> 解决:在 `.cpp` 中包含别人的 `.h` 文件 - 前面有了文件目录 `QTreeWidget`,就有了 `QTreeWidgetItem。`这里有了图片预览列表 `QListWidget`,就有了 `QListWidgetItem`。 - 避免使用 `minGW` 因为有莫名其妙的错误,使用 `MSVC` (使用 `qt` 一般会 `C/C++`,那就一般会安装 `vs`,里面有安装 `MSVC` 就可以了) - 使用 `MSVC` 字体乱码,`minGW` 不会(`MSVC` 得使用宽字节(多字节字符集)。关于 `tr`("轮播图 `play`"),虽然解决了但是后面得有英文的瑕疵,奇葩的地方) - 使用到多媒体的时候,`.pro` 文件得加上 `QT += multimedia` **特别感谢: b 站 恋恋风辰 2017 up 主,他的个人主页:https://space.bilibili.com/271469206, 个人博客:https://llfc.club/home** ### 第三部分:`qt` 多线程模块 `part01:` `Qthread` 的简单使用 - `QThread` 使用相关配套件 `QWaitCondition` 和 `QMutex` 等 - `QThread` 使用的两种方式以及注意事项 `part02:` `QThreadPool` 的简单使用 - 添加到线程池中的任务得继承 `QRunnable` 类(看 `QThreadPool` 添加任务的函数的参数都是要求的 `QRunnable* `类型的哦,相当于是子类隐式类型转化为基类了) - 默认全局是有一个单例的线程池对象的,通过 `QThreadPool::globalInstance()` 即可获取使用 ### 第四部分:`qt` 网络编程模块 `part01:` 基于 `TCP` 的 `Qt` 网络通信 - 使用 `Qt` 的网络模块,在 `.pro` 文件中需要 `QT += network` - 前面关于 `MSVC` 和 `minGW` 关于字体乱码的问题:属于 `Qt5` 的设计缺陷,得在 `.pro` 中强制让 `MSVC` 使用 `utf8` 编码 `part02:` `Tcp` 粘包处理(配合 `boost::asio` 异步网络库的粘包处理) - 进行中...... ### 第五部分:网上的一个 `qt` 单机斗地主项目(`qt` 项目的打包发布) #### 5.1 项目来源 本来是进行到上面的多线程的内容了,但是没有学习内容,就上 `b` 站百度了一下。就看到[爱编程的大丙]这个 `up` 的 `QThread` 视频了。 `QThread` 和 `QThreadPool` 都有试了一下代码,还有后面的 `qt` 网络编程模块。 无意间看到了他的 `qt` 写的斗地主项目,又想了一下,前面跟着视频敲的一个相册展示的项目,把 `qt` 的基础内容和布局用了起来,自己来写的话还是有点难度的,相册项目确实没有用到什么算法什么的,巩固了前面的内容吧。 看到的这个斗地主的项目,想着确实得用一些算法了,也是有点难度的,就把代码拉下来运行了一下(**运行了一下午都没有搞好,还是刚刚请求了同学帮忙搞好的**)。 根据视频里说的,可以通过关注微信公众号:爱编程的大丙,回复:斗地主。就可以得到源码的地址了 ---> 码云:https://gitee.com/subingwen/landlords #### 5.2 运行项目 1. 在电脑本地创建一个文件夹,不要有中文路径,`git init` 是得这个文件夹变成一个 `git` 代码仓库。 2. `git clone https://gitee.com/subingwen/landlords.git` 就把远程代码拉取到本地了,会有新创建一个 `landlords` 目录 3. 进入 `landlords` 目录下,双击 `.pro` 文件,之后选择想要使用什么编译器来构建就行(`qt5` 版本的话就尽量使用 `minGW`,`qt6` 的话 `minGW` 和 `MSVC` 都是可以的)。 **注意:设置好系统环境变量,有多个 `qt` 版本的话,只有 `path` 中最上面的一个环境变量是有效的。** 4. 我选的是 `qt5 minGW 64` 位版本的,打开了项目,双击 `.pro` 文件,把里面最后面需要使用 `res.qrc` 和 `resource.rcc` 的地方注释掉。再点构建项目。再把 `landlords` 目录下的 `resource.rcc` 复制到构建项目新创建的 `build-Landlords-Desktop_Qt_5_15_2_MSVC2019_64bit-Release`(`build`)路径下(`qt Creator` 运行的路径)。再点击运行就可以看到完整的项目被运行了(尽量使用内存小的 `release` 版本)。 **注意:`resource.rcc` 也可以复制到其中的 `release` 文件的`.exe` 同级目录中(需要设置对应 `qt` 版本的环境变量双击`.exe` 才可以使用)。** **注意:`minGW` 是会把 `release` 文件夹下的`.obj` 等文件删除的,`MSVC` 是会保留的。** 5. 对于要把代码发给别人也是可以玩的话,就可以把构建项目新创建的 `build-Landlords-Desktop_Qt_5_15_2_MSVC2019_64bit-Release` 文件夹下的`.exe` 文件和 `resource.rcc` 再单独创建一个 `landlords` 文件夹。 6. 打开 `qt5 minGW 64` 位版本的控制台 `D:\qt5\5.15.2\mingw81_64>` 使用 `windeployqt` 命令:`windeployqt C:\Users\Administrator\Desktop\landlords\Landlords.exe`(打包时不能有任何中文路径、中文名称),就可以生成需要的 `.dll` 动态库了,那么 `C:\Users\Administrator\Desktop\landlords` 这个文件夹就可以发给别人使用了,别人双击其中的`.exe` 就可以运行了,不用再使用上面 `1-4` 的步骤了。 #### 5.3 关于 `res.qrc` 和 `resource.rcc` 两个文件 `rcc` 文件是由 `qrc` 文件转化而来,可以将 `qrc` 中所有的图片、文档等资源以二进制的件形式存储到 `rcc` 文件中,程序打包的时候将 `rcc` 文带上即可。 **`.qrc` 文件生成 `.rcc` 文件** 打开对应的 `qt` 版本 `cmd`(控制台),输入 `rcc -binary res.qrc -o resource.rcc` 就可以把 `res.qrc` 资源文件转化成 `resource.rcc` 二进制的件形式存储资源文件,因为在上面第 `6` 步打包发布项目的时候,就不用再把对应的资源文件再下载和发送了(方便)。 **使用 `resource.rcc`** 在程序初始化时将 `rcc` 文件注册一下即可 `QResource::registerResource(rcc 文件路径);`。 #### 5.4 读项目 推荐先看 `b` 站上视频的解说,再自己有时候再理解。在源码 `landlords` 目录下使用 `$ find ./*.cpp ./*.h -type f -exec cat {} \; | wc -l` 输出 `4590`,再加上是一个游戏项目,各模块的交互是比较多的,对于初出茅庐的编程人士是有一点难度的。有时间会再发布自己对于项目理解的。 初看是没有使用网络编程的。