当前位置: 首页 > news >正文

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第六篇 嵌入式GUI开发篇-第八十三章 Qt基础

i.MX8MM处理器采用了先进的14LPCFinFET工艺,提供更快的速度和更高的电源效率;四核Cortex-A53,单核Cortex-M4,多达五个内核 ,主频高达1.8GHz,2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT、4G模块、CAN、RS485等接口一应俱全。H264、VP8视频硬编码,H.264、H.265、VP8、VP9视频硬解码,并提供相关历程,支持8路PDM接口、5路SAI接口、2路Speaker。系统支持Android9.0(支持获取root限)Linux4.14.78+Qt5.10.1、Yocto、Ubuntu20、Debian9系统。适用于智能充电桩,物联网,工业控制,医疗,智能交通等,可用于任何通用工业和物联网应用、

【公众号】迅为电子

【粉丝群】258811263


第八十三章 Qt基础

Qt的移植性非常的强。一套代码我们不用改太多,直接通用所有的平台。

不久的将来,Qt会被用到MCU上,学习Qt还是非常有意义的。

83.1 安装Qtcreator

本章内容对应视频讲解链接(在线观看):

在Windows上搭建QT开发环境  https://www.bilibili.com/video/BV1tp4y1i7EJ?p=2

Qtcreator下载地址:http://download.qt.io/new_archive/qt/5.11/进入选择版本号界面,本教程使用的是5.11.1,也推荐读者选择此版本。进入如界面后,选择安装包,我们在windows下学习Qt,所以选择qt-opensource-windows-x86-5.11.1.exe,点击即可下载。

下载后右键点击exe文件,选择以管理员身份运行。注册账号,点击下一步(或next),选择安装路径。选择下一步,勾选需要用到的组件,本阶段教程需要勾选以下七个选项: 

选择完后一直点下一步,安装完成后如下图: 

83.2 创建工程

创建一个QT工程步骤:

步骤一:

步骤二:

填写工程名字,不要有中文路径:

 步骤三:填写类名:

创建成功后如下图: 

工程目录下的.pro工程文件分析: 

点击forms,然后双击ui文件,就可以进入ui编辑器。

ui编辑器面板介绍:

83.3 信号和槽

本章内容对应视频讲解链接(在线观看):

QT信号和槽  https://www.bilibili.com/video/BV1tp4y1i7EJ?p=6

信号就是指控件发出的特定的信号。槽就是槽函数的意思,信号和槽都位于类中,不是C++标准代码。我们可以把槽函数绑定在某一个控件的信号上。当需要调用外部函数时,发送一个信号,此时与该信号相关联的槽便会被调用,槽其实就是一个函数,槽与信号的关联要由程序员来完成,关联方法有自动关联和手动关联。

83.3.1 自动关联

使用Qt信号和槽的自动关联,可加快开发速度,一般用于同一个窗体之间的控件关联,槽函数格式非常关键,格式为:

void on_<窗口部件名称>_<信号名称>(<signal parameters>);

自动关联步骤:

步骤一:手动选择相应的控件,然后右键->转到槽。

 

选择信号类型:

 自动关联会在.h文件声明槽函数。槽函数只能声明到private slots或者public  slots 下面。按住Ctrl+鼠标左键,跳转到.cpp文件对应的函数功能实现部分。填写功能代码,我们在槽函数内用qDebug打印信息。

保存,点击构建,运行: 

 

每次点击,按钮都会发信号,对应的槽函数就会执行,结果如下图:

 

83.3.2 手动关联

信号和槽机制是QT的核心机制,要精通QT编程就必须对信号和槽有所了解。信号和槽是一种高级接口,应用于对象之间的通信,它是QT的核心特性,也是QT区别于其它工具包的重要地方。此外如果遇到不懂的函数或类,可以先选中,然后按F1键,即可查看介绍。

虽然Qt有自动关联功能,但涉及到多个窗体和复杂事件的时候,只能使用手动关联,手动关联使用connect这个函数。

函数定义:

connect(const QObject *sender, const char *signal,

       const QObject *receiver, const char *member,

  Qt::ConnectionType = Qt::AutoConnection);

参数含义:

sender:发送对象;

singal:发送对象里面的一个信号,格式一般为SIGNAL(信号);

receiver:接收对象;

member:接收对象里面的槽函数,格式一般为SLOT(信号)。

ConnectionType:设置信号和槽的同步异步,一般使用默认值Qt::AutoConnection,可不填。

通常只传递前四个参数,如下所示:

connect(A,SIGNAL(B),C,SLOT(D));

