--- title: QT date: 2022年4月28日 author: TianZD top: true cover: true toc: true mathjax: false summary: QT的安装教程,以及一些使用过程记录 tags: - C++ - QT categories: - C++ - QT - IDE abbrlink: 8e7feb07 reprintPolicy: cc_by password: coverImg: img: --- [toc] # QT ## QT 安装 中国科学技术大学:http://mirrors.ustc.edu.cn/qtproject/ 清华大学:https://mirrors.tuna.tsinghua.edu.cn/qt/ 北京理工大学:http://mirror.bit.edu.cn/qtproject/ 中国互联网络信息中心:https://mirrors.cnnic.cn/qt/ ![在这里插入图片描述](https://gitee.com/tianzhendong/img/raw/master//images/202202181559349.png) ![在这里插入图片描述](https://gitee.com/tianzhendong/img/raw/master//images/202202181559285.png) 各模块意思 Qt Charts是二维图表模块,用于绘制柱状图、饼图、曲线图等常用二维图表。在制作一些需要绘制表格的软件的时候经常会用到,建议勾选 Qt Quick 3D模块初探,这个是技术预览勾不勾都可以,Quick 3D提供了用于基于Qt Quick创建3D内容或UI的高级API。提供了对现有Qt Quick场景图的扩展,以及对该扩展场景图的渲染器。不过还是在测试阶段,因为我可能会用到,所以我勾了。 Qt Data Visualization 是三维数据图表模块,用于数据的三维显示,如散点的三维空间分布、三维曲面等。这个如果你勾选了Qt Charts的话,这个基本也要用得到,建议勾选。 QT lottie animation 这个主要是用来实现复杂的动画效果,如果要用来制作动画或者制作游戏的话菜肴勾选。 Qt Purchasing、Qt WebEngine、Qt Network Auth(TP)等其他模块,括号里的 TP 表示技术预览。(技术预览就是就算你勾选了,也用不了,就是让你看看而已,哈哈哈) Qt Scritp(Deprecated)是脚本模块,括号里的“Deprecated”表示这是个已经过时的模块。 接下来是tool的选择 ------------------------------------------------ 版权声明:本文为CSDN博主「流楚丶格念」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_45525272/article/details/113062352 ## 解决Qt-至少需要一个有效且已启用的储存库 问题 [blog.csdn.net](https://blog.csdn.net/asdsa12311/article/details/101935605) 问题如图所示 ![image-20220218155724951](https://gitee.com/tianzhendong/img/raw/master//images/202202181557049.png) 解决方法: 1、选择左下角的设置,进入如图界面,然后选择“临时存储库” ![image-20220218155742235](https://gitee.com/tianzhendong/img/raw/master//images/202202181557304.png) 2、手动添加临时储存库要定位一个储存有QT在线安装镜像的地址,这个地址可以从这里选择http://download.qt.io/static/mirrorlist/ 在这个网站,显示了各国的qt镜像站点,中国有四个,随便选一个中科大的站点击HTTP会进入一个网络文件夹。 以此进入如下路径:/online/qtsdkrepository/windows\_x86/root/qt/ 。然后把该路径添加到临时存储库中。然后就可以增删组件了。 最终路径如下所示 `https://mirrors.tuna.tsinghua.edu.cn/qt/online/qtsdkrepository/windows_x86/root/qt/` ## Qt程序运行时CMD窗口 Linux 下都是带终端的. Windows 下面如果 pro 文件中 config+=console,就会带一个 cmd 窗口 ## Qt Creator中显示的汉字变为乱码 [blog.csdn.net](https://blog.csdn.net/liuweilhy/article/details/82321105) ### 问题是什么? 在学习Qt编程的过程中,大多数人都遇到过中文乱码的问题。总结起来有三类: 1\. Qt Creator中显示的汉字变为乱码,编辑器上方有“Could not [decode](https://so.csdn.net/so/search?q=decode&spm=1001.2101.3001.7020) "..." with "UTF-8"-encoding. Editing not possible.”的错误提示。此时,出现乱码的文档是不可编辑的。如下图所示,“你好中文!”这5个中文字符变成了乱码: ![image-20220218160928755](https://gitee.com/tianzhendong/img/raw/master//images/202202181609841.png) 2\. Qt Creator中显示的汉字正常,但编译的时候会出现“常量中有换行符”等一系列错误报警。其实,这也是文字编码的问题。如下图所示: ![image-20220218160959524](https://gitee.com/tianzhendong/img/raw/master//images/202202181609646.png) 3\. 编译时未报错,但生成的程序中文乱码。如下图所示: ![image-20220218161014007](https://gitee.com/tianzhendong/img/raw/master//images/202202181610061.png) 其中,第3条是网上提问的最多的,几乎是所有使用MSVC的初学者都会碰到的问题。很多回答是针对Qt4版本的,Qt5中不可用。 ### 为什么会出现这些问题? 在解决问题之前,字符编码知识是必需的。你要知道ASCII、GB2312、GBK、Unicode、UTF-8、UTF-16、BOM是怎么回事。此外,你还要明白源码字符集、执行字符集是什么。详细内容可以在网上搜索一下,俯拾即是。 1\. Qt Creator的编辑器默认使用**UTF-8**(代码页65001)编码来读取文本文件。而Visual Studio保存文件时默认采用的是本地编码,对于简体中文的Windows操作系统,这个编码就是GB2312(代码页936)。如果使用Qt Creator读取由Visual Studio创建的文件,那么编辑器就会以UTF-8编码格式读取GB2312编码格式的文件,出现中文乱码,因为这两套编码系统对汉字编码是不同的。至于英文部分不会乱码,是因为UTF-8和GB2312在单字节字符部分是兼容的。 2\. MSVC在编译时,会根据源代码文件有无BOM来定义源码字符集。如果有BOM,则按BOM解释识别编码;如果没有,则使用本地字符集,对于简体中文的Windows操作系统就是GB2312。那么,当MSVC遇到一个没有BOM的UTF-8编码的文件时,它通常会把文件看作GB2312的来处理。如果文件全是英文没有问题,但如果包含中文,编译器就会出现误读。这种情况下,Qt Creator编辑器是正常的。但对于MSVC编译器,原代码会被它认识成下图这个样子: ![image-20220218161038546](https://gitee.com/tianzhendong/img/raw/master//images/202202181610648.png) 这是我用EverEdit指定本地编码重读后的结果,可以看到汉字出错,末端的引号也没了。 在UTF-8中,一个中文字符(汉字或标点符号)占用3个字节,“你好中文!”这5个中文字符共占用15个字节;而在GB2312中,一个中文字符(汉字或标点符号)占用2个字节,这时,MSVC把UTF-8编码的15个字节加上后面1个字节的英文引号合成16个字节当作8个中文字符处理。之后,MSVC在这一行里直到末尾换行符出现都没有找到下一个引号,它以为你把字符串在这里敲回车换行了,于是报警称“常量中有换行符”,并引出一系列的错误。 不过,当以无BOM的UTF-8编码的字符串正好凑够偶数个字节时(比如偶数个汉字,或奇数个汉字加奇数个英文字母),编译器通常不会报警,因为它以为用GB2312编码读出的是正确的。 3\. 不管源文件是何种编码,只要MSVC能够正确识别,就可以通过编译。但MSVC的执行字符集默认是本地字符集。对我们来说,它生成的可执行文件中的文字是GB2312编码的。而生成的Qt程序以UTF-8编码来识别GB2312编码的文字,对于“你好中文!”这几个字符,采用GB2312编码后再以UFT-8编码来读取,就会变成如下的乱码: ![image-20220218161057717](https://gitee.com/tianzhendong/img/raw/master//images/202202181610774.png) 当以无BOM的UTF-8编码的字符串正好凑够偶数个字节时(比如偶数个汉字,或奇数个汉字加奇数个英文字母),反而不会出现乱码。那是因为,编译器用GB2312编码读出的乱码本身就是UTF-8编码的,现在又用UTF-8解读,自然就正确了。这纯粹是歪打正着。 ### 怎么解决这些问题? 首先,你要确定采用哪种源码字符集。你有两个选择: 1\. 采用本地编码字符集(不推荐,跨平台时会比较麻烦,但在Visual Studio环境下配合Add-in工具编程比较方便); 2\. 采用UTF-8编码字符集(推荐,适合跨平台)。 #### “采用本地编码字符集”方案,解决方法如下: 首先,要把项目中所有的头文件和源文件全都转换成GB2312编码保存。 1\. 第1个问题:在Qt Creator中打开项目,点击左侧工具栏“项目”,在“编辑器”选项卡中把“默认编码”改成“GB2312”。如下图所示: ![image-20220218161128126](https://gitee.com/tianzhendong/img/raw/master//images/202202181611248.png) 话说回来,既然选择本地字符集,大致上是放弃跨平台了。与其用轻量级的Qt Creator,不如用Visual Studio作开发环境更好。 2\. 第2个问题:“常量中有换行符”等一系列报警已不存在了。 3\. 第3个问题:在字符串常量上加QStringLiteral宏或QString::fromLocal8Bit函数,如: QString str = "你好中文!"; 改为: QString str = QStringLiteral("你好中文!"); 或者: QString str = QString::fromLocal8Bit("你好中文!"); 不过,在这两种形式下,你都无法用tr方法来创建翻译了。 #### “采用UTF-8编码字符集”方案,解决方法如下: **注意:加上后,qdebug输出中文正常,cout输出中文乱码,窗口名称输出中文乱码** 首先,要把项目中所有的头文件和源文件全都转换成UTF-8+BOM编码保存。 ![image-20220218172128013](https://gitee.com/tianzhendong/img/raw/master//images/202202181721125.png) 1\. 第1个问题不存在了。 2\. 第2个问题也不存在了。 3\. 第3个问题,你也可以用上个方案中的方法来解决,但有更好的方法。那就是要用到中文字符的头文件和源文件开头加上MSVC的一个宏: ```c++ #if _MSC_VER >= 1600 #pragma execution_character_set("utf-8") #endif ``` 这个宏告诉MSVC,执行字符集是UTF-8编码的,别瞎整成GB2312的!还有个好处,就是能用tr包中文,方便日后的翻译。 或者在pro文件中加入: ```properties msvc { QMAKE_CFLAGS += /utf-8 QMAKE_CXXFLAGS += /utf-8 } ``` 最终效果如下: ![image-20220218161244977](https://gitee.com/tianzhendong/img/raw/master//images/202202181612073.png) [查看原网页: blog.csdn.net](https://blog.csdn.net/liuweilhy/article/details/82321105) ## 回顾C++基础 ### 类和对象 ```C++ //新建类 class student{ public: string name; int age; void test(); }; void student::test(){//类外定义 cout<age<age = 11;//通过箭头访问 stu2->test(); ``` ### 重载 **重载**:函数名相同,但是参数不同 ### 构造函数和析构函数 **析构函数**:对象被删除或者生命周期结束时触发 **构造函数** :对象被创建的时候触发、 ```c++ student::student(){ cout<<"hello,构造函数"<btnLogin,SIGNAL(clicked()),this,SLOT(on_btnRegister_clicked()));` ```c++ Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); connect(ui->btnRegister,SIGNAL(clicked()),this,SLOT(on_btnRegister_clicked()));//添加的关联函数 } ``` 其中`ui`为指向整个窗口的指针,`btnLogin`为要关联的控件A,`SIGNAL()`函数中指定**信号**B,`SLOT()`函数中为**槽函数**C 当A发出B信号时,会触发槽函数C ## 添加图片 [icon网址](http://www.easyicon.net) [图标之家](http://www.icosky.com/) ### 添加资源目录 ![image-20220122193234441](https://s2.loli.net/2022/01/22/XYjrqiCbU6MzaTV.png) 工程会出现Resources文件夹 ### 将图片放入QT工程目录下 1. 右键Resources文件夹下的文件, ![image-20220122193716310](https://s2.loli.net/2022/01/22/4MWZv3ltOqam1ds.png) 2. 添加前缀 前缀设置成`/`,并保存 ![image-20220122193812191](https://s2.loli.net/2022/01/22/dl8oAMyxzrYV7hJ.png) 3. 点击`添加`并选择添加的图片 ### 引用文件 1. 在ui页面添加`label`控件(也支持按钮控件) 2. 删掉控件中的文字,并右键——改变样式表——添加资源右侧的箭头——选择border-image——点击左侧——选择文件 ![image-20220122194046904](https://s2.loli.net/2022/01/22/Ridprh2zasqc1lx.png) ![image-20220122194138559](https://s2.loli.net/2022/01/22/524AtPKZs1dMBTf.png) ## UI界面布局 > 页面布局不会改变任何代码 ### 概述 **用途**:防止因不同设备、不同缩放、不同分辨率导致布局错乱问题 **QT布局样式:** * 水平布局:对应1 * 垂直布局:对应2 * 栅格布局:对应3 ![image-20220122194911328](https://s2.loli.net/2022/01/22/3URe1THBVsWoD2i.png) ### 使用 选中两个控件,点击对应的布局按钮即可 #### 弹簧控件Spacers 弹簧用来当缩放UI时,周边间距进行自动变化 ![image-20220122195329642](https://s2.loli.net/2022/01/22/CdmcWSjxwBlhVpY.png) #### 最终 ![image-20220122195549478](https://s2.loli.net/2022/01/22/gCZIDWbJYyOQ9St.png) ## 界面切换 ### 创建一个新的界面 右键工程——添加新文件——QT——QT设计师界面类——widget——输入名字,这里为`form` 会自动在Headers文件夹下生成`form.h`头文件 ![image-20220122195907707](https://s2.loli.net/2022/01/22/EeoYpPWuvG7byaI.png) ### 关联控件 修改`widget.cpp`文件,添加**头文件**和关联槽函数 ```c++ #include "form.h" //添加头文件 //省略 void Widget::on_btnLogin_clicked() { //qDebug("register"); Form *form1 = new Form;//创建一个Form对象 form1->setGeometry(this->geometry());//设置form.ui的尺寸为当前ui的尺寸 form1->show();//展示 } ``` ### 关闭新界面 1. 新UI界面控件改名 2. 关联信号 3. 设置关闭槽函数 ```c++ void Form::on_pushButton_clicked() { this->close(); } ``` ## 获取界面输入 ```c++ void Widget::on_btnLogin_clicked() { //获取输入的参数 QString name = ui->inputName->text(); QString psd = ui->inputPassword->text(); if(name == "admin" && psd == "123123"){ Form *form1 = new Form;//创建一个Form对象 form1->setGeometry(this->geometry());//设置form.ui的尺寸为当前ui的尺寸 form1->show();//展示 } } ``` ## QT GUI设计 ### 窗口置顶 与 取消置顶 ```c void MainWindow::on_windowTopButton_clicked() {   if (m_flags == NULL)   {     m_flags = windowFlags();     setWindowFlags(m_flags | Qt::WindowStaysOnTopHint);     this->show();   }   else   {     m_flags = NULL;     setWindowFlags(m_flags);     this->show();   } } ``` 回到顶部 ### 全屏显示 与 退出全屏 首先,在QT中对于窗口显示常用的有这么几个方法可以调用: ```c showFullScreen() // Qt全屏显示函数 showMaximized() // Qt最大化显示函数 showMinimized() // Qt最小化显示函数 resize(x, y) // Qt固定尺寸显示函数 setMaximumSize(w, h) // Qt设置最大尺寸函数 setMinimumSize(w, h) // Qt设置最小尺寸函数 ``` 但是 `showFullScreen()` 和 `showNormal()` 只对顶级窗口有效果,对子窗口无效。要将子窗口全屏显示可用以下方法: - 将要全屏的 Qt 子窗口调用 `setWindowFlags(Qt::Window)` 将其类型提升为顶级窗口模式,然后调用 `showFullScreen()` 函数将子窗口全屏显示。 - 当然全屏后还要恢复正常,即调用 `setWindowFlags(Qt::subwindow)` 将子窗口设置为非顶级窗口,再调用 `showNormal()` 还原子窗口显示。 ```c // 全屏显示: VideoWidget->setWindowFlags(Qt::Window); VideoWidget->showFullScreen(); // 退出全屏时: VideoWidget->setWindowFlags(Qt::SubWindow); VideoWidget->showNormal(); ``` 回到顶部 ### 窗口的 "最大化\最小化\关闭" 按钮设置 1、在 QDialog 中添加 "最大化" 和 "最小化"按钮 在使用 QDialog 时,默认情况下只有 “这是什么” 和 “关闭” 按钮,但是我们习惯有最大化和最小化按钮。这里介绍如何在该模式下如何设置。 ```c this->setWindowFlags(Qt::Dialog | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint); ``` 其中 `WindowMinMaxButtonsHint` 设置的就是最大和最小按钮,但是只这样设置,关闭按钮就不可用了,因此需要再添加 `WindowCloseButtonHint` 一项。 2、只禁止最大化按钮 ```c this->setWindowFlags(windowFlags() &~ Qt::WindowMaximizeButtonHint); ``` `Qt::WindowMaximizeButtonHint` 为 `WindowType` 枚举变量,通过修改它可以选择禁止 "最小化" 或者 "关闭" 按钮。 回到顶部 ### 禁止拖动窗口大小 ```c this->setFixedSize(this->width(),this->height()); ``` 回到顶部 ### 获取屏幕的宽度和高度 ```c QApplication::desktop()->width(); QApplication::desktop()->height(); ``` 即得到屏幕分辨率,如 1024*768。 ### 去掉菜单栏 ```c //隐藏菜单栏 this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint); ``` 去掉以后,发现窗口无法移动,无法放大缩小关闭 ### 添加自定义最小化、最大化、关闭按钮 mainwindow.h ```c protected : /** * @brief closeEvent重写closeEvent * @param event */ void closeEvent(QCloseEvent *event); private: //全屏显示标识 bool fullScreenFlag = false; ``` mainwindow.cpp ```c /** * @brief MainWindow::closeEvent 重写closeEvent: 确认退出对话框 * @param event */ void MainWindow::closeEvent(QCloseEvent *event) { QMessageBox::StandardButton button; button=QMessageBox::question(this,tr("退出程序"),QString(tr("确认退出程序?")),QMessageBox::Yes|QMessageBox::No); if(button==QMessageBox::No) { event->ignore(); // 忽略退出信号,程序继续进行 } else if(button==QMessageBox::Yes) { event->accept(); // 接受退出信号,程序退出 } } /** * @brief MainWindow::on_closeBTN_clicked 自定义关闭按钮 */ void MainWindow::on_closeBTN_clicked() { this->close(); } /** * @brief MainWindow::on_showMaximizedBTN_clicked 最大化按钮 */ void MainWindow::on_showMaximizedBTN_clicked() { if(!fullScreenFlag){ this->showFullScreen(); fullScreenFlag = true; } else{ this->showNormal(); fullScreenFlag = false; } } /** * @brief MainWindow::on_showMinBTN_clicked 最小化按钮 */ void MainWindow::on_showMinBTN_clicked() { this->showMinimized(); } ``` ### 拖拽窗口移动 mainwindow.h ```c protected : /** * @brief 拖拽窗口 */ void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void mouseReleaseEvent(QMouseEvent *event); private: Ui::MainWindow *ui; //拖拽窗口 bool m_bDrag; QPoint mouseStartPoint; QPoint windowTopLeftPoint; ``` mainwindow.cpp ```c /** * @brief MainWindow::mousePressEvent 拖拽窗口移动操作 * @param event */ void MainWindow::mousePressEvent(QMouseEvent *event) { if(event->button() == Qt::LeftButton) { m_bDrag = true; //获得鼠标的初始位置 mouseStartPoint = event->globalPos(); //mouseStartPoint = event->pos(); //获得窗口的初始位置 windowTopLeftPoint = this->frameGeometry().topLeft(); } } void MainWindow::mouseMoveEvent(QMouseEvent *event) { if(m_bDrag) { //获得鼠标移动的距离 QPoint distance = event->globalPos() - mouseStartPoint; //QPoint distance = event->pos() - mouseStartPoint; //改变窗口的位置 this->move(windowTopLeftPoint + distance); } } void MainWindow::mouseReleaseEvent(QMouseEvent *event) { if(event->button() == Qt::LeftButton) { m_bDrag = false; } } ``` ### 设置空间透明 ```mode->setFlat(true);``` //就是这句实现按钮透明的效果。 ## QT串口调试工具 网络编程、串口编程、操作GPIO 仿写 ![image-20220122215752070](https://s2.loli.net/2022/01/22/wA6FI9foa1V2edh.png) ### 串口调试助手UI界面 #### 添加控件 1. 设计UI界面大小`800*480` 2. 添加数据接收框`Plain Text Edit`,并在属性设置区勾选`read only` 3. 添加参数下拉选择输入框`Combo Box`和问题提示框`label` 4. 添加发送框`line Edit` 5. 添加按钮`push button` 6. 添加广告框`GroupBox`+`Label` ![image-20220122225909615](https://s2.loli.net/2022/01/22/4w1sceg8J9ILXNC.png) #### 添加属性 1. 双击波特率、数据位、停止位、校验位下拉输入框,添加属性 ![image-20220122230043510](https://s2.loli.net/2022/01/22/QfVq7xgpyjBc9rv.png) 2. 通过属性`currentIndex`设置默认值 ![image-20220122231854565](https://s2.loli.net/2022/01/22/TXhKWq7FQPzLB1C.png) #### 控件改名 添加库支持 在`.pro`文件中,增加`serialport` ```c++ QT += core gui serialport ``` #### 搜索串口并显示 `widget.cpp` ```c++ #include "widget.h" #include "ui_widget.h" #include //添加串口头文件 Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); //获取串口号并显示 QStringList serialPortNums; foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) { serialPortNums<serialPortNum->addItems(serialPortNums); } Widget::~Widget() { delete ui; } ``` ### 实现逻辑功能 #### 创建串口对象 在widget.h中添加头文件,并声明串口对象指针 ```c++ #include//引入串口头文件 public: QSerialPort *serialPort;//声明串口类指针 ``` zai widget.cpp中创建对象 ```c++ serialPort = new QSerialPort(this);//创建串口对象 ``` #### 打开串口并初始化 1. 关联打开串口控件的信号和槽函数 2. 定义槽函数:定义串口数据——初始化 3. 给当前串口对象赋值 ```c++ void Widget::on_btnOpenSerial_clicked()//打开串口-点击信号对应的槽函数 { //声明变量 QSerialPort::BaudRate baudRate; QSerialPort::DataBits dataBits; QSerialPort::StopBits stopBits; QSerialPort::Parity parity; //赋值,初始化 //波特率 if(ui->baudRate->currentText() == "4800"){ baudRate = QSerialPort::Baud4800; }else if(ui->baudRate->currentText() == "9600"){ baudRate = QSerialPort::Baud9600; }else if(ui->baudRate->currentText() == "115200"){ baudRate = QSerialPort::Baud115200; } //数据位 if(ui->dataBits->currentText()=="5"){ dataBits = QSerialPort::Data5; }else if(ui->dataBits->currentText()=="6"){ dataBits = QSerialPort::Data6; }else if(ui->dataBits->currentText()=="7"){ dataBits = QSerialPort::Data7; }else if(ui->dataBits->currentText()=="8"){ dataBits = QSerialPort::Data8; } //停止位 if(ui->stopBits->currentText()=="1"){ stopBits = QSerialPort::OneStop; }else if(ui->stopBits->currentText()=="1.5"){ stopBits = QSerialPort::OneAndHalfStop; }else if(ui->stopBits->currentText()=="2"){ stopBits = QSerialPort::TwoStop; } //校验位 if(ui->parity->currentText()=="none"){ parity = QSerialPort::NoParity; } //获取当前选择的串口号 serialPort->setPortName(ui->serialPortNum->currentText()); //设置串口参数 serialPort->setBaudRate(baudRate); serialPort->setDataBits(dataBits); serialPort->setStopBits(stopBits); serialPort->setParity(parity); } ``` 4. 判断串口是否打开成功 ```c++ //判断串口是否打开成功,并提示,需要添加头文件 if(serialPort->open(QIODevice::ReadWrite) == true){ QMessageBox::information(this,"提示","成功"); }else { QMessageBox::critical(this,"提示","失败"); } ``` ![image-20220123104230714](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123104230714.png) ![image-20220123104241542](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123104241542.png) #### 关闭串口 1. 关联信号和槽函数 2. 定义逻辑 ```c++ void Widget::on_btnCloseSerial_clicked() { serialPort->close(); } ``` #### 数据接收 1. 在`widget.h`中`private slots`下增加槽函数声明 ```c++ private slots: void serialDataReadReady_Slot();//读数据槽函数声明 ``` 2. 在`widget.cpp`中增加槽函数 ```c++ //读数据槽函数定义 void Widget::serialDataReadReady_Slot(){ QString buff;//暂存数据 buff = QString(serialPort->readAll());//读取串口的数据 ui->dataRCV->appendPlainText(buff);//将数据发送到接受区内 } ``` 3. 在`widget.cpp`中,添加关联函数 ```c++ //关联数据接收槽函数 connect(serialPort,SIGNAL(readyRead()),this,SLOT(serialDataReadReady_Slot())); ``` #### 数据发送 ```c++ void Widget::on_btnSend_clicked()//数据发送槽函数 { serialPort->write(ui->dataInput->text().toLocal8Bit().data()); } ``` #### 清空 ```c++ void Widget::on_btnClear_clicked()//清空槽函数 { ui->dataRCV->clear(); } ``` ## QT程序打包和部署 ### 为什么 1. 把写好的程序给别人用 2. 源码不能随便给别人 ### 怎么做 #### release模式 1. 把工程切换到release模式,然后编译 release模式,没有调试信息 debug模式,有很多调试信息 ![image-20220123112932311](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123112932311.png) 2. 找到release模式构建的文件夹,并进入release文件夹 ![image-20220123113146728](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123113146728.png) ![image-20220123113218111](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123113218111.png) **Serial.exe文件就是所需的文件,但是之间无法打开,需要动态库** #### 修改图标 [图标之家](http://www.icosky.com/) 1. 下载图标,**格式为.ico**,并拷贝到工程目录下,注意,不是编译目录 这里图标为:`serial_ico.ico` ![image-20220123114501066](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123114501066.png) 2. 在.pro中增加图标,并编译 ```properties RC_ICONS = serial_ico.ico ``` 编译可以看到左上角图标已经改变 ![image-20220123114615068](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123114615068.png) 同时release目录下的exe文件的图标也已经改变 ![image-20220123114654768](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123114654768.png) #### 封包 1. 创建一个新的文件夹,不要有中文路径`C:\code\QT\Serial_deploy` 2. 拷贝release下的exe文件到文件夹中 3. 进入QT控制台,并进入新建的文件夹 直接搜索QT,应用下面的就是 ![image-20220123114858514](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123114858514.png) 4. 输入`windeployqt Serial.exe`命令后自动封包,结果如下 ![image-20220123115455018](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123115455018.png) 5. 双击里面的Serial.exe文件可以直接运行 ### 程序打成安装包 Inno Setup软件 [参考](https://www.cnblogs.com/linuxAndMcu/p/10974927.html) ## QT网络编程-TCP通信 ### TcpServer 两个类: * QTcpServer * QTcpSocket #### QTcpServer类 提供一个TCP基础服务类 继承自QObject 这个类用来接收到来的TCP连接,可以**指定TCP端口**或者用QTcpServer自己挑选一个端口,可以**监听一个指定的地址或者所有的机器地址**。 调用`listen()`来监听所有的连接,每当一个新的客户端连接到服务端就会**发射信号**`newConnection()` 调用`nextPendingConnection()`来接受待处理的连接。返回一个连接的`QTcpSocket()`,我们可以用这个返回的套接字和客户端进行连接 如果有错误,`serverError()`返回错误的类型。调用`errorString()`来把错误打印出来。 当监听连接时候,可以调用`serverAddress()`和`serverPort()`来返回服务端的地址和端口。 调用`close()`来关闭套接字,停止对连接的监听。 | Header: | #include | | ------------- | --------------------------------- | | qmake: | QT += network | | Inherits: | [QObject](../qtcore/qobject.html) | | Inherited By: | [QSctpServer](qsctpserver.html) | **成员函数** | 函数 | 用途 | | ------------------------------------------------------------ | ------------------------------------------------------------ | | `void close() ` | 关闭服务,然后服务器讲不再监听任何连接 | | `QString errorString()const` | 错误时候返回错误的字符串 | | `bool hasPendingConnections()const` | 如果服务端有一个待处理的连接,就返回真,否则返回假 | | `QTcpSocket* nextPendingConnection()` | 返回一个套接字来处理一个连接,这个套接字作为服务端的一个子对象,意味着当QTcpServer对象销毁时候,这个套接字也自动删除,当使用完后明确的删除这个套接字也好,这样可以避免内存浪费。当没有可处理的连接时候,这个函数返回0。 | | `void incomingConnection(int socketDescriptor)[virtualprotected]` | 这个函数新建一个QTcpSocket套接字,建立套接字描述符,然后存储套接字在一个整形的待连接链表中。最后发射信号newConnection() | | `bool isListening()const` | 当服务端正在监听连接时候返回真,否则返回假 | | `bool listen( const QHostAddress & address =QHostAddress::Any, quint16 port = 0 )` | 告诉服务端监听所有来自地址为address端口为Port的连接,如果Port为0,那么会自动选择,如果address是QHostAddress::Any,那么服务端监听所有连接,成功返回1,否则返回0 | | `int maxPendingConnections()const` | 返回最大允许连接数。默认是30 | | `void setMaxPendingConnections(int numConnections)` | 设定待处理的连接最大数目为numConnections,当超过了最大连接数后,客户端仍旧可以连接服务端,但是服务端不在接受连接,操作系统会把这些链接保存在一个队列中。 | | `QNetworkProxy proxy()const` | 返回这个套接字的网络代理层。 | | `void setProxy(const QNetworkProxy & networkProxy)` | 设置这个套接字的网络代理层,进制使用代理时候,使用QNetworkProxy::NoProxy类型,例如server->setProxy(QNetworkProxy::NoProxy); | | `quint16serverPort()const serverAddress()` | 当服务端正在监听时候,返回服务端的端口和地址 | | `bool waitForNewConnection(int msec=0,bool *timedOut=0)` | 最大等待msec毫秒或者等待一个新连接可用。如果一个连接可用,返回真,否则返回假。如果msec不等于0,那么超时将会被调用 | #### QTcpSocket类 QTcpSocket 类提供一个TCP套接字 TCP是一个面向连接,可靠的的通信协议,非常适合于连续不断的数据传递 QTcpSocket 是QAbstractSocket类非常方便的一个子类,让你创建一个TCP连接和数据流交流。 | Header: | #include | | ------------- | ------------------------------------------------------------ | | qmake: | QT += network | | Inherits: | [QAbstractSocket](qabstractsocket.html) | | Inherited By: | [QSctpSocket](qsctpsocket.html) and [QSslSocket](qsslsocket.html) | 成员函数 | 函数 | 用途 | | ----------------------------------------------- | ------------------------------------------------------------ | | QTcpSocket::QTcpSocket ( QObject * parent = 0 ) | 以UnconnectedState态创建一个QTcpSocket对象 | | QTcpSocket::~QTcpSocket () [virtual]‘ | 析构函数,销毁对象 | | bool waitForConnected(int *msecs* = 30000) | 等待,直到套接字被连接,最高为msecs毫秒。如果连接已经建立,这个函数返回true;否则返回false。在返回false的情况下,可以调用error()来确定错误的原因。 | | void connectToHost() | 尝试连接给定端口上的主机名。如果查找成功,则发出hostFound(), QAbstractSocket进入ConnectingState。然后,它尝试连接到查找返回的一个或多个地址。最后,如果连接建立,QAbstractSocket进入ConnectedState并发出connected()。 | | [signal] void QAbstractSocket::connected() | 这个信号在调用connectToHost()并成功建立连接之后发出。 | | bool disconnect() | 断开对象发送器中的信号与对象接收器中的方法。如果连接成功断开,返回true;否则返回false。 | | void QAbstractSocket::disconnectFromHost() | 试图关闭socket。如果有挂起的数据等待写入,QAbstractSocket将进入ClosingState并等待,直到所有的数据都被写入。最终,它将进入UnconnectedState并发出disconnected()信号。 | #### TcpServer编写 ##### 创建ui界面 ![image-20220123152716836](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123152716836.png) ##### 引入network和两个头文件 ```properties QT += core gui network ``` ```c++ #include #include ``` ##### 声明对象 ```c++ //声明tcp所用的对象 QTcpServer *tcpServer; QTcpSocket *tcpSocket; ``` ##### 创建对象`widget.cpp` ```c++ //创建对象 tcpServer = new QTcpServer(this); tcpSocket = new QTcpSocket(this); ``` ##### 编写槽函数 1. 打开服务器时开启监听 调用`listen()`来监听所有的连接,每当一个新的客户端连接到服务端就会**发射信号**`newConnection()` 2. 有新连接时创建tcpsocket, 调用`nextPendingConnection()`来接受待处理的连接。返回一个连接的`QTcpSocket()`,我们可以用这个返回的套接字和客户端进行连接 3. 读取数据到接收框中 `readyRead()`和`readAll()` 4. 关闭服务器 `close()` 5. 发送数据 `write()` #### 代码 `widget.h` ```c++ #ifndef WIDGET_H #define WIDGET_H #include #include #include namespace Ui { class Widget; } class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = 0); ~Widget(); //声明tcp所用的对象 QTcpServer *tcpServer; QTcpSocket *tcpSocket; private slots: void on_btnOpenServer_clicked(); //2.有新连接时创建tcpsocket void newConnection_Slot(); //3.读取就绪时读取数据 void readyRead_SLOT(); //4. 关闭服务器 void on_btnCloseServer_clicked(); //5. 发送数据 void on_btnSendMessage_clicked(); private: Ui::Widget *ui; }; #endif // WIDGET_H ``` `widget.cpp` ```c++ #include "widget.h" #include "ui_widget.h" Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); //创建对象 tcpServer = new QTcpServer(this); tcpSocket = new QTcpSocket(this); //2.有新连接时创建tcpSocket connect(tcpServer,SIGNAL(newConnection()),this,SLOT(newConnection_Slot())); } Widget::~Widget() { delete ui; } //打开服务器 void Widget::on_btnOpenServer_clicked() { //1.打开监听,所有地址,端口由输入框设定 tcpServer->listen(QHostAddress::Any,ui->tcpPort->text().toUInt()); } //有新连接时创建tcpsocket void Widget::newConnection_Slot(){ //2.调用`nextPendingConnection()`来接受待处理的连接。返回一个连接的`QTcpSocket()` tcpSocket = tcpServer->nextPendingConnection(); //3.有数据时,读取tcpSocket数据 connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(readyRead_SLOT())); } //3.有数据时,读取tcpSocket数据 void Widget::readyRead_SLOT(){ QString buff; buff = tcpSocket->readAll(); ui->messageRCV->appendPlainText(buff); } //4.关闭服务器 void Widget::on_btnCloseServer_clicked() { //tcpServer->close(); tcpSocket->close(); } //5.发送数据 void Widget::on_btnSendMessage_clicked() { //toLocal8Bit()转化为字符数组,data()转化为字符指针 tcpSocket->write(ui->editMessageSend->text().toLocal8Bit().data()); } ``` #### 测试 ![image-20220123161139876](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123161139876.png) ### TcpClient #### QTcpSocket类 主要用到QTcpSocket类 #### QTcpClient编写 1. 添加支持 `.pro` ```properties QT += core gui network #引入network ``` 2. 创建UI界面 ![image-20220123171112644](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123171112644.png) 3. 声明、创建QTcpSocket对象 创建QTcpSocket对象 4. 打开连接 `connectToHost()` 5. 连接成功时触发槽函数 `connected()`这个信号在调用connectToHost()并成功建立连接之后发出。 6. 新数据时,槽函数 `readyRead()`有新数据到来时触发 7. 发送数据 `write()` `toLocal8Bit()`转化为字符数组,`data()`转化为字符指针 8. 关闭连接 `close()` #### 代码 ##### `TcpClient.pro` 略去 ##### `widget.h` ```c++ #ifndef WIDGET_H #define WIDGET_H #include //只需要引入socket #include namespace Ui { class Widget; } class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = 0); ~Widget(); //1.socket对象 QTcpSocket *tcpSocket; private slots: void on_btnConnect_clicked(); //3.连接成功槽函数 void connected_Slot(); //4.新数据到来时触发的槽函数 void readyRead_Slot(); //5.发送数据 void on_btnSend_clicked(); //6.关闭连接 void on_btnClose_clicked(); private: Ui::Widget *ui; }; #endif // WIDGET_H ``` ##### `widget.cpp` ```c++ #include "widget.h" #include "ui_widget.h" #include Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); //1.socket对象 tcpSocket = new QTcpSocket(this); } Widget::~Widget() { delete ui; } void Widget::on_btnConnect_clicked() { //2.打开连接 tcpSocket->connectToHost(ui->editIpInput->text(),ui->editPortInput->text().toUInt()); if(tcpSocket->waitForConnected(1000)){ QMessageBox::information(this,"提示","连接成功"); }else{ QMessageBox::critical(this,"提示","连接失败"); } //3.连接成功时连接函数 //3.`connected()`这个信号在调用connectToHost()并成功建立连接之后发出。 connect(tcpSocket,SIGNAL(connected()),this,SLOT(connected_Slot())); } //3.连接成功槽函数 void Widget::connected_Slot(){ //4.新数据时,连接函数 //`readyRead()`有新数据到来时触发 connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(readyRead_Slot())); } //4.新数据时槽函数 //发送数据到接收框 void Widget::readyRead_Slot(){ QString buff; buff = tcpSocket->readAll(); ui->pteMessageRCV->appendPlainText(buff); } //5.发送数据槽函数 void Widget::on_btnSend_clicked() { //toLocal8Bit()转化为字符数组,data()转化为字符指针 tcpSocket->write(ui->editMessageSend->text().toLocal8Bit().data()); } //6.关闭连接槽函数 void Widget::on_btnClose_clicked() { tcpSocket->close(); //tcpSocket->disconnectFromHost(); QMessageBox::information(this,"提示","断开连接"); } ``` #### 测试 ![image-20220123170825078](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123170825078.png) ![image-20220123172226132](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123172226132.png) ## QT网络编程-UDP通信 ### 概述 UDP不分客户端和服务端 只需要使用`QUdpSocket`类 ### QUdpSocket类 #### 判断是否有数据等待读取 `bool QUdpSocket::hasPendingDatagrams() const` 如果至少有一个数据报正在等待读取,则返回true;否则返回false。 #### 返回数据报的大小 `qint64 QUdpSocket::pendingDatagramSize() const` 返回第一个挂起的UDP数据报的大小。如果没有可用的数据报,这个函数返回-1。 #### 读取数据 `qint64 QUdpSocket::readDatagram(char **data*, qint64 *maxSize*, QHostAddress **address* = Q_NULLPTR, quint16 **port* = Q_NULLPTR)` 接收不大于maxSize字节的数据报,并将其存储在数据中。发送者的主机地址和端口存储在*address和*port中(除非指针为0)。 成功时返回数据报的大小;否则返回1。 如果maxSize太小,则数据报的其余部分将丢失。为了避免数据丢失,在尝试读取挂起的数据报之前,调用pendingDatagramSize()来确定它的大小。如果maxSize为0,该数据报将被丢弃。 #### 绑定端口 bool QAbstractSocket::bind(const [QHostAddress](qhostaddress.html) &*address*, [quint16](../qtcore/qtglobal.html#quint16-typedef) *port* = 0, [BindMode](qabstractsocket.html#BindFlag-enum) *mode* = DefaultForPlatform) 使用BindMode模式在端口端口上绑定到地址。 将这个套接字绑定到地址地址和端口端口。 对于UDP套接字,绑定后,当UDP数据报到达指定的地址和端口时,就会发出信号QUdpSocket::readyRead()。因此,这个函数对编写UDP服务器很有用。 对于TCP套接字,这个函数可以用来指定出连接使用哪个接口,这在有多个网络接口的情况下很有用。 默认情况下,套接字使用DefaultForPlatform BindMode绑定。如果不指定端口,则选择随机端口。 成功时,函数返回true,套接字进入BoundState;否则返回false。 bool QAbstractSocket::bind([quint16](../qtcore/qtglobal.html#quint16-typedef) *port* = 0, [BindMode](qabstractsocket.html#BindFlag-enum) *mode* = DefaultForPlatform) 这是一个重载函数。 绑定到QHostAddress:任何端口端口,使用BindMode模式。 默认情况下,套接字使用DefaultForPlatform BindMode绑定。如果不指定端口,则选择随机端口。 #### 发送数据 [q](../qtcore/qtglobal.html#qint64-typedef)int64 QUdpSocket::writeDatagram(const char **data*, [qint64](../qtcore/qtglobal.html#qint64-typedef) *size*, const [QHostAddress](qhostaddress.html) &*address*, [quint16](../qtcore/qtglobal.html#quint16-typedef) *port*) 将数据报以数据大小发送到主机地址在端口的地址端口。返回成功时发送的字节数;否则返回1。 数据报总是写成一个块。数据报的最大大小高度依赖于平台,但可以低至8192字节。如果数据报太大,这个函数将返回-1,error()将返回DatagramTooLargeError。 发送大于512字节的数据报通常是不建议的,因为即使它们成功发送,它们也可能在到达最终目的地之前被IP层分片。 警告:在一个连接的UDP套接字上调用这个函数可能会导致错误和没有发送数据包。如果您正在使用已连接的套接字,请使用write()发送数据报。 [q](../qtcore/qtglobal.html#qint64-typedef)int64 QUdpSocket::writeDatagram(const [QNetworkDatagram](qnetworkdatagram.html) &*datagram*) 这是一个重载函数。 使用那里设置的网络接口和跳数限制,将数据报数据报发送到包含在数据报中的主机地址和端口号。如果没有设置目的地址和端口号,这个函数将发送到传递给connectToHost()的地址。 如果目的地址是IPv6,范围id非空,但与数据报中的接口索引不同,则操作系统将选择发送哪个接口是未定义的。 如果函数成功,则返回发送的字节数;如果遇到错误,则返回-1。 警告:在一个连接的UDP套接字上调用这个函数可能会导致错误和没有发送数据包。如果您正在使用已连接的套接字,请使用write()发送数据报。 [q](../qtcore/qtglobal.html#qint64-typedef)int64 QUdpSocket::writeDatagram(const [QByteArray](../qtcore/qbytearray.html) &*datagram*, const [QHostAddress](qhostaddress.html) &*host*, [quint16](../qtcore/qtglobal.html#quint16-typedef) *port*) 这是一个重载函数。 将数据报数据报发送到主机地址主机和在端口端口。 如果函数成功,则返回发送的字节数;如果遇到错误,则返回-1。 #### 其他 ` bool QUdpSocket::joinMulticastGroup(const QHostAddress&*groupAddress*)` 加入操作系统选择的缺省接口上的groupAddress指定的组播组。套接字必须处于BoundState状态,否则将发生错误。 注意,如果你试图加入一个IPv4组,你的套接字不能使用IPv6(或双模式,使用QHostAddress::Any)绑定。你必须使用QHostAddress::AnyIPv4代替。 如果成功,此函数返回true;否则返回false并设置相应的socket错误。 `bool QUdpSocket::joinMulticastGroup(const QHostAddress &*groupAddress*, const QNetworkInterface &*iface*)` 这是一个重载函数。 加入该接口的组播组地址groupAddress。 `bool QUdpSocket::leaveMulticastGroup(const QHostAddress &*groupAddress*)` 将groupAddress指定的组播组保留在操作系统选择的缺省接口上。套接字必须处于BoundState状态,否则将发生错误。 如果成功,此函数返回true;否则返回false并设置相应的socket错误。 `bool QUdpSocket::leaveMulticastGroup(const QHostAddress &*groupAddress*, const QNetworkInterface &*iface*)` 这是一个重载函数。 离开接口上的groupAddress指定的组播组。 `QNetworkInterface QUdpSocket::multicastInterface() const` 返回组播数据报的出接口的接口。这对应于IPv4套接字的IP_MULTICAST_IF套接字选项和IPv6套接字的IPV6_MULTICAST_IF套接字选项。如果之前没有设置接口,这个函数返回一个无效的QNetworkInterface。套接字必须处于BoundState,否则返回无效的QNetworkInterface。 `QNetworkDatagram QUdpSocket::receiveDatagram(qint64 *maxSize* = -1)` 接收一个不大于maxSize字节的数据报,并在QNetworkDatagram对象中返回它,以及发送方的主机地址和端口。如果可能,此函数还将尝试确定数据报的目的地址、端口和接收时的跳数。 失败时,返回一个报告无效的QNetworkDatagram。 如果maxSize太小,则数据报的其余部分将丢失。如果maxSize为0,该数据报将被丢弃。如果maxSize为-1(默认值),这个函数将尝试读取整个数据报。 `void QUdpSocket::setMulticastInterface(const QNetworkInterface &*iface*)` 设置组播数据报的出接口为接口interface。这对应于IPv4套接字的IP_MULTICAST_IF套接字选项和IPv6套接字的IPV6_MULTICAST_IF套接字选项。套接字必须处于BoundState,否则这个函数什么都不做。 ### 实例 #### 编写界面 ![image-20220123173536182](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123173536182.png) #### 引入支持文件 qmake:`network` header:`\#include ` #### 代码 * 创建QUdpSocket对象 * 打开连接 `bind()` `readyRead()` * 读取数据 `hasPendDatagrams()` `hasPendDatagramsSize()` `readDatagrams()` * 发送数据 `writeDatagram()` * 关闭 ##### `widget.h` ```c++ #ifndef WIDGET_H #define WIDGET_H #include //添加头文件Qudpsocket #include namespace Ui { class Widget; } class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = 0); ~Widget(); //1.创建对象 QUdpSocket *udpSocket; private slots: //2.打开连接槽函数 void on_btnOpen_clicked(); //3.readyRead槽函数 void readyRead(); //4.发送 void on_btnSend_clicked(); //关闭 void on_btnClose_clicked(); private: Ui::Widget *ui; }; #endif // WIDGET_H ``` ##### `widget.cpp` ```c++ #include "widget.h" #include "ui_widget.h" #include #include Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); //1.创建对象 udpSocket = new QUdpSocket(this); } Widget::~Widget() { delete ui; } void Widget::on_btnOpen_clicked() { //2.打开连接 //判断是否成功 if(udpSocket->bind(ui->editLocalPort->text().toUInt()) == true){ QMessageBox::information(this,"提示","成功"); }else{ QMessageBox::critical(this,"提示","失败"); }; //3.连接成功触发函数 connect(udpSocket,SIGNAL(readyRead()),this,SLOT(readyRead())); } void Widget::readyRead(){ //3.槽函数,读取数据 while(udpSocket->hasPendingDatagrams()){ //判断是否有数据 QByteArray ary; ary.resize(udpSocket->pendingDatagramSize()); udpSocket->readDatagram(ary.data(),ary.size()); QString buff; buff = ary.data(); ui->editMessageRCV->appendPlainText(buff); } } void Widget::on_btnSend_clicked() { //4.发送数据 //数据 QString sendBuff; sendBuff = ui->editMessageSend->text(); //ip QHostAddress addr; addr.setAddress(ui->editTargetIP->text()); //端口 quint16 port; port = ui->editTargetPort->text().toUInt(); //发送数据 udpSocket->writeDatagram(sendBuff.toLocal8Bit().data(),sendBuff.length(),addr,port); } void Widget::on_btnClose_clicked() { //5.关闭 udpSocket->close(); } ``` #### 测试 ![image-20220123182754858](https://gitee.com/tianzhendong/img/raw/master//images/image-20220123182754858.png) ## QT定时器 ```c++ QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(update())); timer->start(1000) ``` ## Qt QMessageBox用法详解 [c.biancheng.net](http://c.biancheng.net/view/9421.html) QMessageBox 是 Qt 框架中常用的一个类,可以生成各式各样、各种用途的消息对话框,如图 1 所示。 ![](https://gitee.com/tianzhendong/img/raw/master/images/202203240942453.gif) 图 1 QMessageBox消息对话框 很多 GUI 程序都会用到消息对话框,且很多场景中使用的消息对话框是类似的,唯一的区别只是提示信息不同。为了提高程序员的开发效率,避免重复地“造轮子”,Qt 开发者设计好了几种通用的 QMessageBox 消息对话框,需要时可以直接使用。 ### 通用的QMessageBox消息框 Qt 提供了 6 种通用的 QMessageBox 消息对话框,通过调用 QMessageBox 类中的 6 个静态成员方法,可以直接在项目中使用它们。 #### 1) information消息对话框 information 对话框常用于给用户提示一些关键的信息,它的外观如下图所示: ![](https://gitee.com/tianzhendong/img/raw/master/images/202203240942112.gif) 图 2 information 消息对话框 在项目中使用 information 消息对话框,直接调用 QMessageBox 类中的 information() 静态成员方法即可,该方法的语法格式如下: ```c StandardButton QMessageBox::information(QWidget *parent, const QString &title, const QString &text, StandardButtons buttons = Ok, StandardButton defaultButton = NoButton) ``` 各个参数的含义是: * parent:指定消息对话框的父窗口,消息提示框会作为一个独立的窗口显示在父窗口的前面。消息提示框从弹出到关闭的整个过程中,用户无法操作父窗口,更不能删除父窗口; * title:指定消息对话框的标题,即图 2 中的 Titile; * text:指定消息对话框的具体内容,即图 2 中的 text; * buttons:指定消息对话框中包含的按钮。默认情况下,消息对话框只包含一个按钮,即图 2 中显示的 "OK" 按钮。根据需要,我们可以用`|`按位或运算符在消息对话框中设置多个按钮,例如 `QMessageBox::Ok|QMessageBox::Cancel`; * defaultButton:指定 Enter 回车键对应的按钮,用户按下回车键时就等同于按下此按钮。注意,defaultButton 参数的值必须是 buttons 中包含的按钮,当然也可以不手动指定,QMessageBox 会自动从 buttons 中选择合适的按钮作为 defaultButton 的值。 information() 函数会返回用户按下的按钮。StandardButton 是 QMessageBox 类中定义的枚举类型,每个枚举值代表一种按钮。StandardButton 类型中的值有很多,下表给大家罗列了几个常用的: 表 1 QMessageBox::StandardButton 枚举类型值 | 枚举值 | 含 义 | | -------------------- | ------------------------------------------------------------ | | QMessageBox::Ok | 标有 "OK" 字样的按钮,通常用来表示用户接受或同意提示框中显示的信息。 | | QMessageBox::Open | 标有 "Open" 字样的按钮。 | | QMessageBox::Save | 标有 "Save" 字样的按钮。 | | QMessageBox::Cancel | 标有 "Cancel" 字样的按钮。点击此按钮,通常表示用户拒绝接受提示框中显示的信息。 | | QMessageBox::Close | 标有 "Close" 字样的按钮。 | | QMessageBox::Discard | 标有 "Discard" 或者 "Don't Save" 字样的按钮,取决于运行平台。 | | QMessageBox::Apply | 标有 "Apply" 字样的按钮。 | | QMessageBox::Reset | 标有 "Reset" 字样的按钮。 | | QMessageBox::Yes | 标有 "Yes" 字样的按钮。 | | QMessageBox::No | 标有 "No" 字样的按钮。 | 例如,使用 information() 函数实现图 2 所示的对话框,实现代码为: ```c QMessageBox::StandardButton result = QMessageBox::information(&widget, "Title","text"); ``` 其中,widget 是我们创建好的 QWidget 窗口,创建好的 information 对话框会显示在 widget 窗口的前面。通过用 result 接收 information() 函数的返回值,我们可以得知用户选择的是哪个按钮。 #### 2) critical消息对话框 critical 消息对话框常用于给用户提示“操作错误”或“运行失败”的信息,它的外观如下图所示: ![](https://gitee.com/tianzhendong/img/raw/master/images/202203240943807.gif) 图 3 critical 消息对话框 项目中使用 critical 消息对话框,直接调用 QMessageBox 类提供的 critical() 静态成员方法即可,该方法的语法格式为: ```c StandardButton QMessageBox::critical(QWidget *parent, const QString &title, const QString &text, StandardButtons buttons = Ok, StandardButton defaultButton = NoButton) ``` 各个参数的含义以及返回值的含义,都与 information() 函数相同,这里不再重复赘述。 例如,使用 critical() 函数实现图 3 所示的对话框,实现代码为: ```c QMessageBox::StandardButton result=QMessageBox::critical(&widget, "Title","text"); ``` 其中,widget 是我们创建好的 QWidget 窗口,创建好的 critical 对话框会显示在 widget 窗口的前面。 #### 3) question消息对话框 question 对话框常用于向用户提出问题并接收用户的答案,它的外观如下图所示: ![](https://gitee.com/tianzhendong/img/raw/master/images/202203240943988.gif) - 图 4 question消息对话框 项目中使用 question 对话框,可以直接调用 QMessageBox 类的 question() 静态成员方法,该方法的语法格式为: ```c StandardButton QMessageBox::question(QWidget *parent, const QString &title, const QString &text, StandardButtons buttons = StandardButtons( Yes | No ), StandardButton defaultButton = NoButton) ``` 各个参数的含义以及返回值的含义,都与 information() 函数相同。 例如,使用 question() 函数实现图 4 所示的对话框,实现代码为: ```c QMessageBox::StandardButton result\=QMessageBox::question(&widget, "Title","text"); ``` 其中,widget 是我们创建好的 QWidget 窗口,创建好的 question 对话框会显示在 widget 窗口的前面。 #### 4) warning消息对话框 warining 对话框常用于向用户显示一些警告信息,它的外观如下图所示: ![](https://gitee.com/tianzhendong/img/raw/master/images/202203240943182.gif) 图 5 warning消息对话框 项目中使用 warning 对话框,可以直接调用 QMessageBox 类的 warning() 静态成员方法,该方法的语法格式为: ```c StandardButton QMessageBox::warning(QWidget *parent, const QString &title, const QString &text, StandardButtons buttons = Ok, StandardButton defaultButton = NoButton) ``` 各个参数的含义以及返回值的含义,都与 information() 函数相同。 例如,使用 warning() 函数实现图 5 所示的对话框,实现代码为: ```c QMessageBox::StandardButton result=QMessageBox::warning(&widget, "Title","text"); ``` 其中,widget 是我们创建好的 QWidget 窗口,创建好的 warning 对话框会显示在 widget 窗口的前面。 #### 5) about和aboutQt对话框 about 对话框常常作为介绍某个产品或某项功能的临时窗口,它的外观如下图所示: ![](https://gitee.com/tianzhendong/img/raw/master/images/202203240943218.gif) 图 6 about消息对话框 注意,about 对话框没有固定的图标,它显示的图标可能来自父窗口、包含父窗口的顶层窗口等,也可能使用和 information 对话框相同的图标。 项目中使用 about 对话框,直接调用 QMessageBox 类提供的 about() 静态成员方法即可,该方法的语法格式如下: void QMessageBox::about(QWidget \*parent, const QString &title, const QString &text) 各个参数的含义和与 information() 函数相同。和前面的几种对话框不同,about对话框中只包含一个默认的 Ok 按钮,且 about() 函数没有返回值。 aboutQt 可以看做是 about 对话框的一个具体实例,它只能显示 Qt 的介绍信息,如下图所示: ![](https://gitee.com/tianzhendong/img/raw/master/images/202203240943559.gif) 图 7 aboutQt对话框 项目中使用 aboutQt 对话框,直接调用 QMessageBox 类提供的 aboutQt() 静态成员方法即可,该函数的语法格式如下: void QMessageBox::aboutQt(QWidget \*parent, const QString &title = QString()) 我们只能设置 aboutQt 对话框的 parent 父窗口和 title 标题,不能自定义它的内容。所以在实际场景中,aboutQt() 对话框很少使用。 ### 自定义QMessageBox对话框 以上 6 种通用的 QMessageBox 对话框,界面上的图片无法修改,按钮上的文字也无法修改(例如无法将 OK、No 改成中文)。如果想修改它们,就需要自定义一个 QMessageBox 对话框。 #### QMessageBox对话框的创建 程序中创建 QMessageBox 对象,必须先引入``头文件。QMessageBox 类提供了两个构造函数,分别是: ```c QMessageBox::QMessageBox(QWidget *parent = Q_NULLPTR) QMessageBox::QMessageBox(Icon icon, const QString &title, const QString &text, StandardButtons buttons = NoButton, QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint) ``` 第一个构造函数可以创建一个“空白”对话框,即对话框中不包含任何文本和按钮。当然,通过调用 QMessageBox 类提供的成员方法,可以向“空白”对话框中添加各种元素(图标、文本、按钮等)。 第二个构造函数中,各个参数的含义是: * icon:指定对话框中的图片。Icon 是 QMessageBox 类中定义的枚举类型,内部包含 QMessageBox::NoIcon、QMessageBox::Question、QMessageBox::Information、QMessageBox::Warning、QMessageBox::Critical 几个值,分别表示:不指定图片、question对话框的图片(图 4)、information对话框的图片(图 2)、warning对话框的图片(图 5)、critical对话框的图片(图 3)。 * title:指定对话框的标题; * text:指定对话框中显示的文本信息; * buttons:指定对话框中包含的的按钮,可选值如表 1 所示。 * parent:指定对话框的父窗口; * f:指定对话框的属性。WindowFlags 是 Qt 提供的枚举类型,内部包含的值有很多,有的用来指定对话框的用途(比如 Qt::Dialog 表示对话框窗口),有的用来指定对话框的外观(比如 MSWindowsFixedSizeDialogHint 表示给对话框添加一个细的边框) 举个简单的例子: ```c #include #include int main(int argc, char *argv[]) { QApplication a(argc, argv); //创建 QMessageBox 类对象 QMessageBox MyBox(QMessageBox::Question,"Title","text",QMessageBox::Yes|QMessageBox::No); //使 MyBox 对话框显示 MyBox.exec(); return a.exec(); } ``` 程序中创建了一个 MyBox 对话框,通过调用 QMessageBox 提供的 exec() 方法,可以使 MyBox 对话框弹出。运行程序可以发现,MyBox 对话框的外观和图 4 的 question 对话框完全一样。 #### QMessageBox对话框的使用 QMessageBox 类提供了很多功能实用的成员方法,方便我们快速地制作出实际场景需要的对话框。 下表给大家罗列了常用的一些 QMessageBox 类成员方法: 表 2 QMessageBox 常用成员方法 | 成员方法 | 功 能 | | ------------------------------------------------------------ | ------------------------------------------------------------ | | void QMessageBox::setWindowTitle(const QString &title) | 设置对话框的标题。 | | void setText(const QString &text) | 设置对话框中要显示的文本。 | | void setIconPixmap(const QPixmap &pixmap) | 设置对话框中使用的图片。 | | QAbstractButton \*QMessageBox::clickedButton() const | 返回用户点击的按钮。 | | QPushButton \*QMessageBox::addButton(const QString &text, ButtonRole role) | 向对话框中添加按钮,text 为按钮的文本,role 是 QMessageBox::ButtonRole 枚举类型的变量,用于描述按钮扮演的角色,它的可选值有 QMessageBox::AcceptRole(同 OK 按钮)、QMessageBox::RejectRole(同 Cancel 按钮)等。 | | int QMessageBox::exec() | 使当前对话框弹出,除非用户关闭对话框,否则对话框将一直存在。此外,当对话框中使用的都是 Qt 提供的按钮时,该方法可以监听用户点击的是哪个按钮,并将该按钮对应的枚举值返回;如果对话框中包含自定义按钮,需要借助 clickedButton() 方法确定用户点击的按钮。 | 举个简单的例子: ```c #include #include #include #include int main(int argc, char *argv[]) { QApplication a(argc, argv); QMessageBox MBox; MBox.setWindowTitle("QMessageBox自定义对话框"); MBox.setText("这是一个自定义的对话框"); MBox.setIconPixmap(QPixmap("C:\\Users\\xiexuewu\\Desktop\\icon_c.png")); QPushButton *agreeBut = MBox.addButton("同意", QMessageBox::AcceptRole); MBox.exec(); if (MBox.clickedButton() == (QAbstractButton*)agreeBut) { //在 Qt Creator 的输出窗口中输出指定字符串 qDebug() << "用户点击了同意按钮"; } return a.exec(); } ``` 程序运行结果如图 8 所示,点击“同意”按钮后,我们会在 Qt Creator 的输出窗口中看到“用户点击了同意按钮”。 ![](https://gitee.com/tianzhendong/img/raw/master/images/202203240943937.gif) 图 8 自定义的 QMessageBox 对话框 #### QMessageBox的信号和槽 操作 QMessageBox 对话框,最常用的信号函数是 buttonClicked() 函数,最常用的槽函数是 exec() 函数,它们的语法格式和功能如下表所示。 表 3 QMessageBox信号和槽 | 信号函数 | 功 能 | | --------------------------------------------------------- | ------------------------------------------------------------ | | void QMessageBox::buttonClicked(QAbstractButton \*button) | 当用户点击对话框中的某个按钮时,会触发此信号函数,该函数会将用户点击的按钮作为参数传递给槽函数。 | | 槽函数 | 功 能 | | int QMessageBox::exec() | 弹出对话框,直到用户手动关闭对话框,此对话框将一直存在。 | 举个简单的例子: ```c //main.cpp #include #include #include #include #include QPushButton* agreeBut; QPushButton* disagreeBut; class MyWidget:public QWidget{ Q_OBJECT public slots: void buttonClicked(QAbstractButton * butClicked); }; void MyWidget::buttonClicked(QAbstractButton * butClicked){ if(butClicked == (QAbstractButton*)disagreeBut){ this->close(); } } int main(int argc, char *argv[]) { QApplication a(argc, argv); //创建主窗口 MyWidget myWidget; myWidget.setWindowTitle("主窗口"); myWidget.resize(400,300); //创建消息框 QMessageBox MyBox(QMessageBox::Question,"",""); MyBox.setParent(&myWidget); //设置消息框的属性为对话框,它会是一个独立的窗口 MyBox.setWindowFlags(Qt::Dialog); MyBox.setWindowTitle("协议"); MyBox.setText("使用本产品,请您严格遵守xxx规定!"); //自定义两个按钮 agreeBut = MyBox.addButton("同意", QMessageBox::AcceptRole); disagreeBut = MyBox.addButton("拒绝", QMessageBox::RejectRole); myWidget.show(); //添加信号和槽,监听用户点击的按钮,如果用户拒绝,则主窗口随之关闭。 QObject::connect(&MyBox,&QMessageBox::buttonClicked,&myWidget,&MyWidget::buttonClicked); MyBox.exec(); return a.exec(); } //MyWidget类的定义应该放到 .h 文件中,本例中将其写到 main.cpp 中,程序最后需要添加 #include "当前源文件名.moc" 语句,否则无法通过编译。 #include "main.moc" ``` 程序执行结果为: ![](https://gitee.com/tianzhendong/img/raw/master/images/202203240943430.gif) [查看原网页: c.biancheng.net](http://c.biancheng.net/view/9421.html)