QT5实现串口收发数据(上位机与下位机通信)

最近帮老师做一个应用程序,通过上位机与下位机进行串口通信,最后实现实时绘图,通过几天努力,成功实现蓝牙串口通信。
参考博客1

注意:代码中一些与串口无关代码,可以忽略掉

一、QT5串口基础知识

1. pro文件与cpp文件

QT5自带QSerialPort这个类的,需要在pro文件中添加(不添加会报错):

QT       += serialport

其他CPP需要引用该类

#include            //提供访问串口的功能 
#include     //提供系统中存在的串口的信息

2. 基本串口设置

//创建串口对象
QSerialPort serial;
//设置串口名称
serial.setPortName("COM3");
//设置波特率
serial.setBaudRate(QSerialPort::Baud9600);
//设置数据位
serial.setDataBits(QSerialPort::Data8);
//设置是否奇偶校验
serial.setParity(QSerialPort::NoParity); 
//设置停止位
serial.setStopBits(QSerialPort::OneStop);
//设置是否流控制
serial.setFlowControl(QSerialPort::NoFlowControl);
//打开串口—可读可写地打开
serial.open(QIODevice::ReadWrite);
//关闭串口
serial->close();

如果对一些类或者成员不懂可以鼠标移上去按F1查看手册,按两次直接全屏。

3. 收发数据

读取数据:

QByteArray buffer = serial.readAll();//读取数据到缓冲数组中

发送数据:

serial->write(data);//将数据发送到串口

二、QT5串口实例

1. h文件

#ifndef MAINWINDOW_H
#define MAINWINDOW_H


#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
//#include 

#include "showwidget.h"
class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();
    void createActions();    //创建动作
    void createMenus();      //创建菜单
    void createToolBars();   //创建工具栏
    void createStatusBars();  //创建状态栏
    void loadFile(QString filename);   //??
    void mergeFormat(QTextCharFormat);  //??
    void setvariables();  //设置调试参数
    void startDeviceDiscovery();
    void deviceDiscovered();
    void bluetooth();
    void statusOfAll();
private:
    //基本布局
    QGraphicsScene *scene;
    QStackedWidget *Stack1;
    QMenu *fileMenu;
    QMenu *zoomMenu;
    QImage img;
    QString filename;
    ShowWidget *showWidget;
    //文件菜单栏
    QAction *OpenFileAction;
    QAction *NewFileAction;
    QAction *ExitAction;
    //编辑菜单栏
    QAction *CopyAction;
    QAction *CutAction;
    QAction *PasteAction;
    QAction *AboutAction;
    QAction *ZoomInAction;
    QAction *ZoomOutAction;
    QAction *UndoAction;
    QAction *RedoAction;
    //工具栏
    QToolBar *Tool;
    QToolBar *DoToolBar;
    //状态栏
    QStatusBar *Status;

    //停靠窗口1,显示蓝牙连接状态
    QLabel *BlueToothLabel;
    QComboBox *BlueToothPortComboBox;
    QPushButton *ConnectBtn;
    QPushButton *BreakBtn;
    QPushButton *Stop1Btn;
    QSerialPort *CurrentPort;
    QLabel *BaudRateLabel;
    QComboBox *BaudRateComBox;
    QLabel *DateRateLabel;
    QComboBox *DateRateComBox;
    QLabel *ParityLabel;
    QComboBox *ParityComBox;
    QLabel *StopBitsLabel;
    QComboBox *StopBitsComBox;


    //停靠窗口2,各种参数调整模块
    QLabel *InitELabel;
    QLabel *FinalELabel;
    QLabel *ScanRateLabel;
    QLabel *ScanningDirectionLabel;
    QLabel *GainLabel;
    QLabel *SampleIntervalLabel;
    QLabel *QuietTimeLabel;
    QLineEdit *InitELineEdit;
    QLineEdit *FinalELineEdit;
    QLineEdit *ScanRateLineEdit;
    QComboBox *ScanningDirectionComboBox;
    QLineEdit *GainLineEdit;
    QLineEdit *SampleIntervalLineEdit;
    QLineEdit *QuietTimeLineEdit;
    QPushButton *SendBtn;
    QPushButton *StartBtn;
    QPushButton *PauseBtn;
    QPushButton *Stop2Btn;
    QButtonGroup *Dock2Group;
    QTextEdit *StatusOfBT;

    //停靠窗口3,3个显示窗口,状态/接受/发送
    QTextEdit *StatusOfDock3;
    QTextEdit *SendInfo;
    QTextEdit *ReceiveInfo;