当对象A发出B信号时候,就

会触发对象C的槽函数D

 signals 是 QT 的关键字,而非 C/C++ 的。signals 关键字指出进入了信号声明区,随后即可声明自己的信号。

slots   槽函数是普通的 C++ 成员函数,当与其关联的信号被发射时,这个槽函数就会被调用。槽函数有的参数个数和类型,在对应的信号函数中必须一一对应,即信号函数的参数个数必须多于或等于槽函数的个数。

emit  Qt定义的一个宏,作用是发射信号,与此信号关联的槽函数就会被调用。

例程:我们在widget.h中自定义一个信号和一个槽函数

并在widget.cpp实现槽函数: 

然后在widget.cpp中绑定信号和槽: 

在widget.ui中创建按钮,并转到槽,自动关联的槽函数如下图: 

发射信号 '

这样,点击按钮,就会发射自定义的信号my_Signal(),与my_Signal()相关联的this对象槽函数my_Solt就会被调用,槽函数就会输出打印信息,如下图: 

部分核心代码如下:

Widget.h:

class student
{
public:student();~student();char name[64];int age;void test();void test(int a);
#include <QWidget>namespace Ui {
class Widget;
}class Widget : public QWidget
{Q_OBJECTpublic:explicit Widget(QWidget *parent = 0);~Widget();
signals:void my_Signal(void);   //自定义的信号private slots:void on_pushButton_clicked();void my_Solt(void);     //自定义的槽函数private:Ui::Widget *ui;
};

 Widget.cpp:

#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget)
{ui->setupUi(this);connect(this,SIGNAL(my_Signal()),this,SLOT(my_Solt()));
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_clicked()
{emit my_Signal();
}
void Widget::my_Solt(void)
{qDebug("按下");
}

83.4 给界面添加图片

本章内容对应视频讲解链接(在线观看):

仿写一个智能家居界面(上)  https://www.bilibili.com/video/BV1tp4y1i7EJ?p=7

83.4.1 添加资源

选中项目名称,右键单击—>选择添加新文件

在弹出窗口中选择Qt—>Qt Resource File ,选择Choose 

填写写资源名称, 

例如填写 picture后,在工程下的Resources会出现picture.qrc文件,成功后如下图。

双击picture.qrc,点击“添加前缀”。

 

指定路径,这里笔者填的“/”此路径可根据需要自定义: 

83.4.2 添加图片

我们首先将要添加的图片复制到工程目录下。

右击picture.qrc,选择Open With -> 资源编辑器,出现资源管理界面,点击下面的 添加->添加前缀,下方前缀栏填写的是 “ / ”,这个路径可以根据需要自定义,然后保存。

 

再次点击“添加”,点击“添加文件”,

选中图片,点击“打开”,进入资源编辑器


 在资源编辑器中会看到添加的图片,然后保存。

以此点开Resources下的各个文件夹,即可看到添加的图片,此时图片已经添加到工程。 

83.4.3 Label添加图片

在ui文件添加QLabel组件,右击->选择改变样式表,

 弹出对话框,选择添加资源->border image,

选择要添加的图片,如下图: 

点击OK,apply,OK,即可完成添加,如下图: 

83.5 界面布局

本章内容对应视频讲解链接(在线观看):

仿写一个智能家居界面(中)  https://www.bilibili.com/video/BV1tp4y1i7EJ?p=8

 

83.5.1 水平布局

Horizontal Layout  水平方向布局,组件自动在水平方向上分布

使用时先选中组件,然后点击水平布局即可完成,可看到组件变为水平排列。如下图:

 

 

83.5.2 垂直布局

Vertical Layout 垂直方向布局,组件自动在垂直方向上分布,操作方法和水平布局一致,在布局之后组件垂直排列。

我们点击打破布局按钮,重新选择要布局的组件,然后点击垂直布局按钮,如下图:

83.5.3 栅格布局

Grid Layout  网格状布局,网状布局大小改变时,每个网格的大小都改变

 

我们发现布局之后各个组件都是紧挨着的,这时候可以用“弹簧”控件来控制组件位置。

Horizontal Spacer  一个用于水平分隔的空格

 

 完成后如下图:

Vertical Spacer  一个用于垂直分隔的空格,拖拽组件如下图: 

选中点击垂直布局,完成后如下图:

83.6 界面切换

本章内容对应视频讲解链接(在线观看):

仿写一个智能家居界面(下)  https://www.bilibili.com/video/BV1tp4y1i7EJ?p=9

本节通过实验介绍通过创建窗口对象的方式实现界面切换:

步骤一:

在主界面ui文件添加pushButton按钮,

 

然后新建一个窗口,工程下创建新的Qt设计师界面类,如下图:

我们选择Widget,用户可以根据需要选择,然后输入类名windowRun。


创建完成后如下图:

步骤二:关联ui界面的pushButton的clicked()信号和槽函数runSolt(),部分代码:

{

    ui->setupUi(this);

    connect(ui->pushButton,SIGNAL(clicked()),SLOT(runSlot()));//关联信号槽

}

步骤三:创建windowRun类对象win,设置大小,显示。


void Example::runSlot(void)
{qDebug ("Run slots");//显示新窗口win = new windowRun();//设置win窗口尺寸与此窗口尺寸相同
win->setGeometry(this->geometry());
//显示win->show();
}

运行程序后,点击按钮后即可跳转到第二个界面。

 

83.7 Qt串口编程

本章内容对应视频讲解链接(在线观看):

QT上位机开发之串口助手(上)  https://www.bilibili.com/video/BV1tp4y1i7EJ?p=10

QT上位机开发之串口助手(下)  https://www.bilibili.com/video/BV1tp4y1i7EJ?p=11

本节我们使用Qt来编写一个简单的上位机。

实验介绍:组装ui界面,使用Qt提供的串口类来实现串口收发功能,需要掌握的相关 Qt知识有以下几点:

QSerialPort是 Qt5中的附加模块,提供了基本的功能,包括配置、I/O操作、获取和设置RS-232引脚的信号,要链接QtSerialPort模块,需要在.pro文件中添加+=serialport。

QSerialPort封装了丰富的成员变量来对应串口属性,还有很多操作串口的成员函数,常用的成员函数有setPort()或setPortName(),setBaudRate(),setDataBits(),setStopBits(),setParity()等,可以用这些函数设置要访问的串口设备。本实验使用了readyRead()信号,当有数据到来时会触发类对象的readyRead()信号,然后利用它的成员函数 readAll()读取。

类QSerialPortInfo可以获取可用的串口信息,如端口名称,系统位置,产品号,描述,制造商等信息。我们把它获取到的端口信息交给QSerialPort类对象。

83.7.1 界面布局

步骤一:将控件拖到ui界面上

接收框使用Plain Text Edit,发送框使用lineEdit,属性选择组件使用Combo Box。

步骤二:属性设置栏布局,以串口号为例,依次水平布局属性选择位。 

然后全部选中属性选择框,点垂直布局 

效果如下图: 

步骤三:功能栏布局,在按钮间添加弹簧,点击水平布局。

选中Lbel,发送框和功能按钮,点击垂直布局:

如下图:

选中属性栏和右侧组件,然后点击水平布局,如下图:

 

完成后: 

再仿照上边的方法将下方的功能部分和接收框垂直布局:

添加完组件后,更改接收框为只读:点击接收框,在QTextEdit里标记readOnly。

在右上角更改ui界面对象名,界面组装完成后可以根据需要自行修改,

双击属性选择框添加属性:

如下图: 

 

83.7.2 实现串口功能

1.编辑工程文件(后缀为 .pro的文件)在QT       += core gui后添加 serialport。

 

2.自动获取串口

使用QSerialPortInfo:::availablePorts()获取当前串口,该函数返回容器类Qlist<QSerialPortInfo>,用Qt定义的关键字foreach遍历容器Qlist里的串口信息,并将串口信息放到QStringList的类对象serialNamePort,显示到ui的串口组件。

{
{ui->setupUi(this);QStringList serialNamePort;//遍历:availablePorts()返回的串口信息foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()){serialNamePort << info.portName();}ui->serialCb->addItems(serialNamePort);
}

编译后点击串口选择框,会出现已经连接的串口。

3.打开串口功能和属性设置

步骤一:实例化串口类QSerialPort对象serialPort,对串口的操作就是对serialPort对象的操作,调用QSerialPort封装的成员变量(属性)和成员函数(功能)就能控制串口。

class Example : public QMainWindow
{
public:
..........
QSerialPort * serialPort;
..........
};ui(new Ui::Example)
{ui->setupUi(this);
......serialPort = new QSerialPort;......
}

步骤二:填充波特率,数据位,停止位,校验位等属性。获取ui组件传递过来的串口信息,将串口属性填充到serialPort对象。

步骤三:打开串口,判断是否打开成功。

/*打开按钮*/
void Example::on_openCb_clicked()
{QSerialPort::BaudRate bauRate;   //波特率QSerialPort::DataBits dataBits;  //数据位QSerialPort::StopBits stopBits;  //停止位QSerialPort::Parity   checkBits; //校验位//设置波特率if     (ui->baudCb->currentText() == "4800"  )    { bauRate = QSerialPort::Baud4800;  }else if(ui->baudCb->currentText() == "9600"  )    { bauRate = QSerialPort::Baud9600;  }else if(ui->baudCb->currentText() == "115200")    { bauRate = QSerialPort::Baud115200;}//设置数据位if    (ui->dataCb->currentText() == "5")   { dataBits = QSerialPort::Data5;}else if(ui->dataCb->currentText() == "6")   { dataBits = QSerialPort::Data6;}else if(ui->dataCb->currentText() == "7")   { dataBits = QSerialPort::Data7;}else if(ui->dataCb->currentText() == "8")   { dataBits = QSerialPort::Data8;}//设置停止位if     (ui->stopCb->currentText() == "1"  ) { stopBits = QSerialPort::OneStop;         }else if(ui->stopCb->currentText() == "1.5" ) { stopBits = QSerialPort::OneAndHalfStop;  }else if(ui->stopCb->currentText() == "2"   ) { stopBits = QSerialPort::TwoStop;         }//设置校验位if(ui->checkCb->currentText() == "none"  )  { checkBits = QSerialPort::NoParity; }//填充串口对象的属性值serialPort->setPortName(ui->serialCb->currentText());serialPort->setBaudRate(bauRate);serialPort->setDataBits(dataBits);serialPort->setStopBits(stopBits);serialPort->setParity(checkBits);//设置好属性后打开串口if(serialPort->open(QIODevice::ReadWrite) == true){QMessageBox::information(this,"提示","成功");}else{QMessageBox::critical(this,"提示","失败");}
}

4.收发串口数据功能

读数据:每当数据流从串口到达系统一次,就会传到Qt应用程序一次,readyRead信号就会触 发一次,所以可以用前面章节讲的信号和槽机制将readyRead信号和槽函数绑定,然后 就可以在槽函数中读取串口数据。槽函数中使用readAll()读取数据,使用带换行功能的appendPlainText()显示到ui的接收窗口。

//类中声明槽函数
private slots:void serialPortReadyRead_Solt(void);
//readyRead信号和槽函数绑定
connect(serialPort,SIGNAL(readyRead()),this,SLOT(serialPortReadyRead_Solt()));//读串口
void Example::serialPortReadyRead_Solt(void)
{QString buf;buf = QString(serilaPort->readAll());ui->recvEdit->appendPlainText(buf);
}

写数据:获取ui 界面填写的信息,ui->sendEdit->text(),使用QSerialPort的成员函数 write将数据写到串口。

void Widget::on_sendBt_clicked()

{

    serilaPort->write(ui->sendEdit->text().toLocal8Bit().data());

}

5.关闭串口功能

使用QSerialPort的成员函数close()关闭串口。

void Widget::on_closeBt_clicked()

{

serilaPort->close();

}

6.清空发送栏数据

调用ui组件lineEdit的成员函数clear即可清空数据

void Widget::on_clearBt_clicked()

{

    ui->recvEdit->clear();

}

编译测试,结果如

83.8 Qt程序打包和部署

本章内容对应视频讲解链接(在线观看):

把QT程序打包成Windows软件  https://www.bilibili.com/video/BV1tp4y1i7EJ?p=12

因为我们要把写好的程序发给用户来用,写好的源码也不方便给别人看,所以要把程序进行打包部署。

步骤一:点击左下角的电脑图标将Debug模式切换到Release模式。

 

release模式:发布版本,不对源代码进行调试,基本没有调试信息。

debug模式:调试版本,有很多调试信息。

步骤二:找到release模式构建的文件夹。

 

步骤三:修改可执行程序图标。先把图标加到工程所在文件夹。然后在pro文件里面添加

 RC_ICONS=serial_iocn.ico

注意:图标的格式必须为.ico这个格式的,其他格式不行。

 

步骤四:封包操作。需要用到QT的控制台,点击电脑左下角,在搜索栏搜索qt,即可看到 qt控制台,双击即可打开,如下图  

我们需要电脑桌面上创建一个新的文件夹,注意千万不要有中文路径。然后把exe文件拷贝到我们新创建的文件夹里面,在控制台进入可执行文件所在的目录,如

步骤五:使用windeployqt工具把库加到我们新创建的这个文件夹里面。

格式:windeployqt exe 文件的名称,如

打包成功后,进入执行文件路径,双击程序就可以打开,如下

83.9 Qt网络编程

本章内容对应视频讲解链接(在线观看):

QT网络编程之TCP通信  https://www.bilibili.com/video/BV1tp4y1i7EJ?p=13

QT网络编程之UDP通信  https://www.bilibili.com/video/BV1tp4y1i7EJ?p=14

网络编程有TCP和UDP,TCP编程需要用到两个类:QTcpServer和QTcpSocket。

区别

TCP 

UDP

是否连接

面向连接

面向非连接

传输可靠性 

可靠

不可靠

应用场合

少量数据,如传输文件

传输大量数据,如传输视频语音

83.9.1 TCP实现服务器和客户端

TCP协议(Transmission Control Protocol)是一种面向连接的,可靠的,基于字节流的传输层通信协议,传输数据稳定可靠。

在 help索引中搜索到如两个重要类:


 

 

服务器编程中两个类都会用到,客户端编程中只会用到QTcpSocket对象。

本实验中对QTcpServer类的基本使用:

(1)监听客户端连接。

(2)每当有新的客户端连接服务器的时候,会自动触发信号,

(3)根据当前连接上的新的客户端创建一个Socket对象,将数据的收发动作交给socket套 接字去处理。

(4)关闭服务器close();

对QTcpSocket类的基本使用:

(1)服务器端:有新连接时获取连接状态,绑定socket 。

(2)客户端:通过socket连接服务器,连接成功触发信号。

(3)当有数据到来时会触发信号,用readAll()读取。

(4)通过读写socket收发数据。

具体步骤:

步骤一:创建工程,在工程文件.pro中添加network,如

步骤二:设计ui界面,

  1. 在属性编辑栏设置主窗口大小:
  1. 添加组件

     接收窗口: Plain Text Edit

   发送窗口,IP地址窗口,端口号窗口:Line Edit

   打开服务器,关闭服务器:Push Button

拖拽完成后逐个布局,根据需要设置组件大小,这里端口号框设置成了最小200

 按钮布局:拖拽按钮和弹簧,然后点击水平布局。

然后选中全部组件,点击栅格布局:

最后更改组件名称注释,完成后如

步骤三:服务器端编程:

1.创建QTcpServer对象

2.创建监听端口,使得客户端可以使用这个端口访问服务器,使用listen函数。

bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0);

第一个参数是绑定指定的地址(即本机服务器IP),第二个参数是绑定的本服务器端口号

监听某个端口时,如果有新连接进来就发出newConnection()信号

3.当服务器对象被访问时,会发出newConnection()信号,所以为该信号添加槽函数并用一个QTcpSocket对象接受客户端的访问。

4.当socket接收缓冲区有新数据到来时,会发出readyRead()信号,为该信号添加槽函数,使用readyRead()读取。

5.socket发送数据可直接调用write()成员函数。

6.关闭端口号。

代码如下:

#include <QMainWindow>
#include <QTcpServer>
#include <QTcpSocket>namespace Ui {
class TcpServer;
}class TcpServer : public QMainWindow
{Q_OBJECTpublic:explicit TcpServer(QWidget *parent = 0);~TcpServer();QTcpServer * tcpServer;QTcpSocket * tcpSocket;
public slots:void newConnection_Slot(void);void readyRead_Solt(void);
private slots:void on_openBu_clicked();void on_sendBu_clicked();void on_closeBu_clicked();private:Ui::TcpServer *ui;
};
#include "tcpserver.h"
#include "ui_tcpserver.h"
#include <QTcpServer>
#include <QTcpSocket>
#include <QString>
TcpServer::TcpServer(QWidget *parent) :QMainWindow(parent),ui(new Ui::TcpServer)
{ui->setupUi(this);tcpServer = new QTcpServer(this);tcpSocket = new QTcpSocket(this);//连接信号与槽函数进行绑定connect(tcpServer,SIGNAL(newConnection()),SLOT(newConnection_Slot()));
}//连接信号槽函数
void TcpServer::newConnection_Slot(void)
{//连接客户端后sockettcpSocket = tcpServer->nextPendingConnection();//套接字的接收数据信号与都数据槽函数连接connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(readyRead_Solt()));
}
//读取数据
void  TcpServer::readyRead_Solt(void)
{QString buf;//读取buf = tcpSocket->readAll();ui->recvEdit->appendPlainText(buf);
}TcpServer::~TcpServer()
{delete ui;
}//打开
void TcpServer::on_openBu_clicked()
{//监听tcpServer->listen(QHostAddress::Any,ui->portEdit->text().toUInt());
}//发送数据
void TcpServer::on_sendBu_clicked()
{tcpSocket->write(ui->sendEdit->text().toLocal8Bit().data());
}//关闭
void TcpServer::on_closeBu_clicked()
{tcpSocket->close();
}

