码上通QT实战15--监控页面07-打开串口连接

1、前言

在 Qt 中操作串口需要使用 QSerialPort 类,它是 Qt Serial Port 模块的一部分。以下是实现串口连接的步骤:

确保项目配置中包含 Serial Port 模块 在项目文件 (.pro) 中添加:

复制代码
QT += serialport

创建串口对象并设置参数

cpp 复制代码
QSerialPort serial;
serial.setPortName("COM3");  // Windows 格式
// serial.setPortName("/dev/ttyUSB0");  // Linux 格式
serial.setBaudRate(QSerialPort::Baud9600);
serial.setDataBits(QSerialPort::Data8);
serial.setParity(QSerialPort::NoParity);
serial.setStopBits(QSerialPort::OneStop);
serial.setFlowControl(QSerialPort::NoFlowControl);

打开串口连接

cpp 复制代码
QSerialPort serial;
serial.setPortName("COM3");  // Windows 格式
// serial.setPortName("/dev/ttyUSB0");  // Linux 格式
serial.setBaudRate(QSerialPort::Baud9600);
serial.setDataBits(QSerialPort::Data8);
serial.setParity(QSerialPort::NoParity);
serial.setStopBits(QSerialPort::OneStop);
serial.setFlowControl(QSerialPort::NoFlowControl);

枚举系统可用串口:

读写数据操作

注意事项:

  • 确保串口未被其他程序占用
  • 不同操作系统下串口命名方式不同
  • 波特率等参数需与设备端匹配
  • 读写操作建议放在单独线程中执行

2、这么干

1、创建类对象

创建一个单独的类,这是一个多线程运行时需要的类对象,因为串口的操作需要单独放在线程中运行,因此要这么干。

信号和槽是Qt框架中用于对象间通信的机制。信号在特定事件发生时被发射,槽是接收信号并做出响应的函数。这种机制松散耦合,发射信号的对象无需知道接收槽的对象信息。信号和槽可以是任意数量参数,但信号参数不能少于槽参数。槽的参数类型必须与信号对应参数类型兼容,无需完全一致。

2、编写槽函数

3、定义多线程对象

在Qt中,多线程通过QThread类实现,允许并行执行任务以提高性能或避免主线程(UI线程)阻塞。核心组件包括:

  • QThread:线程管理类,提供线程生命周期控制。
  • 信号与槽:跨线程通信机制,需注意线程安全性。
  • 线程安全对象 :如QMutexQReadWriteLock等用于同步

注意事项

  • UI操作限制:所有UI更新必须在主线程执行,子线程通过信号触发。
  • 资源释放:线程退出时确保清理资源,避免内存泄漏。
  • 事件循环moveToThread方式依赖事件循环,需确保线程未提前终止。

通过合理选择上述方法,可高效实现Qt多线程编程

4、绑定信号和槽函数

在 Qt 中,emit 是一个宏,用于触发信号(signal)。信号是 Qt 信号与槽机制的核心组成部分,用于对象间的通信。当某个事件发生时,对象通过 emit 发送信号,与之连接的槽(slot)会被自动调用。emit 的语法非常简单,只需在信号名前加上 emit 关键字即可:

cpp 复制代码
emit signalName(arguments);

emit 的作用是触发信号,但信号的接收需要通过 QObject::connect 将信号与槽连接起来,例如:

cpp 复制代码
QObject::connect(sender, &SenderClass::valueChanged, receiver, &ReceiverClass::handleValueChange);

emit 的注意事项

  • emit 是一个空宏,仅用于代码可读性。实际编译时会被替换为空。
  • 信号必须在类的 signals 部分声明,且无需实现(由 moc 生成)。
  • 只有 QObject 或其子类的对象才能使用信号与槽机制。
  • 信号可以带参数,但参数类型必须是 Qt 能够识别的类型(如基本类型、QString 等)。
cpp 复制代码
#include "qworker.h"
#include <QPushButton>
#include <QMessageBox>
#include <QMetaEnum>

QWorker::QWorker(QObject *parent) : QObject{parent}
{

    serialPort=new QSerialPort(this);//创建串口对象
}


