简单串口编写
1.ui 界面的创建
打开Qt新建一个工程
工程命名为 **QtSerial** ,选择路径时注意路径**不可出现中文名**。 这里选择默认,点击**下一步** 这里基类选择 QWidget,修改类名为**MainWidget**,点击**下一步** 这里选择默认,点击**下一步** 编译套件随便选择,后面可以随时进行更改,点击**下一步** 这里选择默认,点击**完成**进入工程,双击MainWidget.ui编辑ui文件(这里我们通过Qt的UI界面编辑器来设计ui界面,不以纯代码的形式来进行界面的设计。)
拖拽合适的控件完善布局。
- 拖拽Tab Widget,并修改第 1 页为“串口助手”,第 2 页为“关于”
- 操作方法如下(动图展示)
完善其他控件的拖拽,完整 UI 文件可以到此处下载提取码:h7po。
最终界面展示图
界面效果展示图(Windows 平台)
2.QSerialPort模块介绍
QT的QtSerialPort模块
Qt中提供了两个C++类,分别是QSerialPort 和QSerialPortInfo。
它们功能如下:
QSerialPort :提供了操作串口的各种接口。
QSerialPortInfo :可以提供计算机中可用串口的各种信息。
QtSerialPort模块使用方法
首先,需要在pro文件中增加如下内容:
QT += serialport
然后执行qmake,如果未执行 后面添加头文件时会报错。
给项目添加新的**C++**类,
选择C++ Class
取名Serial,点击下一步即可生成对应的文件
在生成的serial.h中进行如下操作
#ifndef SERIAL_H
#define SERIAL_H
#include <QObject>
#include <QSerialPort> //添加串口类的头文件
#include <QSerialPortInfo> //添加串口信息的头文件
class Serial : public QObject
{
Q_OBJECT
public:
explicit Serial(QObject *parent = nullptr);
~Serial(void); //添加析构函数
void SerialOpen(); //添加打开串口函数
void SerialClose(); //添加关闭串口函数
private:
QSerialPort* MySerial; //添加串口类成员
signals:
void SetInfo(QString info);
void isnoSerialOpen();
};
#endif // SERIAL_H
分别将光标置于函数后面 按下快捷键 alt + enter
~Serial(void); //添加析构函数
void SerialOpen(); //添加打开串口函数
void SerialClose(); //添加关闭串口函数
出现以下图片时 回车 即可在 cpp 文件中定义函数。
动图展示。
~Serial(void); 用来delete 之后程序中 new出来的变量
void SerialOpen();和 void SerialClose();则是用来进行打开串口的操作。
首先在 cpp 文件中 对MySerial进行实例化。
然后,鼠标放在MySerial上按下快捷键F1,打开QSerialPort的帮助文档。找到Public Functions
打开Detailed Description
从帮助文档中可以看出来,我们需要对串口进行的一些配置。
配置串口参数
操作步骤如下:
1.首先需要设置要打开的串口名,这里可以通过**setPortName()或者setPort()**进行配置
2.然后通过使用open()函数以 read-only (r/o), write-only (w/o), or read-write (r/w) 模式之一打开串口
3.然后,检测串口是否被打开 (且没有其他的进程或者线程打开串口,如果有就关闭串口在重新打开)
4.最后,配置串口参数如配置串口名,波特率,数据位,校验位,停止位和流控位
配置函数如下:
void setPortName(const QString &name)
bool setBaudRate(qint32 baudRate, QSerialPort::Directions directions = AllDirections)
bool setDataBits(QSerialPort::DataBits dataBits)
bool setParity(QSerialPort::Parity parity)
bool setStopBits(QSerialPort::StopBits stopBits)
bool setFlowControl(QSerialPort::FlowControl flowControl)
首先,我们需要QString和qint32这两个类型的name参数和baudRate参数,这是通过ui界面的Qcombobox选项得到的。因为使用多线程的原因,不能直接调用,所以,这里通过构建结构体,通过传递结构体来传递参数。
鼠标右键点击工程,添加一个新的cpp头文件
设置头文件名称为 SerialInfo.h ,点击下一步,完成。
添加 SerialInfo.h的内容如下。
#ifndef SERIALINFO_H
#define SERIALINFO_H
#include <QVector>
#include <QMetaType>
typedef struct SerialInfos //串口配置信息
{
QString comName; //串口名称
qint32 baudRate; //波特率
qint32 dataBits; //数据位
qint32 parity; //校验位
qint32 stopBits; //停止位
qint32 flowControl; //流控位
qint32 Encode; //编码格式
}Sinfo;
//通过Q_DECLARE_METATYPE声明后,就可以让自定义的类型设置到QVariant。
Q_DECLARE_METATYPE(Sinfo);
#endif // SERIALINFO_H
在serial.h中添加头文件
#include "SerialInfo.h"
并添加私有成员
private:
QSerialPort* MySerial;
Sinfo *info=nullptr; //串口配置
QString InfoSet; //存储串口配置
修改后的serial.h内容如下
#ifndef SERIAL_H
#define SERIAL_H
#include <QObject>
#include <QSerialPort> //添加串口类的头文件
#include <QSerialPortInfo> //添加串口信息的头文件
#include "SerialInfo.h"
class Serial : public QObject
{
Q_OBJECT
public:
explicit Serial(QObject *parent = nullptr);
~Serial(void); //添加析构函数
void SerialOpen(); //添加打开串口函数
void SerialClose(); //添加关闭串口函数
private:
QSerialPort* MySerial; //添加串口类成员
Sinfo *info=nullptr; //串口配置
QString InfoSet; //存储串口配置
signals:
void SetInfo(QString info); //发送串口配置信号
void isnoSerialOpen(); //发送串口打开失败信号
};
#endif // SERIAL_H
接下来在SerialOpen中操作。
首先判断串口是否打开,如果已经打开就关闭。这里调用SerialClose()(具体内容见下面SerialClose部分)
this->SerialClose();
然后设置串口名
MySerial->setPortName(QString(info->comName));
然后设置串口打开模式,R/W模式,如果设置失败发送错误信息,然后返回。
if(!MySerial->open(QIODevice::ReadWrite))//用ReadWrite 的模式尝试打开串口
{
emit isnoSerialOpen(); //发送打开失败的标志
return;
}
其中isnoSerialOpen()为设置的发送打开失败信号。
然后设置波特率,波特率通过info->baudRate设置
MySerial->setBaudRate(qint32(info->baudRate));
设置数据位,这里通过switch函数设置,其中setDataBits();中的参数通过使用F1查看,具体操作步骤如下。
使用同样的方法设置检验位、停止位和流控位。
在**SerialOpen()**中实现上述操作步骤,具体代码如下:
void Serial::SerialOpen()
{
this->SerialClose();
MySerial->setPortName(QString(info->comName));
InfoSet=QString::fromLocal8Bit("串口:"); //InfoSet存储串口设置信息,发送给mainWidget
InfoSet.append(QString(info->comName));
if(!MySerial->open(QIODevice::ReadWrite))//用ReadWrite 的模式尝试打开串口
{
emit isnoSerialOpen(); //发送打开失败的标志
return;
}
//设置波特率
bool Bflag = MySerial->setBaudRate(qint32(info->baudRate));
if(Bflag){
InfoSet.append(QString::fromLocal8Bit(" 波特率:"));
//第一个参数为int变量,第二个参数10表示转换为10进制数
QString baudRateinfo = QString::number(int(info->baudRate),10);
InfoSet.append(baudRateinfo);
}
else{
InfoSet.QString::fromLocal8Bit("波特率:Unknown");
};
//设置数据位
switch (info->dataBits) {
case 0:
MySerial->setDataBits(QSerialPort::Data5);
InfoSet.append(QString::fromLocal8Bit(" 数据位:5"));
break;
case 1:
MySerial->setDataBits(QSerialPort::Data6);
InfoSet.append(QString::fromLocal8Bit(" 数据位:6"));
break;
case 2:
MySerial->setDataBits(QSerialPort::Data7);
InfoSet.append(QString::fromLocal8Bit(" 数据位:7"));
break;
case 3:
MySerial->setDataBits(QSerialPort::Data8);
InfoSet.append(QString::fromLocal8Bit(" 数据位:8"));
break;
default:
MySerial->setDataBits(QSerialPort::UnknownDataBits);
InfoSet.append(QString::fromLocal8Bit(" 数据位:Unknown"));
break;
}
//设置校验位
switch (info->parity) {
case 0:
MySerial->setParity(QSerialPort::EvenParity);
InfoSet.append(QString::fromLocal8Bit(" 校验位:Even"));
break;
case 1:
MySerial->setParity(QSerialPort::MarkParity);
InfoSet.append(QString::fromLocal8Bit(" 校验位:Mark"));
break;
case 2:
MySerial->setParity(QSerialPort::NoParity);
InfoSet.append(QString::fromLocal8Bit(" 校验位:None"));
break;
case 3:
MySerial->setParity(QSerialPort::OddParity);
InfoSet.append(QString::fromLocal8Bit(" 校验位:Odd"));
break;
case 4:
MySerial->setParity(QSerialPort::SpaceParity);
InfoSet.append(QString::fromLocal8Bit(" 校验位:Space"));
break;
default:
MySerial->setParity(QSerialPort::UnknownParity);
InfoSet.append(QString::fromLocal8Bit(" 校验位:Unknown"));
break;
}
//设置停止位
switch (info->stopBits) {
case 0:
MySerial->setStopBits(QSerialPort::OneStop);
InfoSet.append(QString::fromLocal8Bit(" 停止位:1"));
break;
case 1:
MySerial->setStopBits(QSerialPort::OneAndHalfStop);
InfoSet.append(QString::fromLocal8Bit(" 停止位:1.5"));
break;
case 2:
MySerial->setStopBits(QSerialPort::TwoStop);
InfoSet.append(QString::fromLocal8Bit(" 停止位:2"));
break;
default:
MySerial->setStopBits(QSerialPort::UnknownStopBits);
InfoSet.append(QString::fromLocal8Bit(" 停止位:Unknown"));
break;
}
//设置流控位
switch (info->flowControl) {
case 0:
MySerial->setFlowControl(QSerialPort::NoFlowControl);
InfoSet.append(QString::fromLocal8Bit(" 流控位:None"));
break;
case 1:
MySerial->setFlowControl(QSerialPort::HardwareControl);
InfoSet.append(QString::fromLocal8Bit(" 流控位:Hardware"));
break;
case 2:
MySerial->setFlowControl(QSerialPort::SoftwareControl);
InfoSet.append(QString::fromLocal8Bit(" 流控位:Software"));
break;
default:
MySerial->setFlowControl(QSerialPort::UnknownFlowControl);
InfoSet.append(QString::fromLocal8Bit(" 流控位:Unknown"));
break;
}
emit SetInfo(InfoSet);//发送串口配置信号
}
添加**SerialClose()**函数。具体内容如下:
void Serial::SerialClose()
{
if(MySerial->isOpen())//如果串口已经打开了 先给他关闭了
{
MySerial->clear();
MySerial->close();
}
}
添加接收串口配置参数函数。这个函数主要用从接收从ui界面处选择的串口参数,并保存在info中,前面提到了是通过结构体来传递参数的,因此构造函数时,要添加结构体的形参。
在serial.h中添加公共函数*void RecvSerialConfig(Sinfo data); 。
public:
void RecvSerialConfig(Sinfo *data); //接收串口配置参数函数
按下alt+enter,在serial.cpp中添加定义。
void Serial::RecvSerialConfig(Sinfo *data)
{
if(info!=nullptr) //删除原先内存空间
{
delete info;
}
this->info = new Sinfo;//防止内存泄漏,关闭时 delete info;
//接收参数设置
this->info->Encode=data->Encode;
this->info->baudRate=data->baudRate;
this->info->comName=data->comName;
this->info->dataBits=data->dataBits;
this->info->flowControl=data->flowControl;
this->info->parity=data->parity;
this->info->stopBits=data->stopBits;
}
这里要注意,申请内存空间时,结束后必须释放,不然容易导致内存泄漏。因此,需要在**~Serial(void);中添加delete info;**
**~Serial(void);**函数如下:
Serial::~Serial()
{
delete info;
}
配置发送和接收函数
配置完打开和关闭函数后,这里要配置发送和接收函数
在serial.h中添加公共函数void SendData(QByteArray data, bool hexflag); 和void RecvData();。添加信号void isRecvData(QByteArray);
public:
void SendData(QByteArray data, bool hexflag); //发送数据
void RecvData(); //接收数据
signals:
void isRecvData(QByteArray); //接收数据信号
按下alt+enter,在serial.cpp中添加定义。
其中void SendData(QByteArray data, bool hexflag); 函数中,data为ui界面传递的数据,hexflag则为是否通过hex模式发送。
isRecvData(QByteArray);则是向ui传递串口接收的数据。
具体函数内容为:
void Serial::SendData(QByteArray data, bool hexflag)
{
if(data.isEmpty())
{
return;//没有读取到数据就退出
}
if(hexflag==true)
{ //hex模式直接发送
MySerial->write(data);
}
else{ //判断编码格式在发送
data=SetCodeType(data,info->Encode); //先根据编码转换数据编码格式
MySerial->write(data);
}
}
void Serial::RecvData()
{
QByteArray info = MySerial->readAll();
if(info.isEmpty())
{
return ;//没有读取到数据就退出
}
emit isRecvData(info);
}
这里**SetCodeType();**函数为自己定义的设置数据编码格式函数。具体实现方式见 配置编码格式函数 。
配置编码格式函数
给项目添加新的C++类,右击工程,选择**ADD NEW …**。
选择C++ Class
按照如下图选择,点击下一步,完成。
修改codetype.h内容如下:
#ifndef CODETYPE_H
#define CODETYPE_H
#include <QString>
#include <QTextCodec>
//编码格式列表
typedef enum
{
ASCII = 0,
Utf8, //Utf8编码格式
Utf16, //Utf16编码格式
GBK, //GBK编码格式、兼容GBK18030、GB2312
Big5, //Big5
ShiftJIS
}CodeType;
//设置编码格式
QByteArray SetCodeType(QByteArray const &data,qint32 control);
//解析编码格式
QByteArray GetCodeType(QByteArray const &data, qint32 control);
#endif // CODETYPE_H
修改codetype.cpp内容如下:
#include "codetype.h"
//编码
QByteArray SetCodeType(const QByteArray &data, qint32 control)
{
QByteArray tmpData;
switch (control) {
case ASCII: tmpData=QTextCodec::codecForName("latin1")->fromUnicode(data);break;
case Utf8: tmpData= QTextCodec::codecForName("UTF-8")->fromUnicode(data);break;
case Utf16: tmpData= QTextCodec::codecForName("UTF-16")->fromUnicode(data);break;
case GBK: tmpData= QTextCodec::codecForName("GBK")->fromUnicode(data);break;
case Big5: tmpData= QTextCodec::codecForName("Big5")->fromUnicode(data);break;
case ShiftJIS: tmpData= QTextCodec::codecForName("Shift-JIS")->fromUnicode(data);break;
default:;break;
}
return tmpData;
}
//解码
QByteArray GetCodeType(const QByteArray &data, qint32 control)
{
QString tmpData;
switch (control) {
case ASCII: tmpData= QTextCodec::codecForName("latin1")->toUnicode(data);break;
case Utf8: tmpData= QTextCodec::codecForName("UTF-8")->toUnicode(data);break;
case Utf16: tmpData= QTextCodec::codecForName("UTF-16")->toUnicode(data);break;
case GBK: tmpData= QTextCodec::codecForName("GBK")->toUnicode(data);break;
case Big5: tmpData= QTextCodec::codecForName("Big5")->toUnicode(data);break;
case ShiftJIS: tmpData= QTextCodec::codecForName("Shift-JIS")->toUnicode(data);break;
default:;break;
}
return tmpData.toUtf8(); //设置成Unicode格式
}
这里主要用到了QTextCodec这个类,具体内容可以看Qt的帮助文档,这里只简要概括。
1.需要包含**#include
**这个头文件。 2.QTextCodec 类主要是将数据用来在非 Unicode 格式和 Unicode 之间进行转换。
至此,QtSerialPort配置完成。
3.线程类对象的添加和UI界面的参数设置
添加任务类对象和线程类对象
打开工程,选择mainwidget.h,添加我们创建的类Serial和线程类QThread。
内容如下:
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include <QWidget>
#include "serial.h" //添加自定义类serial头文件
#include <QThread> //添加线程类QThread头文件
QT_BEGIN_NAMESPACE
namespace Ui { class MainWidget; }
QT_END_NAMESPACE
class MainWidget : public QWidget
{
Q_OBJECT
public:
MainWidget(QWidget *parent = nullptr);
~MainWidget();
QThread *t1; //1.创建子线程对象
Serial *m_work; //2.创建任务类对象
private:
Ui::MainWidget *ui;
};
#endif // MAINWIDGET_H
在mainwidget.cpp中将创建的对象实例化,并将任务函数移入子线程。
内容如下:
#include "mainwidget.h"
#include "ui_mainwidget.h"
MainWidget::MainWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::MainWidget)
{
ui->setupUi(this);
//1.创建子线程对象,动态分配空间,指定父对象
t1 = new QThread(this);
//2.创建任务类对象,动态分配空间,不能指定父对象
m_work = new Serial;//防止内存泄漏,关闭窗口时 delete m_work;
//3.将任务对象移动到某个子线程中
m_work->moveToThread(t1);
}
MainWidget::~MainWidget()
{
delete ui;
delete m_work; //关闭时释放内存空间
}
注:这里线程操作参考之前的文章Qt多线程的使用记录中的方式二。
初始化ui界面参数
在开始工作前需要初始化ui界面的参数,如设置QCombobox的下拉框内容。因此需要创建一个void initUI(void);函数。打开mainwidget.h,添加我们需要创建的函数,并使用快捷键alt+enter,在mainwidget.cpp中定义void initUI(void);函数。
设置内容如下:
public:
MainWidget(QWidget *parent = nullptr);
~MainWidget();
QThread *t1; //1.创建子线程对象
Serial *m_work; //2.创建任务类对象
void initUI(); //初始化UI界面参数,如设置QCombobox的item和设置QSS样式。
其中 void initUI(); 函数内容如下。
void MainWidget::initUI()
{
QStringList baudrateList; //设置波特率列表
baudrateList<<"1200"<<"2400"<<"4800"<<"9600"<<"19200"<<"38400"
<<"57600"<<"115200";
ui->comBaudRate->addItems(baudrateList); //将设置的Item加入到列表中
ui->comBaudRate->setEditable(true); //设置可以手动输入波特率
//限定波特率手动输入时只能输入数字且范围为(0, 1000000)即最高1M
isBaudRateRange= new QIntValidator; //防止内存泄漏,关闭窗口时 delete aIntValidator;
isBaudRateRange->setRange(0, 1000000);
ui->comBaudRate->setValidator(isBaudRateRange);
ui->comBaudRate->setCurrentIndex(3); //设置默认显示第4个即9600(从0开始)
QStringList databitsList; //设置数据位列表,根据串口中配置顺序设置。
databitsList<<"5"<<"6"<<"7"<<"8";
ui->comDataBits->addItems(databitsList); //将设置的Item加入到列表中
ui->comDataBits->setCurrentIndex(3); //设置默认显示第4个即8bit(从0开始)
QStringList parityList; //设置校验位列表,根据串口中配置顺序设置。
parityList<<"Even"<<"Mark"<<"None"<<"Odd"<<"Space";
ui->comParity->addItems(parityList); //将设置的Item加入到列表中
ui->comParity->setCurrentIndex(2); //设置默认显示第3个即None(从0开始)
QStringList stopbitsList; //设置停止位列表,根据串口中配置顺序设置。
stopbitsList<<"1"<<"1.5"<<"2";
ui->comStopBits->addItems(stopbitsList); //将设置的Item加入到列表中
ui->comStopBits->setCurrentIndex(0); //设置默认显示第1个即1bit(从0开始)
QStringList encodeList; //设置编码格式列表,根据编码格式中配置顺序设置。
encodeList<<"ASCII"<<"UTF8"<<"UTF16"<<"GBK"<<"Big5"<<"ShiftJIS";
ui->comEncode->addItems(encodeList); //将设置的Item加入到列表中
ui->comEncode->setCurrentIndex(0); //设置默认显示第1个即ASCII(从0开始)
}
注:
- 其中需要在mainwidget.h中添加 #include < QStringList > ;
- 配置的item参数需要同之前switch设置的参数顺序相同。
- 要在mainwidget.h中添加 #include < QIntValidator >;并创建一个私有成员,QIntValidator isBaudRateRange;*
设置完成后在MainWidget中运行 initUI(); 函数
MainWidget::MainWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::MainWidget)
{
ui->setupUi(this);
//0.初始化ui界面;
initUI();
//1.创建子线程对象,动态分配空间,指定父对象
t1 = new QThread(this);
//2.创建任务类对象,动态分配空间,不能指定父对象
m_work = new Serial;//防止内存泄漏,关闭窗口时 delete m_work;
//3.将任务对象移动到某个子线程中
m_work->moveToThread(t1);
}
至此,QCombobox 参数初始化完成。(后期仍会在 initUI() 中进行其他配置如设置QSS样式等)。
利用QSerialPortInfo得到串口号
这里我们会发现并没有设置串口的Item参数。主要原因是,电脑再识别设备的串口号时,会任意分配可用的端口号,因此这里我们通过使用QSerialPortInfo类来得到可用端口号,并设置到串口的Item中。
首先,打开mainwidget.h,添加**#include
实现步骤:
首先检测可用的设备端口号,当存在可用的端口号时,通过遍历的方式,将可用的端口号保存在portStringList中。然后设置到串口的Item中。无可用端口号时,弹出信号框。
具体内容如下:
void MainWidget::getportInfo()
{
qint32 comCnt=0; //保存当前可用的串口数量
QStringList portStringList; //保存当前可用的串口列表
if(portStringList.length()!=0)
{
portStringList.clear();//检测portStringList内容当不为空时清除再次调用时使用
}
if(ui->comPortName->count()!=0)
{
ui->comPortName->clear();//检测comPortName的列表数量当不为空时清除再次调用时使用
}
//获取串口设备数量
comCnt = QSerialPortInfo::availablePorts().length();
if(comCnt!=0)
{
//获取串口信息
foreach (const QSerialPortInfo &qspinfo, QSerialPortInfo::availablePorts())
{
portStringList+=qspinfo.portName();
}
ui->comPortName->addItems(portStringList);
}
else{
message("未检测到串口!");
}
}
注:
- 其中message();为自己重新封装的信号框函数,具体内容见下。
打开mainwidget.h,添加 #include < QMessageBox > , 然后重新封装函数 void message(const char str); ,并使用快捷键alt+enter,在*mainwidget.cpp 中定义 void message(const char *str);函数。
具体内容如下:
void MainWidget::message(const char *str)
{
QMessageBox msgBox;
msgBox.setText(QString::fromLocal8Bit(str));
msgBox.exec();
}
然后在MainWidget中运行 getportInfo(); 函数
这里要注意,在拔插串口设备时,可用的端口号会产生变化,但 getportInfo(); 函数仅在打开程序时运行一次,因此,这里需要增加更新端口号的方法。
实现方法:
- 添加检测拔插设备的函数,当设备变化时重新运行getportInfo();
- 使用定时器,设定每100ms检测一次,当设备变化时重新运行getportInfo();
- 增加按钮,当点击按钮时重新运行getportInfo();
这里选择增加按钮的方法,主要原因是简单且不占用资源。
增加按钮的信号与槽,当点击按钮时,重新运行getportInfo();
具体内容如下:
MainWidget::MainWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::MainWidget)
{
ui->setupUi(this);
//0.初始化ui界面;配置qss样式
initUI();
//1.创建子线程对象,动态分配空间,指定父对象
t1 = new QThread(this);
//2.创建任务类对象,动态分配空间,不能指定父对象
m_work = new Serial;//防止内存泄漏,关闭窗口时 delete m_work;
//3.将任务对象移动到某个子线程中
m_work->moveToThread(t1);
//4.获取串口设备信息
getportInfo();
//添加更新按钮信号与槽,用于更新串口设备信息
connect(ui->upBtn,&QPushButton::clicked,this,&MainWidget::getportInfo);
}
至此,利用QSerialPortInfo得到串口号完成。
4.配置UI界面的串口打开和关闭
设置串口参数配置数组
从之前的Serial配置可知,我们是通过配置ui界面的QCombobox来进行串口参数的配置的,因此想要配置的参数应用到Serial,需要设置结构体,通过信号与槽来传递结构体的值,来传递配置参数。
这里需要在mainwidget.h中添加**#include “SerialInfo.h”头文件,并添加一个私有成员info**。
private:
Ui::MainWidget *ui;
QIntValidator* isBaudRateRange;//设置波特率输入范围
Sinfo *info=nullptr; //设置串口配置的参数
注:
- Sinfo *info可以见之前配置的内容。
在mainwidget.h中创建**void getComboBoxInfo();**用以获取配置参数。
具体内容如下:
void MainWidget::getComboBoxInfo()
{
if(info!=nullptr) //删除原先内存空间
{
delete info;
}
this->info = new Sinfo;//防止内存泄漏,关闭窗口时 delete info;
this->info->comName=ui->comPortName->currentText(); //设置串口号
this->info->baudRate=ui->comBaudRate->currentText().toInt(); //设置波特率
this->info->dataBits=ui->comDataBits->currentIndex(); //设置数据位
this->info->parity=ui->comParity->currentIndex(); //设置检验位
this->info->stopBits=ui->comStopBits->currentIndex(); //设置停止位
this->info->flowControl=0 ; //设置流控位,默认值为0无流控
this->info->Encode=ui->comEncode->currentIndex(); //设置编码格式
}
注:
- 其中,info在退出时要delete。
设置完参数时,记得放进MainWidget中执行。但MainWidget中之在打开程序时执行一次,因此,需要添加信号与槽来更新info的参数。由于使用的是QCombobox,查阅帮助文档可知,能用到的信号函数为**currentIndexChanged();**。
具体内容:
从截图中我们可以看出,当QCombobox的index变化时,可以返回两种参数:一种是当前所选变化的index值,另一种是当前所选变化的text的值。
因此可以在mainwidget.h中创建**void updataComboBox();**用以更新配置参数。
具体内容如下:
void MainWidget::updataComboBox()
{
connect(ui->comPortName, QOverload<const QString &>::of(&QComboBox::currentIndexChanged),
this,[=](const QString &text){
/* ... */
this->info->comName=text; //设置串口号
});
connect(ui->comBaudRate, QOverload<const QString &>::of(&QComboBox::currentIndexChanged),
this,[=](const QString &text){
/* ... */
this->info->baudRate=text.toInt(); //设置串口波特率
});
connect(ui->comDataBits, QOverload<int>::of(&QComboBox::currentIndexChanged),
this,[=](int index){
/* ... */
this->info->dataBits=index; //设置串口数据位
});
connect(ui->comParity, QOverload<int>::of(&QComboBox::currentIndexChanged),
this,[=](int index){
/* ... */
this->info->parity=index; //设置串口校验位
});
connect(ui->comStopBits, QOverload<int>::of(&QComboBox::currentIndexChanged),
this,[=](int index){
/* ... */
this->info->stopBits=index; //设置串口停止位
});
connect(ui->comEncode, QOverload<int>::of(&QComboBox::currentIndexChanged),
this,[=](int index){
/* ... */
this->info->Encode=index; //设置串口编码格式
});
/**预留流控控制**/
// connect(ui->comFlowControl, QOverload<int>::of(&QComboBox::currentIndexChanged),
// this,[=](int index){
// /* ... */
// this->info->flowControl=index; //设置串口流控位
// });
}
至此,设置串口参数配置数组完成。
设置打开串口和关闭串口按钮
通过设置的ui界面可知,我们仅设置了一个按钮,因此想通过一个按钮去触发打开和关闭串口时就需要添加别的条件,这里可以使用两种解决方法。
- 设置按钮的文字,通过判断文字来判断当前所处的状态。(**setText()**函数)
- 设置按钮的状态,通过判断按钮状态来判断当前所处的状态。(**setChecked()**函数)
这里我们使用**setText()**的方法。
在mainwidget.h中创建**void SerialOpen();和void SerialClose();**用以打开和关闭串口。同时创建发送配置,打开和关闭串口信号。
内容如下:
public:
void SerialOpen(); //打开串口函数
void SerialClose(); //关闭串口函数
signals:
void SendSerialConfig(Sinfo *info); //发送串口配置信号
void isCloseSerial(); //发送关闭串口信号
void isOpenSerial(); //发送打开串口信号
根据上述步骤,修改**SerialOpen();和SerialClose()**内容如下:
//打开串口设备
void MainWidget::SerialOpen(void)
{
//设置串口打开按钮文本样式
if(ui->OpenComBtn->text() == QString::fromLocal8Bit("打开串口")){
ui->OpenComBtn->setText(QString::fromLocal8Bit("关闭串口"));
emit SendSerialConfig(info); //发送串口配置信号
emit isOpenSerial();
}
else{
ui->OpenComBtn->setText(QString::fromLocal8Bit("打开串口"));
this->SerialClose(); //执行关闭串口设备
}
}
//关闭串口设备
void MainWidget::SerialClose(void)
{
emit isCloseSerial();
}
设置完SerialOpen();和SerialClose()后,记得放进MainWidget中执行。
具体内容如下:
MainWidget::MainWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::MainWidget)
{
ui->setupUi(this);
//0.初始化ui界面;配置qss样式
initUI();
//1.创建子线程对象,动态分配空间,指定父对象
t1 = new QThread(this);
//2.创建任务类对象,动态分配空间,不能指定父对象
m_work = new Serial;//防止内存泄漏,关闭窗口时 delete m_work;
//3.将任务对象移动到某个子线程中
m_work->moveToThread(t1);
//4.获取串口设备信息
getportInfo();
//添加更新按钮信号与槽,用于更新串口设备信息
connect(ui->upBtn,&QPushButton::clicked,this,&MainWidget::getportInfo);
//5.获取串口参数配置数组
getComboBoxInfo();
//添加串口参数数组的信号与槽,用于更新串口参数配置数组
updataComboBox();
//6.打开线程并运行m_work的SerialOpen()和SerialClose()
connect(ui->OpenComBtn,&QPushButton::clicked,this,[=]{
this->SerialOpen();
t1->start();
});
qRegisterMetaType<Sinfo>("Sinfo");
//添加传递串口参数配置的信号与槽,用于传递串口参数配置
connect(this,&MainWidget::SendSerialConfig,m_work,&Serial::RecvSerialConfig);
//添加打开串口的信号与槽,用于打开串口
connect(this,&MainWidget::isOpenSerial,m_work,&Serial::SerialOpen);
//添加串口打开失败标志
connect(m_work,&Serial::isnoSerialOpen,this,[=](){
message("串口不存在,或被占用!");
ui->OpenComBtn->setText(QString::fromLocal8Bit("打开串口"));
});
//添加关闭串口的信号与槽,用于释放串口
connect(this,&MainWidget::isCloseSerial,m_work,&Serial::SerialClose);
}
注:
- 传递串口参数结构体时,需要先声明。
设置打开串口后,更新串口信息在ui界面上。
添加相应的信号与槽,具体内容如下
//7.从m_work处传递回来的Serialinfo显示在ui界面上
connect(m_work,&Serial::SetInfo,this,[=](QString info){
ui->Info->setText(info);
});
设置成功后,添加当串口关闭时,清除INFO的内容。因此需要在**SerialClose()**中添加以下内容。
//关闭串口设备
void MainWidget::SerialClose(void)
{
ui->Info->clear(); //清空显示的配置参数
emit isCloseSerial(); //发送关闭串口信号
}
至此,设置打开串口和关闭串口按钮完成。