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:线程管理类,提供线程生命周期控制。
- 信号与槽:跨线程通信机制,需注意线程安全性。
- 线程安全对象 :如
QMutex、QReadWriteLock等用于同步
注意事项
- 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线程)阻塞。
原创不易,打字不易,截图不易,撸码不易,整理不易,走过路过,不要错过,欢迎点赞,收藏,转载,复制,抄袭,留言,灌水,请动动你的金手指,祝您早日实现财务自由。