void QWorker::openPort(QString port,QString baud,QString parity,QString data,QString stop)
{
    bool state=false;
    if(serialPort->isOpen()){//如果打开了,则关闭
        serialPort->close();
    }else{
        serialPort->setPortName(port);//1、设置串口名称
        serialPort->setBaudRate(baud.toUInt());//2、设置波特率
        parity+="Parity";//设置校验位的前缀
        QMetaEnum metaEnum=QMetaEnum::fromType<QSerialPort::Parity>();//创建枚举
        QSerialPort::Parity p= (QSerialPort::Parity)metaEnum.keyToValue(parity.toLatin1());//将校验位强转成枚举类型
        serialPort->setParity(p);//3、设置校验位

        data="Data"+data;//设置数据位的前缀
        metaEnum=QMetaEnum::fromType<QSerialPort::DataBits>();
        QSerialPort::DataBits d=(QSerialPort::DataBits)metaEnum.keysToValue(data.toLatin1());
        serialPort->setDataBits(d);//4、设置数据位

        stop+="Stop";//设置停止位的前缀
        metaEnum=QMetaEnum::fromType<QSerialPort::StopBits>();
        QSerialPort::StopBits s=(QSerialPort::StopBits)metaEnum.keysToValue(stop.toLatin1());
        serialPort->setStopBits(s);//5、设置停止位

        serialPort->setFlowControl(QSerialPort::NoFlowControl);//设置流控制
        state= serialPort->open(QIODevice::ReadWrite);//读写方式打开串口

        // showMessage();

        // MonitorView -触发一个信号,由MainWindow进行接收,然后收MainWindow发送一个信号,由QWorker进行接收
        // 最终在QWorker中进行打开串口的动作处理
        // 有可能打开成功/失败
        // 最终得将打开的状态给到MonitorView
    }
    emit openComleted(state);//触发信号
}

emit 是 Qt 信号与槽机制中的关键部分,用于触发信号。通过 QObject::connect 将信号与槽连接后,emit 可以实现对象间的松耦合通信。

绑定流程如下:

cpp 复制代码
#include "mainwindow.h"
#include "systemutils.h"
#include "ui_mainwindow.h"
#include <QMessageBox>
#include <QMouseEvent>
#include <QThread>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 1.设置窗口无边框和窗口透明
    this->setWindowFlags(Qt::FramelessWindowHint);
    this->setAttribute(Qt::WA_TranslucentBackground);


    // 设置相关阴影效果
    SystemUtils * utils=new SystemUtils();
    utils->SetDropShadowEffect(ui->wdg_root,Qt::gray,10);

    //设置关闭,最小,最大的图标
    // 设置相关字体图标
    QFont font=QFont(QString("zx_icons"),9);
    // 关闭
    ui->pb_close->setFont(font);
    ui->pb_close->setText(QChar(0xe653));
    // 最大化
    ui->pb_max->setFont(font);
    ui->pb_max->setText(QChar(0xe694));
    // 最小化
    ui->pb_min->setFont(font);
    ui->pb_min->setText(QChar(0xe7e6));
    //设置第二行消息的图标
    ui->lbl_message_icon->setFont(font);
    ui->lbl_message_icon->setText(QChar(0xe7ff));

    //设置标题字体
    QFont font2=QFont(QString("钉钉进步体"),14);
    ui->lbl_title->setFont(font2);

    //设置"实时监控"按钮的信号和槽函数
    connect(ui->nb_monitor,SIGNAL(onClicked(int)),SLOT(onNavClicked(int)));
    //设置"趋势图表"导航按钮的信号和槽函数
    connect(ui->nb_trend,SIGNAL(onClicked(int)),SLOT(onNavClicked(int)));
    //设置"异常报警"导航按钮的信号和槽函数
    connect(ui->nb_alarm,SIGNAL(onClicked(int)),SLOT(onNavClicked(int)));
    //设置"系统设置"导航按钮的信号和槽函数
    connect(ui->nb_settings,SIGNAL(onClicked(int)),SLOT(onNavClicked(int)));

    //绑定监控页面Monitor的信号和槽函数关联,即monitor页面中的信号函数onConnect由本页面的槽函数onConnect响应处理
    connect(ui->Monitor,SIGNAL(onConnect(QString,QString,QString,QString,QString)),SLOT(onConnect(QString,QString,QString,QString,QString)));

    //创建线程及对象,处理串口
    thread=new QThread();
    worker=new QWorker();
    //1、本页面的SIGNAL信号函数onOpen由worker对象的SLOT槽函数openPort响应处理
    connect(this,SIGNAL(onOpen(QString,QString,QString,QString,QString)),worker,SLOT(openPort(QString,QString,QString,QString,QString)));
    //2、worker对象中的信号函数openComleted由本页面的onSetopenState槽函数响应处理
    connect(worker,SIGNAL(openComleted(bool)),this,SLOT(onSetopenState(bool)));

    worker->moveToThread(thread);
    thread->start();//启动线程
}

