You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2152 lines
71 KiB

3 years ago
---
title: QT
date: 2022年4月28日
author: TianZD
top: true
cover: true
toc: true
mathjax: false
summary: QT的安装教程,以及一些使用过程记录
tags:
- C++
- QT
categories:
- C++
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<<this->age<<endl;
}
student stu1;//实例化对象
stu1.age = 10;//通过点访问
stu1.test();
student *stu2 = new student;//在堆里定义,需要删除
stu2->age = 11;//通过箭头访问
stu2->test();
```
### 重载
**重载**:函数名相同,但是参数不同
### 构造函数和析构函数
**析构函数**:对象被删除或者生命周期结束时触发
**构造函数** :对象被创建的时候触发、
```c++
student::student(){
cout<<"hello,构造函数"<<endl;
}
student::~student(){
cout<<"bye,析构函数"<<endl;
}
```
### 虚函数和纯虚函数
**虚函数**:有实际定义的,允许派生类对他进行覆盖替换,用virtual修饰
**虚函数**:没有实际定义的虚函数
## QT工程基础
### 新建
### 项目文件结构
![image-20220122165058047](https://s2.loli.net/2022/01/22/q7dae3mKW2ITYUG.png)
.pro文件为项目文件
widget.ui文件为UI设计文件,双击进入UI界面设计
sources文件夹为代码文件夹
headers为库文件
### .PRO文件解析
```properties
#-------------------------------------------------
#
# Project created by QtCreator 2022-01-22T16:46:31
#
#-------------------------------------------------
QT += core gui #添加core gui模块
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets #qt大于4时加入widgets模块
TARGET = QTDemo2 # 生成app的名称
TEMPLATE = app ##编译产物的类型
# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS #定义的一个宏
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \ #指定工程里有哪些CPP
main.cpp \
widget.cpp
HEADERS += \ #指定工程中有哪些文件
widget.h
FORMS += \ #指定工程中有哪些ui文件
widget.ui
```
### widget.ui
![image-20220122165312247](https://s2.loli.net/2022/01/22/pdRG9q34ZuEUKO5.png)
## 第一个qt工程
### 添加控件
![image-20220122170231198](https://s2.loli.net/2022/01/22/XwNTV5jEZOHtvco.png)
密码输入设置密文:
![image-20220122171208294](https://s2.loli.net/2022/01/22/34YDbvTk6ZmIeus.png)
![image-20220122170248539](https://s2.loli.net/2022/01/22/dVmUZ6GQbuPjoTE.png)
### 控件改名
为了分析代码方便,需要改名字
要通俗易懂
![image-20220122171521061](https://s2.loli.net/2022/01/22/fUCVs9mbzDP3jle.png)
### 信号和槽
**信号**:控件发出的特定的信号
**槽**:槽函数,把控件发出的信号绑定到特定的槽函数
### 关联信号和槽
1. **自动关联**:右键控件——转到槽,选择相应的信号
2. **手动关联**
#### 自动关联
**右键控件——转到槽,选择相应的信号**
![image-20220122171503322](https://s2.loli.net/2022/01/22/Np73wr9UCTxAYfF.png)
**选择相应的信号后,会自动进行:**
1. 在`widget.h`中`private slots`下增加槽函数声明(注意,只能在`private slots`或者`public slots`下面)
```c++
private slots:
void on_btnLogin_clicked();
```
2. 自动在`widget.cpp`中增加函数定义(具体内容需要自己定义)
```c++
void Widget::on_btnLogin_clicked()
{
}
```
3. **随后自己定义相应的槽函数即可**
```c++
void Widget::on_btnLogin_clicked()
{
qDebug("login");//在调试台输出login
}
```
#### 手动关联
手动关联需要用到`connect()`函数
1. 将自动关联步骤中的1、2、3手动实现,实现槽函数
```c++
void Widget::on_btnRegister_clicked()
{
qDebug("register");
}
```
2. 在`widget.cpp`中,添加关联函数
`connect(ui->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——点击左侧<resource root>——选择文件
![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 <QSerialPortInfo>//添加串口头文件
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//获取串口号并显示
QStringList serialPortNums;
foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
serialPortNums<<info.portName();
}
ui->serialPortNum->addItems(serialPortNums);
}
Widget::~Widget()
{
delete ui;
}
```
### 实现逻辑功能
#### 创建串口对象
在widget.h中添加头文件,并声明串口对象指针
```c++
#include<QSerialPort>//引入串口头文件
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++
//判断串口是否打开成功,并提示,需要添加<QMessgaeBox>头文件
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 <QTcpServer> |
| ------------- | --------------------------------- |
| 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 <QTcpSocket> |
| ------------- | ------------------------------------------------------------ |
| 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 <QTcpServer>
#include <QTcpSocket>
```
##### 声明对象
```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 <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
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 <QWidget>
//只需要引入socket
#include <QTcpSocket>
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 <QMessageBox>
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> `
#### 代码
* 创建QUdpSocket对象
* 打开连接
`bind()`
`readyRead()`
* 读取数据
`hasPendDatagrams()`
`hasPendDatagramsSize()`
`readDatagrams()`
* 发送数据
`writeDatagram()`
* 关闭
##### `widget.h`
```c++
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
//添加头文件Qudpsocket
#include <QUdpSocket>
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 <QMessageBox>
#include <QHostAddress>
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>`头文件。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 <QApplication>
#include <QMessageBox>
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 <QApplication>
#include <QMessageBox>
#include <QPushButton>
#include <QDebug>
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 <QApplication>
#include <QWidget>
#include <QMessageBox>
#include <QPushButton>
#include <QAbstractButton>
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)