步骤四:客户端编程

1.创建QTcpSocket套接字对象

2.使用套接字对象的成员函数去请求连接服务器。

void connectToHost(const QHostAddress &address, quint16 port, openMode mode = ReadWrite);

第一个参数为服务器IP地址,第二个参数为服务器端口号。第三个参数为打开方式,默认为可读可写

函数功能:请求连接服务器连接成功后发出connected()信号,绑定槽函数connected_Solt()去操作socket。

3.使用write函数向服务器发送数据,当socket接收缓冲区有新数据到来时

会发出readyRead()信号,为该信号添加槽函数以读取数据。

4.断开与服务器的连接。

class TcpClient : public QMainWindow
{
.......
private slots:void on_openBt_clicked();void connected_Solt(void);void readyRead_Solt(void);void on_sendEdit_2_clicked();void on_closeBt_clicked();
};TcpClient::TcpClient(QWidget *parent) :QMainWindow(parent),ui(new Ui::TcpClient)
{ui->setupUi(this);//创建socket 对象tcpSocket = new QTcpSocket(this);
}TcpClient::~TcpClient()
{delete ui;
}
//打开(连接服务器)
void TcpClient::on_openBt_clicked()
{tcpSocket->connectToHost(ui->ipEdit->text(),ui->portEdit->text().toUInt());connect(tcpSocket,SIGNAL(connected()),this,SLOT(connected_Solt()));
}
//等待数据到来
void TcpClient::connected_Solt(void)
{connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(readyRead_Solt()));
}
//读取数据
void TcpClient::readyRead_Solt(void)
{ui->recvEdit->appendPlainText(tcpSocket->readAll());
}
//发送
void TcpClient::on_sendEdit_2_clicked()
{tcpSocket->write(ui->sendEdit->text().toLocal8Bit().data());
}
//关闭
void TcpClient::on_closeBt_clicked()
{tcpSocket->close();
}