protected slots:
    void showNewFile();
    void showZoomIn();
    void showZoomOut();
    void startPainting();
    void stopPainting();
private slots:
    void on_clearButton_clicked();
    void on_sendButtton_clicked();
    void on_connectButton_clicked();
    void on_breakButton_clicked();
    void Read_Data();
};

#endif // MAINWINDOW_H

2. cpp文件

#include "mainwindow.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
void MainWindow::createActions()
{
    OpenFileAction = new QAction(QIcon("Open.png"),tr("Open"),this);
    OpenFileAction->setShortcut(tr("Ctrl+O"));
    OpenFileAction->setStatusTip(tr("Open one file"));
    connect(OpenFileAction,&QAction::triggered,[=] ()
    {
          QString path = QFileDialog::getOpenFileName(
                      this,"open","../",
                      "souce(*.cpp *.h);;Text(*.txt);;All(*.*)");
    }
            );
    NewFileAction = new QAction(QIcon("Open.png"),tr("New"),this);
    NewFileAction->setShortcut(tr("Ctrl+N"));
    NewFileAction->setStatusTip(tr("Build one new file"));
    connect(NewFileAction,SIGNAL(triggered()),this,SLOT(showNewFile()));
    ExitAction = new QAction(QIcon("Open.png"),tr("Exit"),this);
    ExitAction->setShortcut(tr("Ctrl+N"));
    ExitAction->setStatusTip(tr("Exit"));
    CopyAction = new QAction(QIcon("Open.png"),tr("Copy"),this);
    CopyAction->setShortcut(tr("Ctrl+Q"));
    CopyAction->setStatusTip(tr("Exit"));
    //connect(CopyAction,SIGNAL(triggered()),showWidget->text,SLOT(copy()));
    CutAction = new QAction(QIcon("Open.png"),tr("Cut"),this);
    CutAction->setShortcut(tr("Crtl+C"));
    //connect(CutAction,SIGNAL(triggered()),showWidget->text,SLOT(cut()));
    PasteAction = new QAction(QIcon("Open.png"),tr("Paste"),this);
    PasteAction->setShortcut(tr("Ctrl+V"));
    //connect(PasteAction,SIGNAL(triggered()),showWidget->text,SLOT(paste()));
    ZoomInAction = new QAction(QIcon("Open.png"),tr("ZoomIn"),this);
    connect(ZoomInAction,SIGNAL(triggered()),this,SLOT(showZoomIn()));
    ZoomOutAction = new QAction(QIcon("Open.png"),tr("ZoomOut"),this);
    connect(ZoomOutAction,SIGNAL(triggered()),this,SLOT(showZoomOut()));//图标未设置,均采用open.png

}
void MainWindow::createMenus()
{
    fileMenu = menuBar()->addMenu(tr("File"));
    fileMenu->addAction(OpenFileAction);
    fileMenu->addAction(NewFileAction);
    fileMenu->addSeparator();
    fileMenu->addAction(ExitAction);

}
void MainWindow::createToolBars()
{
    Tool = addToolBar("Tool");
    Tool->addAction(CopyAction);
    Tool->addAction(CutAction);
    Tool->addAction(PasteAction);
    Tool->addAction(ZoomInAction);
    Tool->addAction(ZoomOutAction);
    Tool->setAllowedAreas(Qt::TopToolBarArea);

}
void MainWindow::createStatusBars()//状态栏,最下面一行,显示状态
{
    Status = statusBar();
    Status->addWidget(new QLabel("2",this));

}