MainWindow::~MainWindow()
{
    delete ui;
}
//串口连接状态
void MainWindow::onSetopenState(bool state)
{
    //接收到worker对象中的打开状态信号
    ui->Monitor->setOpenState(state);
}


//连接串口设备
void MainWindow::onConnect(QString port, QString baud, QString parity, QString data, QString stop)
{
    //执行串口对象的连接动作,动作必须在后台线程中处理
    //触发信号,让qworker这个对象来接收信号
    emit onOpen(port,baud,parity,data,stop);
}
//鼠标按下事件
void MainWindow::mousePressEvent(QMouseEvent *event){
    if (event->button() == Qt::LeftButton) {  // 如果按下左边按钮
        m_drag = true;//表示要移动
        // 获取当前光标的位置
        m_dragPos = event->pos();
        // 当前鼠标点相对于桌面屏幕左上角的坐标(0,0),全局坐标;
        m_resizeDownPos = event->globalPosition().toPoint();
        // 获取当前窗口的相关参数,包括位置,大小等等各种参数
        m_mouseDownRect = this->rect();
    }
}

//鼠标移动事件
void MainWindow::mouseMoveEvent(QMouseEvent *event){
    // 如果是鼠标在拖动时,当前窗口是全屏,不做任何处理
    if (isFullScreen()) {
        return;
    }
    if (m_move) {
        move(event->globalPosition().toPoint() - m_dragPos);
        return;
    }

    setCursor(Qt::ArrowCursor);
    if (m_drag && (event->buttons() & Qt::LeftButton)) {
        m_move = true;
        move(event->globalPosition().toPoint() - m_dragPos);
    }
}

//鼠标释放事件
void MainWindow::mouseReleaseEvent(QMouseEvent *event){
    Q_UNUSED(event)
    m_drag = false;
    if (m_move) {
        m_move = false;
    }
    setCursor(Qt::ArrowCursor);
}

//关闭事件
void MainWindow::on_pb_close_clicked()
{
    int ret=QMessageBox::question(this,"关闭系统运行","您确定要退出系统吗?","确认","取消");
    if(ret==0){
        this->close();
    }

}
//最大化
void MainWindow::on_pb_max_clicked()
{
    this->showFullScreen();  // 全屏展示
}
//最小化
void MainWindow::on_pb_min_clicked()
{
    this->showMinimized();
}

//窗体双击事件
void MainWindow::mouseDoubleClickEvent(QMouseEvent *event)
{
    if (isFullScreen()) {
        showNormal();  // 如果是全屏,就恢复到非全屏
    } else {
        showFullScreen();  // 否则就变成全屏的
    }
}


//导航按钮的点击响应事件
void MainWindow::onNavClicked(int index)
{
    ui->sw_pages->setCurrentIndex(index);
}

5、运行测试

6、小结

emit 是 Qt 信号与槽机制中的关键部分,用于触发信号。通过 QObject::connect 将信号与槽连接后,emit 可以实现对象间的松耦合通信。在Qt中,多线程通过QThread类实现,允许并行执行任务以提高性能或避免主线程(UI线程)阻塞。

复制代码
原创不易,打字不易,截图不易,撸码不易,整理不易,走过路过,不要错过,欢迎点赞,收藏,转载,复制,抄袭,留言,灌水,请动动你的金手指,祝您早日实现财务自由。
相关推荐
mjhcsp2 小时前
C++ 后缀树(Suffix Tree):原理、实现与应用全解析
java·开发语言·c++·suffix-tree·后缀树
mjhcsp2 小时前
C++ 有限状态自动机(FSM):原理、实现与应用全解析
开发语言·c++·有限状态自动机
2301_797312262 小时前
学习java37天
开发语言·python
xifangge20252 小时前
PHP 接口跨域调试完整解决方案附源码(从 0 到定位问题)
开发语言·php
go_bai2 小时前
Linux-网络基础
linux·开发语言·网络·笔记·学习方法·笔记总结
好大哥呀3 小时前
C++ IDE
开发语言·c++·ide
BHXDML3 小时前
JVM 深度理解 —— 程序的底层运行逻辑
java·开发语言·jvm
Wang's Blog3 小时前
Nodejs-HardCore: 深入解析DBF文件之二进制文件处理指南
开发语言·nodejs