编译运行成功,使用服务器和客户端通信如:

83.9.2 UDP实现服务器和客户端

UDP协议是开放式,无连接,不可靠的传输层通信协议,但它收发数据的速度相对于TCP快很多,常用在传输音视频等数据量非常大的场合。

udp网络编程只需要使用一个类QUdpSocket。

本实验中对QUdpSocket的基本使用:

1.创建QUdpSocket对象。

2.绑定端口号

3.数据到来触发readyRead()信号。

4.读取发送数据。

5.关闭。

具体步骤:

步骤一:组装ui界面,和TCP章节搭建UI界面方法一致。

步骤二:编写代码

1.创建QUdpSocket对象,使用bind函数绑定端口号和套接字,数据报到来后会发出信 号readyRead(),在绑定的槽函数内去读取数据。

2.读取数据,数据到来hasPendingDatagrams()返回true,再用pendingDatagramSize()获取数据报的长度,如果数据没有被读取

完,hasPendingDatagrams()就会返回true,直至数据都被读取完。

readDatagram(data,size);

参数data为读取的数据,size为数据长度。

3.发送数据,使用writeDatagram函数,

writeDatagram(const char *data, qint64 len, const QHostAddress &host, quint16 port);

Data:发送的数据。

Len:发送的数据长度。