void MainWindow::showNewFile()//新建文件,这里需要修改
{
    MainWindow *newFile = new MainWindow;
    newFile->show();
}
void MainWindow::showZoomIn()//下面这两个要修改,能够将实时图像放大或者缩小
{

}
void MainWindow::showZoomOut()//
{

}
void MainWindow::bluetooth()//蓝牙模块
{
    QDockWidget *dock1 = new QDockWidget(tr("DockWindow1"));
    dock1->setFeatures(QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetFloatable); //窗口可移动
    dock1->setAllowedAreas(Qt::LeftDockWidgetArea|Qt::RightDockWidgetArea);

    addDockWidget(Qt::RightDockWidgetArea,dock1);
    dock1->setStyleSheet("QPushButton{color:white;background-color:black}");//按钮样式设置
    BlueToothLabel = new QLabel(tr("Port name: "));
    ConnectBtn = new QPushButton("Connect");
    BreakBtn = new QPushButton(tr("Break"));
    Stop1Btn = new QPushButton(tr("Stop"));
    BlueToothPortComboBox = new QComboBox;

    BaudRateLabel = new QLabel(tr("BaudRate: "));
    BaudRateComBox = new QComboBox;
    BaudRateComBox->addItem(tr("11520"));
    BaudRateComBox->addItem(tr("9600"));

    DateRateLabel = new QLabel(tr("DateRate: "));
    DateRateComBox = new QComboBox;
    DateRateComBox->addItem("5");
    DateRateComBox->addItem("6");
    DateRateComBox->addItem("7");
    DateRateComBox->addItem("8");
    DateRateComBox->setCurrentIndex(3);//设置下拉选择默认为第4个
    ParityLabel = new QLabel("Parity");
    ParityComBox = new QComboBox;
    ParityComBox->addItem("On");
    ParityComBox->addItem("Off");
    ParityComBox->setCurrentIndex(1);
    StopBitsLabel = new QLabel("StopBits: ");
    StopBitsComBox = new QComboBox;
    StopBitsComBox->addItem("1");
    StopBitsComBox->addItem("2");


    // QTextEdit *StatusOfBT = new QTextEdit;

    //进行布局,先横向再纵向
    QHBoxLayout *Dock1Layout1 = new QHBoxLayout();
    Dock1Layout1->addWidget(BlueToothLabel);
    Dock1Layout1->addWidget(BlueToothPortComboBox);

    QHBoxLayout *Dock1Layout2 = new QHBoxLayout();
    Dock1Layout2->addWidget(BaudRateLabel);
    Dock1Layout2->addWidget(BaudRateComBox);

    QHBoxLayout *Dock1Layout3 = new QHBoxLayout();
    Dock1Layout3->addWidget(DateRateLabel);
    Dock1Layout3->addWidget(DateRateComBox);

    QHBoxLayout *Dock1Layout4 = new QHBoxLayout();
    Dock1Layout4->addWidget(ParityLabel);
    Dock1Layout4->addWidget(ParityComBox);

    QHBoxLayout *Dock1Layout5 = new QHBoxLayout();
    Dock1Layout5->addWidget(StopBitsLabel);
    Dock1Layout5->addWidget(StopBitsComBox);

    QHBoxLayout *Dock1Layout6 = new QHBoxLayout();
    Dock1Layout6->addWidget(ConnectBtn);//点击后就开始寻找设备进行连接
    Dock1Layout6->addWidget(BreakBtn);//点击后断开串口连接
 //   Dock1Layout6->addWidget(Stop1Btn);//暂时未用到

    QVBoxLayout *Dock1Layout = new QVBoxLayout();
    Dock1Layout->setAlignment(Qt::AlignCenter);
    Dock1Layout->addLayout(Dock1Layout1);
    Dock1Layout->addLayout(Dock1Layout2);
    Dock1Layout->addLayout(Dock1Layout3);
    Dock1Layout->addLayout(Dock1Layout4);
    Dock1Layout->addLayout(Dock1Layout5);
    Dock1Layout->addLayout(Dock1Layout6);


    QWidget *Dock1Widget = new QWidget();
    Dock1Widget->setLayout(Dock1Layout);
    dock1->setWidget(Dock1Widget);

    ConnectBtn->setMinimumWidth(70);
    BreakBtn->setMaximumWidth(50);
    Stop1Btn ->setMaximumWidth(50);

    //搜索串口,并添加到选项上供使用者选择
    foreach(const QSerialPortInfo &Info,QSerialPortInfo ::availablePorts())
    {
        QSerialPort CurrentPort;
        CurrentPort.setPort(Info);
        if(CurrentPort.open(QIODevice::ReadWrite))
        {
          BlueToothPortComboBox->addItem(CurrentPort.portName());//插入串口的名字
          CurrentPort.close();   //先开再关,把串口名称先导入
        }

    }
    connect(ConnectBtn, SIGNAL(clicked()),this,SLOT(on_connectButton_clicked()));
    connect(BreakBtn, SIGNAL(clicked()),this,SLOT(on_breakButton_clicked()));

}

