码上通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线程)阻塞。

复制代码
原创不易,打字不易,截图不易,撸码不易,整理不易,走过路过,不要错过,欢迎点赞,收藏,转载,复制,抄袭,留言,灌水,请动动你的金手指,祝您早日实现财务自由。
相关推荐
用户805533698031 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner1 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
小bo波6 天前
使用Thread子类创建线程 VS 使用Runnable接口创建线程的区别
java·多线程·thread·并发编程·runnable
Quz6 天前
QML Hello World 入门示例
qt
xcyxiner9 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner10 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner10 天前
DicomViewer (添加模型类)3
qt
xcyxiner11 天前
DicomViewer (目录调整) 2
qt
xcyxiner11 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00613 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言