Qt Widget 之简易串口助手(QSerialPort)
Posted 火山上的企鹅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Qt Widget 之简易串口助手(QSerialPort)相关的知识,希望对你有一定的参考价值。
文章目录
GitHub 地址: QWidgetPro ,选择子工程 QSerialAssistant .pro(目前只有这一个)
QT 其它文章请点击这里: QT QUICK QML 学习笔记
一、演示
Qt 作上位机,与硬件连接中,串口是最最最常用的功能。 本文写了一个很容易入手的简单串口程序,演示如下:
二、前端 UI
添加一个新的 Qt Widgets 工程
1. UI 布局
布局在设计中拖拽生成:
2. 控件改名
如下修改控件名:
3. 建立槽函数
创建如下控件的槽函数,点击 UI 自动生成的
private slots:
void on_pushButton_operate_clicked();
void on_pushButton_send_clicked();
void on_pushButton_clearRcv_clicked();
void on_pushButton_clearSend_clicked();
其它如下拉框中的选项、默认值都在程序中设定。
三、后端实现思路
1. 添加 QSerialPort 模块
在.pro 文件中增加 QT += serialport
然后在 mainwindow.h 中添加:
#include <QSerialPort> //提供访问串口的功能
#include <QSerialPortInfo> //提供系统中存在的串口的信息
注意,在 Qt4 的时候有第三方模块 QextSerialPort,到了Qt5.1 官方提供了 QSerialPort 模块。
2. 初始化设置
实例化对象、建立信号槽、初始化控件的内容等
void MainWindow::initConfig() {
//创建对象,并建立信号槽
serial = new QSerialPort(this);
//读函数的信号槽,详情见后文
QObject::connect(serial, &QSerialPort::readyRead, this, &MainWindow::serial_readyRead);
//端口号下拉框升级后的点击事件,见后文
QObject::connect(ui->comboBox_port, SIGNAL(clicked()), this, SLOT(updataPortNum()));
//添加端口号
updataPortNum();
ui->comboBox_port->setCurrentIndex(0);
//添加波特率
QStringList baudList;
baudList << "115200" << "57600" << "9600" ;
ui->comboBox_baud->addItems(baudList);
ui->comboBox_baud->setCurrentText("115200");
//添加停止位、添加数据位、添加校验位
...
//设置默认文本
...
}
3. 串口设置
在单击 “OPEN” 的时候触发,主要为检查串口是否打开、是否被占用、串口配置、设置串口状态, 打开串口,核心代码如下:
void MainWindow::on_pushButton_operate_clicked()
{
if (ui->pushButton_operate->text() == QString("OPEN")) {
//检查串口是否被占用
const QString portnameStr = ui->comboBox_port->currentText();
QSerialPortInfo info(portnameStr);
if(info.isBusy()){
qDebug()<< "The serial port is occupied" <<portnameStr;
return;
}
...
///串口配置
//清空缓冲区
serial->flush();
//设置端口号
serial->setPortName(portnameStr);
//设置波特率
serial->setBaudRate( static_cast<QSerialPort::BaudRate> (ui->comboBox_baud->currentText().toInt()) );
//设置停止位、设置数据位、设置校验、设置流控
...
isSerialOpen = serial->open(QIODevice::ReadWrite);
...
}
}
4. 数据发送
点击发送按钮,发送数据
void MainWindow::on_pushButton_send_clicked()
{
//简单文本框用 toPlainText() 取文本框的内容 toUtf8 是转换成utf8格式的字节流
QByteArray data = ui->textEdit_send->toPlainText().toUtf8();
serial->write(data);
}
5. 数据接收
前文在初始化中已经建立了信号槽:
QObject::connect(serial, &QSerialPort::readyRead, this, &MainWindow::serial_readyRead);
在设置了串口,并打开了串口后,每次串口收到数据后都会发出这个 QSerialPort::readyRead 信号。我们的程序中需要定义一个 slot,并将其与这个 signal 相连接。这样,每次新数据到来后,我们就可以在 slot 中读取数据了。这时一定要将串口缓冲区中的数据全部读出来,可以利用 readAll() 来实现,如下:
void MainWindow::serial_readyRead()
{
//从界面中读取以前收到的数据
QString recv = ui->textEdit_rcv->toPlainText();
//从接收缓冲区中读取数据
QByteArray buffer = serial->readAll();
//界面中读取的数据中加入刚读取的数据
recv += QString(buffer);
//清除显示
ui->textEdit_rcv->clear();
//更新显示
ui->textEdit_rcv->append(recv);
}
6. 自动获取硬件上串口
● 获取串口号的核心程序
//清除串口号
ui->comboBox_port->clear();
//遍历 QSerialPortInfo, 添加到串口下拉框中
foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()){
ui->comboBox_port->addItem(info.portName());
}
想每次点击串口号下拉框的时候,也就是下面这个,能实时更新串口号。
可是 QComboBox 控件竟然没有点击事件,可恶!
于是乎找到了这篇文章,QT中ui界面的控件QComboBox实现鼠标点击事件
● 提升 QComboBox
具体细节可以点进去看,核心思路如下:
自定义一个类 MyComboBox 继承 QComboBox 类。在 MyComboBox 类中添加了 mousePressEvent 鼠标点击事件
void MyComboBox::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton){
emit clicked(); //触发clicked信号
}
//将该事件传给父类处理,这句话很重要,如果没有,父类无法处理本来的点击事件
QComboBox::mousePressEvent(event);
}
然后在 ui 界面将 ComboBox 提升为自创建的 MyComboBox。具体操作为:
打开ui界面----->>选中QComboBox控件,右击----->>选择“提升为”----->>在“提升的类名称里面”填入新建的类“MyComboBox”名称----->>点击“添加”按钮----->>再点击“提升”按钮。 具体参考上面的链接文章。
● 建立信号槽
//连接信号槽,前文已经初始化
QObject::connect(ui->comboBox_port, SIGNAL(clicked()), this, SLOT(updataPortNum()));
//槽函数中,更新串口号
void MainWindow:: updataPortNum(void) {
//清除串口号
ui->comboBox_port->clear();
//遍历 QSerialPortInfo, 添加到串口下拉框中
foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()){
ui->comboBox_port->addItem(info.portName());
}
}
四、部分代码
mainwindow.cpp 中:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, isSerialOpen(false)
{
ui->setupUi(this);
//初始化配置
initConfig();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::initConfig() {
//创建对象,并建立信号槽
serial = new QSerialPort(this);
//读函数的信号槽, 具体见博客
QObject::connect(serial, &QSerialPort::readyRead, this, &MainWindow::serial_readyRead);
//端口号下拉框升级后的点击事件
QObject::connect(ui->comboBox_port, SIGNAL(clicked()), this, SLOT(updataPortNum()));
//添加端口号
updataPortNum();
ui->comboBox_port->setCurrentIndex(0);
//添加波特率
QStringList baudList;
baudList << "115200" << "57600" << "9600" ;
ui->comboBox_baud->addItems(baudList);
ui->comboBox_baud->setCurrentText("115200");
//添加停止位
QStringList stopBitsList;
stopBitsList << "1" << "1.5" << "2";
ui->comboBox_stop->addItems(stopBitsList);
ui->comboBox_stop->setCurrentText("1");
//添加数据位
QStringList dataBitsList;
dataBitsList << "8" << "7" << "6";
ui->comboBox_data->addItems(dataBitsList);
ui->comboBox_data->setCurrentText("8");
//添加校验位
QStringList checkList;
checkList << "NO" << "EVEN"<< "ODD" ;
ui->comboBox_check->addItems(checkList);
ui->comboBox_check->setCurrentText("NO");
ui->pushButton_operate->setText("OPEN");
ui->textEdit_send->setText("123456789\\r\\n");
}
//--槽函数,点击端口下拉框的时候更新
void MainWindow:: updataPortNum(void) {
//清除串口号
ui->comboBox_port->clear();
//遍历 QSerialPortInfo, 添加到串口下拉框中
foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()){
ui->comboBox_port->addItem(info.portName());
}
}
//槽函数:读串口数据并更新
void MainWindow::serial_readyRead()
{
//从界面中读取以前收到的数据
QString recv = ui->textEdit_rcv->toPlainText();
//从接收缓冲区中读取数据
QByteArray buffer = serial->readAll();
//界面中读取的数据中加入刚读取的数据
recv += QString(buffer);
//清除显示
ui->textEdit_rcv->clear();
//更新显示
ui->textEdit_rcv->append(recv);
}
void MainWindow::configSetEnable(bool b)
{
ui->comboBox_port->setEnabled(b);
ui->comboBox_baud->setEnabled(b);
ui->comboBox_stop->setEnabled(b);
ui->comboBox_data->setEnabled(b);
ui->comboBox_check->setEnabled(b);
//
ui->pushButton_send->setEnabled(!b);
}
void MainWindow::on_pushButton_operate_clicked()
{
if (ui->pushButton_operate->text() == QString("OPEN")) {
const QString portnameStr = ui->comboBox_port->currentText();
QSerialPortInfo info(portnameStr);
if(info.isBusy()){
qDebug()<< "The serial port is occupied" <<portnameStr;
return;
}
ui->pushButton_operate->setText("CLOSE");
//清空缓冲区
serial->flush();
//设置端口号
serial->setPortName(portnameStr);
//设置波特率
serial->setBaudRate( static_cast<QSerialPort::BaudRate> (ui->comboBox_baud->currentText().toInt()) );
//设置停止位
serial->setStopBits( static_cast<QSerialPort::StopBits> (ui->comboBox_stop->currentText().toInt()));
//设置数据位
serial->setDataBits( static_cast<QSerialPort::DataBits> (ui->comboBox_data->currentText().toInt()) );
//设置校验
serial->setParity ( static_cast<QSerialPort::Parity> (ui->comboBox_check->currentIndex()));
//设置流控
serial->setFlowControl(QSerialPort::NoFlowControl);
isSerialOpen = serial->open(QIODevice::ReadWrite);
if (!isSerialOpen) {
qDebug()<< QString("Failed to open serial port:") << portnameStr << serial->errorString();
serial->clearError();
configSetEnable(true);
}
else {
qDebug()<< QString("The serial port is open: ") <<portnameStr;
configSetEnable(false);
}
}
else {
ui->pushButton_operate->setText("OPEN");
serial->close();
configSetEnable(true);
}
}
//
void MainWindow::on_pushButton_send_clicked()
{
//简单文本框用 toPlainText() 取文本框的内容 toUtf8 是转换成utf8格式的字节流
QByteArray data = ui->textEdit_send->toPlainText().toUtf8();
serial->write(data);
}
void MainWindow::on_pushButton_clearRcv_clicked()
{
ui->textEdit_rcv->clear();
}
void MainWindow::on_pushButton_clearSend_clicked()
{
ui->textEdit_send->clear();
}
mainwindow.h :
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QSerialPort>
#include <QSerialPortInfo>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void configSetEnable(bool b);
void initConfig();
private slots:
void serial_readyRead();
//updata port number
void updataPortNum();
void on_pushButton_operate_clicked();
void on_pushButton_send_clicked();
void on_pushButton_clearRcv_clicked();
void on_pushButton_clearSend_clicked();
private:
Ui::MainWindow *ui;
QSerialPort *serial;
bool isSerialOpen;
};
#endif // MAINWINDOW_H
myComboBox.cpp 和 myComboBox.h 省略
此版本仅是最简单的串口程序,相比正常的串口助手,缺少 hex 发送、时间戳、定时发送、文件保存等等。仅供学习 Qt Weiget 的串口使用,后续有时间再慢慢完善,也会用 QT Quick 来写一个串口助手。
【参考】
GitHub 地址: QWidgetPro ,选择子工程 QSerialAssistant .pro
QT 其它文章请点击这里: QT QUICK QML 学习笔记
以上是关于Qt Widget 之简易串口助手(QSerialPort)的主要内容,如果未能解决你的问题,请参考以下文章
49.Qt-网络编程之QTCPSocket和QTCPServer(实现简易网络调试助手)