void MainWindow::on_clearButton_clicked()//清空发送与接收窗口信息
{
    SendInfo->clear();
    ReceiveInfo->clear();
}


void MainWindow::on_sendButtton_clicked()//发送数据
{


    QByteArray SendBytes = SendInfo->toPlainText().toLatin1();//toPlainText(将文本编辑的文本转换为纯文本)


    if(SendBytes.isEmpty())//判断发送数据是否为空
     {
            StatusOfDock3->append("No message can be sent, Please write something");
     }

    CurrentPort->write(SendBytes);
}


void MainWindow::Read_Data()//读取接收到的数据
{
    QByteArray buf;
    buf = CurrentPort->readAll();//Qbytearray类提供一个字节数组,buf这里应该是缓冲数据的功能


    if(!buf.isEmpty())
    {
        QString str = this->ReceiveInfo->toPlainText().toUtf8();
        str += tr(buf);//???
        ReceiveInfo->clear();
        ReceiveInfo->append(str);

    }
    buf.clear();
}


void MainWindow::on_connectButton_clicked()
{
        CurrentPort = new QSerialPort;
        CurrentPort->setPortName(BlueToothPortComboBox->currentText());//设置串口名
        //设置波特率
        switch (BaudRateComBox->currentIndex())
        {
         case 0: StatusOfDock3->append("Baud:115200");
            CurrentPort->setBaudRate(QSerialPort::Baud115200,QSerialPort::AllDirections); break;
         case 1: StatusOfDock3->append("Baud:9600");
            CurrentPort->setBaudRate(QSerialPort::Baud9600,QSerialPort::AllDirections); break;
        default: break;
        }

        //设置数据位数
        switch(DateRateComBox->currentIndex())
        {
        case 0: StatusOfDock3->append("Data:5");
            CurrentPort->setDataBits(QSerialPort::Data5); break;
        case 1: StatusOfDock3->append("Data:6");
            CurrentPort->setDataBits(QSerialPort::Data6); break;
        case 2: StatusOfDock3->append("Data:7");
            CurrentPort->setDataBits(QSerialPort::Data7); break;
        case 3: StatusOfDock3->append("Data:8");
            CurrentPort->setDataBits(QSerialPort::Data8); break;
        default: break;
        }
        //设置奇偶校验
        switch(ParityComBox->currentIndex())
        {
        case 1:StatusOfDock3->append("Parity: off");
            CurrentPort->setParity(QSerialPort::NoParity); break;
        default: break;
        }
        switch(StopBitsComBox->currentIndex())   //设置停止位
        {
        case 0: StatusOfDock3->append("StopBits:1");
            CurrentPort->setStopBits(QSerialPort::OneStop); break;
        case 1: StatusOfDock3->append("StopBits:2");
            CurrentPort->setStopBits(QSerialPort::TwoStop); break;
        default: break;
        }
        CurrentPort->setFlowControl(QSerialPort::NoFlowControl);  //设置流控制

        CurrentPort->open(QIODevice::ReadWrite);//打开串口

        if(CurrentPort->isOpen())
        {
           StatusOfDock3->append("Succeesfully open the Port ");
           //关闭设置菜单使能
           BlueToothPortComboBox->setEnabled(false);
           BaudRateComBox->setEnabled(false);
           DateRateComBox->setEnabled(false);
           ParityComBox->setEnabled(false);
           StopBitsComBox->setEnabled(false);
           ConnectBtn->setEnabled(false);
           BreakBtn->setEnabled(true);
           SendBtn->setEnabled(true);
        }
        else
        {
           StatusOfDock3->append("Defeatly open the port");
        }

        //连接信号槽
        QObject::connect(CurrentPort, &QSerialPort::readyRead, this, &MainWindow::Read_Data);

}
void MainWindow::on_breakButton_clicked()
{
    //关闭串口
    CurrentPort->clear();
    CurrentPort->close();
    CurrentPort->deleteLater();
    StatusOfDock3->append("Serial connection has been disconnected");
    //恢复设置使能
    BlueToothPortComboBox->setEnabled(true);
    BaudRateComBox->setEnabled(true);
    DateRateComBox->setEnabled(true);
    ParityComBox->setEnabled(true);
    StopBitsComBox->setEnabled(true);
    ConnectBtn->setEnabled(true);
    BreakBtn->setEnabled(false);
    SendBtn->setEnabled(false);
}
void MainWindow::startPainting()
{

}
void MainWindow::stopPainting()
{

}
void MainWindow::setvariables()
{
    //停靠窗口2,调整各种变量
    QDockWidget *dock2 = new QDockWidget(tr("DockWindow2"));
    dock2->setFeatures(QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetFloatable); //窗口可移动
    dock2->setAllowedAreas(Qt::LeftDockWidgetArea|Qt::RightDockWidgetArea);
    //dock2->setStyleSheet("QPushButtonl{color:white;background-color:black}");//设置按钮/标签等颜色   color\background-color
                                                                             //空白margain/边框border/填充padding/内容content
    addDockWidget(Qt::RightDockWidgetArea,dock2);
                  //设置各个Widget,进行填充
    InitELabel = new QLabel(tr("Init E(V): "));
    InitELineEdit = new QLineEdit;
    FinalELabel = new QLabel(tr("Final E(V): "));
    FinalELineEdit = new QLineEdit;
    ScanRateLabel = new QLabel(tr("Scan Rate (mV/s): "));
    ScanRateLineEdit = new QLineEdit;
    ScanningDirectionLabel = new QLabel(tr("Scanning direction: "));
    ScanningDirectionComboBox = new QComboBox;
    ScanningDirectionComboBox->addItem(tr("Positive"));
    ScanningDirectionComboBox->addItem(tr("Negative"));
    GainLabel = new QLabel(tr("Gain : "));
    GainLineEdit = new QLineEdit;
    SampleIntervalLabel = new QLabel(tr("Sample Interval (mv): "));
    SampleIntervalLineEdit = new QLineEdit;
    QuietTimeLabel = new QLabel(tr("Quiet Time: "));
    QuietTimeLineEdit = new QLineEdit;
    SendBtn = new QPushButton(tr("Send"));
    StartBtn = new QPushButton(tr("Start"));
    PauseBtn = new QPushButton(tr("Pause"));
    Stop2Btn = new QPushButton(tr("Stop"));
    dock2->setStyleSheet("QPushButton{color:white;background-color:black}");//设置按钮样式
    StartBtn->setMaximumWidth(50);
    PauseBtn->setMaximumWidth(50);
    Stop2Btn ->setMaximumWidth(50);
    //设置完各个控件进行布局,先水平布局再垂直布局
    QHBoxLayout *Dock2Layout1 = new QHBoxLayout();
    Dock2Layout1->addWidget(InitELabel);
    Dock2Layout1->addWidget(InitELineEdit);

    QHBoxLayout *Dock2Layout2 = new QHBoxLayout();
    Dock2Layout2->addWidget(FinalELabel);
    Dock2Layout2->addWidget(FinalELineEdit);

    QHBoxLayout *Dock2Layout3 = new QHBoxLayout();
    Dock2Layout3->addWidget(ScanRateLabel);
    Dock2Layout3->addWidget(ScanRateLineEdit);

    QHBoxLayout *Dock2Layout4 = new QHBoxLayout();
    Dock2Layout4->addWidget(ScanningDirectionLabel);
    Dock2Layout4->addWidget(ScanningDirectionComboBox);

    QHBoxLayout *Dock2Layout5 = new QHBoxLayout();
    Dock2Layout5->addWidget(GainLabel);
    Dock2Layout5->addWidget(GainLineEdit);

    QHBoxLayout *Dock2Layout6 = new QHBoxLayout();
    Dock2Layout6->addWidget(SampleIntervalLabel);
    Dock2Layout6->addWidget(SampleIntervalLineEdit);

    QHBoxLayout *Dock2Layout7 = new QHBoxLayout();
    Dock2Layout7->addWidget(QuietTimeLabel);
    Dock2Layout7->addWidget(QuietTimeLineEdit);

    QHBoxLayout *Dock2Layout8 = new QHBoxLayout();
    Dock2Layout8->addWidget(SendBtn);
    Dock2Layout8->addWidget(StartBtn);
    Dock2Layout8->addWidget(PauseBtn);
    Dock2Layout8->addWidget(Stop2Btn);

    QVBoxLayout *Dock2Layout = new QVBoxLayout;
    Dock2Layout->setAlignment(Qt::AlignCenter);
    Dock2Layout->addLayout(Dock2Layout1);
    Dock2Layout->addLayout(Dock2Layout2);
    Dock2Layout->addLayout(Dock2Layout3);
    Dock2Layout->addLayout(Dock2Layout4);
    Dock2Layout->addLayout(Dock2Layout5);
    Dock2Layout->addLayout(Dock2Layout6);
    Dock2Layout->addLayout(Dock2Layout7);
    Dock2Layout->addLayout(Dock2Layout8);

    Dock2Layout->setSpacing(20);//设置间隙
    QWidget *Dock2Widget = new QWidget();
    Dock2Widget->setLayout(Dock2Layout);
    dock2->setWidget(Dock2Widget);

    connect(SendBtn,SIGNAL(clicked()),this,SLOT(on_sendButtton_clicked()));

}
void MainWindow::statusOfAll()//最下面的窗口,显示图像的各个数据;暂时让收发数据显示在这里,到后面可隐藏掉
{
    //停靠窗口3,显示图像各种参数
    QDockWidget *dock3 = new QDockWidget(tr("DockWindow3"));
    dock3->setFeatures(QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetFloatable); //窗口可移动
    dock3->setAllowedAreas(Qt::BottomDockWidgetArea);
    addDockWidget(Qt::BottomDockWidgetArea,dock3);
    dock3->setMinimumSize(1500,100);
    QHBoxLayout *Dock3Layout = new QHBoxLayout;
    StatusOfDock3 = new QTextEdit;
    StatusOfDock3->setMinimumSize(300,200);
    SendInfo = new QTextEdit;
    ReceiveInfo = new QTextEdit;

    Dock3Layout->addWidget(StatusOfDock3);
    Dock3Layout->addWidget(SendInfo);
    Dock3Layout->addWidget(ReceiveInfo);
    QWidget *Dock3Widget = new QWidget();
    Dock3Widget->setLayout(Dock3Layout);
    dock3->setWidget(Dock3Widget);

}
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    setWindowTitle(tr("V1.0"));//窗体标题
    //主窗口设计
    scene = new QGraphicsScene;
    scene->setSceneRect(-200,-200,400,400);
    //initScene();
    QGraphicsView *view = new QGraphicsView;
    view->setScene(scene);
    view->setMinimumSize(800,600);
    setCentralWidget(view);
    resize(1000,800);
    //创建动作、菜单、工具栏的函数
    createActions();
    createMenus();
    createToolBars();
    createStatusBars();
    //蓝牙、参数设置、状态显示
    bluetooth();
    setvariables();
    statusOfAll();
    //创建坐标轴


}

MainWindow::~MainWindow()
{

}

3. 实例结果

QT5实现串口收发数据(上位机与下位机通信)_第1张图片
QT5实现串口收发数据(上位机与下位机通信)_第2张图片

三、QT5串口注意点

虚拟串口下载:https://download.csdn.net/download/qq_41429220/11383961
串口助手下载:https://download.csdn.net/download/qq_41429220/11383925
本实例源文件下载:https://download.csdn.net/download/qq_41429220/11383958

  1. 运行程序后实现收发数据,电脑端可以直接进行模拟,虚拟串口出来。用COM1实现发送,再用串口助手将COM2打开,两者之间可以成功实现通信。

你可能感兴趣的:(Qt5)