Host:目标IP地址。

Port:目标端口号。

4.关闭socket套接字。

代码如下:

udp.h 

#include <QMainWindow>
#include <QUdpSocket>namespace Ui {
class Udp;
}class Udp : public QMainWindow
{Q_OBJECTpublic:explicit Udp(QWidget *parent = 0);~Udp();QUdpSocket * udpSocket;
private slots:void on_pushButton_clicked();void readyRead_Slot(void);void on_pushButton_3_clicked();void on_pushButton_2_clicked();private:Ui::Udp *ui;
};

udp.cpp:

Udp::Udp(QWidget *parent) :QMainWindow(parent),ui(new Ui::Udp)
{ui->setupUi(this);udpSocket = new QUdpSocket(this);
}Udp::~Udp()
{delete ui;
}
/** 打开按钮*/
void Udp::on_pushButton_clicked()
{//绑定本端口的端口号if(udpSocket->bind(ui->cliEdit->text().toUInt()) == true){QMessageBox::information(this,"提示","成功");}else{QMessageBox::information(this,"提示","失败");}//绑定数据信号和槽函数connect(udpSocket,SIGNAL(readyRead()),this,SLOT(readyRead_Slot()));
}
/**读取数据槽函数*/
void Udp::readyRead_Slot()
{QString buf;QByteArray array;//hasPendingDatagrams()返回true时表示至少有一个数据报在等待被读取while(udpSocket->hasPendingDatagrams()){//获取数据array.resize(udpSocket->pendingDatagramSize());udpSocket->readDatagram(array.data(),array.size());buf = array.data();ui->recvEdit->appendPlainText(buf);}
}/** 发送数据*/
void Udp::on_pushButton_3_clicked()
{quint16 port;QString sendBuff;QHostAddress address;address.setAddress(ui->ipEdit->text());//目标机地址port = ui->portEdit->text().toInt();//目标机端口号sendBuff = ui->sendEdit->text();//发送的数据//发送udpSocket->writeDatagram(sendBuff.toLocal8Bit().data(),sendBuff.length(),address,port);
}/**关闭*/
void Udp::on_pushButton_2_clicked()
{udpSocket->close();
}

