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 来写一个串口助手。

【参考】

QT中ui界面的控件QComboBox实现鼠标点击事件

QT5串口编程——编写简单的上位机


GitHub 地址:     QWidgetPro选择子工程 QSerialAssistant .pro

QT 其它文章请点击这里:     QT QUICK QML 学习笔记

  • 31
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 22
    评论
Qt串口通信是一种可以在Qt平台上进行串口通信的解决方案。其中,Qt串口通信模块qserialport是实现串口通信的关键模块。本文将为大家介绍Qt串口通信模块qserialport的开发完整实例。 首先,我们需要在Qt中打开串口通信模块qserialport。在Qt 5.2及以上版本中,qserialport已经被包含在QtCore模块中。因此,我们只需要在代码中引入QtCore模块即可使用qserialport。 接下来,我们需要定义串口参数。我们可以利用QSerialPort类中提供的函数setBaudRate()、setDataBits()、setParity()等来设置参数。例如: ```cpp QSerialPort serial; serial.setBaudRate(QSerialPort::Baud9600); serial.setDataBits(QSerialPort::Data8); serial.setParity(QSerialPort::NoParity); serial.setStopBits(QSerialPort::OneStop); serial.setFlowControl(QSerialPort::NoFlowControl); ``` 然后,我们需要打开串口。我们可以使用QSerialPort类的open()函数来打开串口。例如: ```cpp serial.setPortName("COM1"); // 设置串口号为COM1 if (serial.open(QIODevice::ReadWrite)) { qDebug() << "串口已打开"; } else { qDebug() << "串口打开失败"; } ``` 接下来,我们可以通过QSerialPort类的函数write()和read()来进行数据读写。例如: ```cpp // 向串口写入数据 char buf[] = "hello world"; serial.write(buf, sizeof(buf)); // 从串口读取数据 QByteArray data = serial.readAll(); qDebug() << data; ``` 最后,在程序结束时我们需要关闭串口。我们可以使用QSerialPort类的close()函数来关闭串口。例如: ```cpp serial.close(); ``` 综上所述,以上就是Qt串口通信模块qserialport的开发完整实例。在实际应用中,我们可以根据需要对以上代码进行修改,从而实现更加复杂的串口通信功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值