步骤三:运行测试,收发功能正常如

83.10 Qt定时器

本章内容对应视频讲解链接(在线观看):

QT时间编程之QT时钟  https://www.bilibili.com/video/BV1tp4y1i7EJ?p=15

实验目标:实现计时器功能,并且点击打点按钮将当前时间打印出来。

用到的类有QTimer和QTime,QTimer是一个计时器类,相当于秒表,QTimer是一个时间类,相当于手表。

83.10.1 实验步骤

步骤一:界面布局:

拖拽组件,在属性编辑栏设置大小,然后选中按钮,点击水平布局;

 

在属性编辑栏设置Label的最小高度为50,选中全部组件,点击栅格布局,如

 

 

根据实际情况调整大小,更改对象名后如下图:

 步骤二:创建计时器类对象timer和时间类time,设置初始时间为0。

    class TimerP : public QMainWindow{Q_OBJECTpublic:explicit TimerP(QWidget *parent = 0);~TimerP();QTimer timer;
QTime time;
..........};

 步骤三:开启计时器对象,设置定时时间,时间到后会发出 timeout() 信号,绑定此信号和自定义的槽函数timeOut_Slot()。

void start(int msec);

函数功能:开启定时器,时间到后发出timeout信号,并重新计时。

参数msec含义:定时时间,单位毫秒。

    TimerP::TimerP(QWidget *parent) :QMainWindow(parent),ui(new Ui::TimerP){ui->setupUi(this);
//信号timeout与槽函数绑定connect(&timer,SIGNAL(timeout()),this,SLOT(timeOut_Slot()));time.setHMS(0,0,0,0);ui->showTime->setText("00:00:00:000");}
/**开始定时*/
void TimerP::on_starBu_clicked()
{timer.start(3);
}

步骤四:槽函数timeOut_Slot()内处理时间类对象,使每次计时时间结束后,时间对象能增加  相同的时间,实现计时功能。

QTime addMSecs(int ms) const;

参数msec含义:增加的时间值,单位毫秒。

函数功能:返回一个当前时间对象之后ms毫秒之后的时间对象。

	/** 计时*/void TimerP::timeOut_Slot(){//qDebug("timt out");time = time.addMSecs(3);ui->showTime->setText(time.toString("hh:mm:ss.zzz"));}

步骤五:打点记录功能,使用全局变量记录排名,并显示到界面。

/** 记录*/
void TimerP::on_bitBu_clicked()
{QString temp;i=i+1;temp.sprintf("%d: ",i);ui->bitTime->append(temp);ui->bitTime->append(time.toString("hh:mm:ss.zzz"));
}

83.10.2 部分代码

timerp.h:
class TimerP : public QMainWindow
{Q_OBJECTpublic:explicit TimerP(QWidget *parent = 0);~TimerP();QTimer timer;QTime time;private slots:void on_starBu_clicked();//开始计时按钮槽函数void timeOut_Slot();//定时时间到槽函数void on_closeBu_clicked();//关闭按钮槽函数void on_resetBu_clicked();//重置按钮槽函数void on_bitBu_clicked();//打点记录按钮槽函数private:Ui::TimerP *ui;
};
timerp.cpp:
#include <QTimer>
#include <QTime>static int i;
TimerP::TimerP(QWidget *parent) :QMainWindow(parent),ui(new Ui::TimerP)
{ui->setupUi(this);connect(&timer,SIGNAL(timeout()),this,SLOT(timeOut_Slot()));time.setHMS(0,0,0,0);ui->showTime->setText("00:00:00:000");
}TimerP::~TimerP()
{delete ui;
}void TimerP::on_starBu_clicked()
{timer.start(3);
}
/** 处理时间类对象*/
void TimerP::timeOut_Slot()
{//qDebug("timt out");time = time.addMSecs(3);ui->showTime->setText(time.toString("hh:mm:ss.zzz"));
}
/** 关闭*/
void TimerP::on_closeBu_clicked()
{timer.stop();i=0;
}
/** 重置*/
void TimerP::on_resetBu_clicked()
{timer.stop();time.setHMS(0,0,0,0);ui->showTime->setText("00:00:00:000");ui->bitTime->clear();i=0;
}/** 记录*/
void TimerP::on_bitBu_clicked()
{QString temp;i=i+1;temp.sprintf("%d: ",i);ui->bitTime->append(temp);ui->bitTime->append(time.toString("hh:mm:ss.zzz"));
}

运行结果:

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Windows系统电脑安装多个Tomcat服务教程
  • 2021年高教社杯国赛a题 详细代码 文章 教学 2024数学建模国赛数模备赛: “FAST”主动反射面的形状调节
  • Android 自适应屏幕技术
  • SpringBootWeb 篇-深入了解 SpringBoot + Vue 的前后端分离项目部署上线与 Nginx 配置文件结构
  • HTML简单了解和基础知识记录
  • 高可用IP地址管理:使用Keepalived和Nginx实现VIP及IP池配置
  • kaggle竞赛宝典 | 量化竞赛第一名的网络模型
  • 【系统架构设计师】论文:论软件开发平台的选择与应用
  • NPJ系列|放射组学与多组学数据整合:推进精准肿瘤学的新模式|文献速递·24-08-25
  • 虚幻5|制作一个木桩,含血量及伤害数字
  • python代码错误集合
  • Linux自旋锁和读写锁
  • Runnable
  • Flutter-->Widget上屏之路
  • git cherry-pick 合并单个提交
  • 【跃迁之路】【477天】刻意练习系列236(2018.05.28)
  • If…else
  • javascript面向对象之创建对象
  • js写一个简单的选项卡
  • LeetCode541. Reverse String II -- 按步长反转字符串
  • node 版本过低
  • SegmentFault 技术周刊 Vol.27 - Git 学习宝典:程序员走江湖必备
  • vue总结
  • 持续集成与持续部署宝典Part 2:创建持续集成流水线
  • 飞驰在Mesos的涡轮引擎上
  • 简单易用的leetcode开发测试工具(npm)
  • 猫头鹰的深夜翻译:JDK9 NotNullOrElse方法
  • 如何设计一个比特币钱包服务
  • 使用Maven插件构建SpringBoot项目,生成Docker镜像push到DockerHub上
  • 推荐一款sublime text 3 支持JSX和es201x 代码格式化的插件
  • 为视图添加丝滑的水波纹
  • ionic入门之数据绑定显示-1
  • 策略 : 一文教你成为人工智能(AI)领域专家
  • #NOIP 2014# day.1 T2 联合权值
  • #vue3 实现前端下载excel文件模板功能
  • ()、[]、{}、(())、[[]]等各种括号的使用
  • (大众金融)SQL server面试题(1)-总销售量最少的3个型号的车及其总销售量
  • (附源码)springboot 基于HTML5的个人网页的网站设计与实现 毕业设计 031623
  • (附源码)ssm户外用品商城 毕业设计 112346
  • (机器学习-深度学习快速入门)第三章机器学习-第二节:机器学习模型之线性回归
  • (论文阅读22/100)Learning a Deep Compact Image Representation for Visual Tracking
  • (每日一问)计算机网络:浏览器输入一个地址到跳出网页这个过程中发生了哪些事情?(废话少说版)
  • (转) Android中ViewStub组件使用
  • ***微信公众号支付+微信H5支付+微信扫码支付+小程序支付+APP微信支付解决方案总结...
  • .babyk勒索病毒解析:恶意更新如何威胁您的数据安全
  • .NET MAUI学习笔记——2.构建第一个程序_初级篇
  • .NET 中的轻量级线程安全
  • .NET/C#⾯试题汇总系列:⾯向对象
  • .Net+SQL Server企业应用性能优化笔记4——精确查找瓶颈
  • .NET下的多线程编程—1-线程机制概述
  • @converter 只能用mysql吗_python-MySQLConverter对象没有mysql-connector属性’...
  • @PostConstruct 注解的方法用于资源的初始化
  • []常用AT命令解释()
  • [8481302]博弈论 斯坦福game theory stanford week 1
  • [AHK V2]鼠标悬停展开窗口,鼠标离开折叠窗口