一开始想用Qml写一个简单的串口助手来熟悉Qml是如何和C++来进行交互的。Qml用来写我们所见到的界面,C++完成背后的逻辑处理,有点类似于前端和后端的概念。在实现多线程的过程中用了一些时间,在这里记录一下交互和多线程的实现。

Qml中访问C++的方式

因为要在Qml中访问C++类或对象,对C++类有这两点要求

1.类必须继承自QObject类或其导出类
2.类首添加QObject宏
这两条要求是把这个类加入Qt的元对象系统中,只有使用元对象系统,一个类的方法和属性才可以通过字符串的形式的名字来调用,才具有在QML中访问的基础条件

在Qml中访问 C++类或对象 ,有如下的方式

1.信号和槽
2.具有Q_INVOKABLE 宏方法
3.具有Q_ENUMS宏的枚举
4.具有Q_PROPERTY宏的属性

在Qml中和C++交互,有两种方式

1.将C++类注册到Qml中,在Qml中完成类的实例化
2.将C++类的 实例 注册为Qml的上下文属性,可在Qml中直接引用该实例

a)先来看第一种方式,C++类注册到Qml中
要达到在Qml中访问 C++类 的目的,要将这个类注册为Qml的可用类型,注册方法如下

  1. qmlRegisterType : 可在Qml里面直接进行构造实例并访问
  2. qmlRegisterUncreatableType : 可在C++里面以属性的方式进行访问,但不可以在qml前端进行访问
  3. qmlRegisterSingltonType : 注册Qml单例,Qml中可通过 inporm相应包的方式进行访问

注册之后就可以在Qml中像普通控件一样使用C++类的实例,前提要import C++类导出的包

再来看第二种方式,C++类的实例注册为Qml的上下文属性

1.在main函数中,如果用QQuickView加载Qml文件
在这里插入图片描述 >2在main函数中,如果用QQmlApplicationEngine加载Qml文件
在这里插入图片描述
因为在C++中对串口类进行操作很方便,所以采用第二种方式,将串口类的实例导入Qml中的上下文属性。

MySerialPort类的定义

#ifndef MYSERIALPORT_H
#define MYSERIALPORT_H
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
#include <QObject>
class MySerialPort : public QObject
    Q_OBJECT
    Q_PROPERTY(bool linkStatus  READ linkStatus NOTIFY linkStatusChanged)
    Q_PROPERTY(QStringList portName  READ portName NOTIFY portNameChanged)
    Q_PROPERTY(QString strDisplay  READ strDisplay NOTIFY strDisplayChanged)//这里没有用到
public:
    explicit MySerialPort(QObject *parent = nullptr);
    QList<QSerialPortInfo> availablePorts();
    static bool isSystemPort(QSerialPortInfo *port);
    QStringList portName()const{
        return myPortName;
    bool linkStatus()const{
        return myLinkStatus;
    QString strDisplay()const{
        return hexStrDisplay;
    Q_INVOKABLE void serialPortOperate(int PortNameIndex,int BaudRateIndex,int StopbitsIndex,int DatabitsIndex,int ParityIndex);
private:
    QSerialPort *mySerialPort;
    bool myLinkStatus=false;     //true connected  false unconnected
    QList<QSerialPortInfo> mySerialPortInfoList;
    QStringList myPortName;
    QString hexStrDisplay;
signals:
    void portNameChanged(QStringList);
    void linkStatusChanged(bool);
    void strDisplayChanged(QString);
    void bytesReceived(QByteArray data);
public slots:
    void readMyCom();
#endif // MYSERIALPORT_H

在这个类中我用Q_PROPERTY注册了三个属性供Qml访问,三个属性portName 、linkStatus 、strDisplay 的Changed信号发射后自动会更改前端中使用了这三个属性的控件属性。还有一个有Q_INVOKABLE宏操作串口的函数,接收前端发来的Indexs来打开串口。我在这里并没有定义定时器来定时检测串口,所以先插串口设备在运行哦

#include "myserialport.h"
#include <mavlink.h>   //这里我的目的是接收串口字节流解析mavlink消息
#include <QDebug>
MySerialPort::MySerialPort(QObject *parent) : QObject(parent){
    mySerialPortInfoList = availablePorts();
    qDebug() << "run  myserialport class constructor" ;
QList<QSerialPortInfo> MySerialPort::availablePorts(void){
    QList<QSerialPortInfo>    list;
    foreach(QSerialPortInfo portInfo, QSerialPortInfo::availablePorts()) {
        if (!isSystemPort(&portInfo)) {
            list << *((QSerialPortInfo*)&portInfo);
            qDebug() << "portName    :" << portInfo.portName();    //调试时可以看的串口信息
            qDebug() << "Description :" << portInfo.description();
            qDebug() << "Manufacturer:" << portInfo.manufacturer();
            myPortName.append(portInfo.portName());
    emit portNameChanged(myPortName);      //通知前端comNameCombox下拉菜单更新
    return list;
bool MySerialPort::isSystemPort(QSerialPortInfo* port){
    // Known operating system peripherals that are NEVER a peripheral
    // that we should connect to.
    // XXX Add Linux (LTE modems, etc) and Windows as needed
    // MAC OS
    if (port->systemLocation().contains("tty.MALS")
        || port->systemLocation().contains("tty.SOC")
        || port->systemLocation().contains("tty.Bluetooth-Incoming-Port")
        // We open these by their cu.usbserial and cu.usbmodem handles
        // already. We don't want to open them twice and conflict
        // with ourselves.
        || port->systemLocation().contains("tty.usbserial")
        || port->systemLocation().contains("tty.usbmodem")) {
        return true;
    return false;
void MySerialPort::serialPortOperate(int PortNameIndex, int BaudRateIndex, int StopbitsIndex, int DatabitsIndex, int ParityIndex){
    qDebug() << "run  myserialport operator" ;
    if(myLinkStatus==false) // 目前尚无连接
        qDebug()<<"portNameIndex:"<<QString::number(PortNameIndex);
        qDebug()<<"baudNameIndex:"<<QString::number(BaudRateIndex);
        qDebug()<<"stopNameIndex:"<<QString::number(StopbitsIndex);
        qDebug()<<"dataNameIndex:"<<QString::number(DatabitsIndex);
        qDebug()<<"parityNameIndex:"<<QString::number(ParityIndex);
        mySerialPort = new QSerialPort();        //串口类对象的实例化
        if (mySerialPort->isOpen()){
           qDebug("COM already open");
           return;
        mySerialPort->setPortName(myPortName[PortNameIndex]);
        mySerialPort->setBaudRate(115200);
        mySerialPort->setDataBits(QSerialPort::Data8);
        mySerialPort->setParity(QSerialPort::NoParity);
        mySerialPort->setFlowControl( QSerialPort::NoFlowControl );
        mySerialPort->setStopBits(QSerialPort::OneStop);
        myLinkStatus = mySerialPort->open(QIODevice::ReadWrite);
        if(myLinkStatus){
            mySerialPort->setDataTerminalReady(true);
            qDebug() << mySerialPort->portName() + " is open";
        }else {
            qDebug("Uart not exist or being occupied");
            return;
        emit linkStatusChanged(myLinkStatus);//通知前端连接状态变化
 //串口类对象的实例化,后连接readyRead信号 到本类的 readMyCom槽
 connect(mySerialPort,SIGNAL(readyRead()),this,SLOT(readMyCom()),Qt::QueuedConnection);
//connect(mySerialPort, &MySerialPort::bytesReceived, _mavlinkProtocol,  &MAVLinkProtocol::receiveBytes);
    } else  //存在连接 要断开
        if((mySerialPort->isOpen()))
             qDebug() << mySerialPort->portName() + " is close";
             mySerialPort->clear();
             mySerialPort->close();
             mySerialPort->deleteLater();
             myLinkStatus=false;
             emit linkStatusChanged(myLinkStatus);//通知前端连接状态变化
void MySerialPort::readMyCom()
    qint64 byteCount = mySerialPort->bytesAvailable();
    if (byteCount) {
      QByteArray buffer;
      buffer.resize(byteCount);
      mySerialPort->read(buffer.data(), buffer.size());
      emit bytesReceived(buffer);  //通知解析类
   else {
        // Error occurred
        qWarning() << "Serial port not readable";

在构造函数中调用availablePorts函数检测可用串口,并且将port name加入myPortName这个QStringList类型变量中,在发射portNameChanged信号通知前端显示串口号。在前端的Button的onClicked槽中调用serialPortOperate并传入Index参数来配置串口,我在这里简化了只用了portNameIndex来选择串口号,其他的采用默认配置。在serialPortOperate实例化串口类对象并连接readyRead槽到本类的读取函数readMyCom中,在readMyCom中发射bytesReceived通知解析类来解析字节流。

接下来在Main函数中注册类的实例为Qml上下文属性

    MySerialPort *myport = new MySerialPort();
    QObject::connect(myport, &MySerialPort::bytesReceived,myMavlinkProtocol, &MAVLinkProtocol::receiveBytes);//串口接收的字节流到解析类
    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("MySerialPort",myport);
    engine.rootContext()->setContextProperty("MavlinkProtocol",myMavlinkProtocol);
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

在来看Qml文件

import QtQuick 2.9
import QtQuick.Controls 2.2
//import an.qt.MySerialPort 1.0
Page {
  objectName: "COMConfigurePage";
  width: 600
  height: 400
  title:qsTr("Uart Conncet");
  Column{
      id:comConfigureLayout;
      anchors.verticalCenter: parent.verticalCenter;
      anchors.horizontalCenter: parent.horizontalCenter;
      spacing:15;
      Row{
          id:comRowLayout;
          spacing:15;
          Label{
              id:comLabel;
              width:25;
              anchors.verticalCenter: parent.verticalCenter;
              text:                   qsTr("Com")
              wrapMode:               Text.WordWrap
              horizontalAlignment:    Text.AlignHCenter
          ComboBox{
              objectName: "comCombox1";
              id:comCombox;
              height: 35;
              anchors.verticalCenter: parent.verticalCenter;
              model:MySerialPort.portName;
      Row{
          id:baudRowLayout;
          spacing:15;
          Label{
              id:baudLabel;
               width:25;
               anchors.verticalCenter: parent.verticalCenter;
              text:                   qsTr("Baud")
              wrapMode:               Text.WordWrap
              horizontalAlignment:    Text.AlignHCenter
          ComboBox{
              objectName: "baudCombox";
              id:baudCombox;
              height: 35;
              anchors.verticalCenter: parent.verticalCenter;
              model: ["9600","115200","230400","460800"];
      Row{
          id:stopRowLayout;
          spacing:15;
          Label{
              id:stopLabel;
              width:25;
              anchors.verticalCenter: parent.verticalCenter;
              text:                   qsTr("Stop")
              wrapMode:               Text.WordWrap
              horizontalAlignment:    Text.AlignHCenter
          ComboBox{
              objectName: "stopCombox";
              id:stopCombox;
              height: 35;
              anchors.verticalCenter: parent.verticalCenter;
              model: ["1","1.5","2"];
      Row{
         id:dataRowLayout;
          spacing:15;
          Label{
              id:dataLabel;
               width:25;
               anchors.verticalCenter: parent.verticalCenter;
              text:                   qsTr("Data")
              wrapMode:               Text.WordWrap
              horizontalAlignment:    Text.AlignHCenter
          ComboBox{
              objectName: "dataCombox";
              id:dataCombox;
              height: 35;
              anchors.verticalCenter: parent.verticalCenter;
              model: ["8","7","6","5"];
      Row{
          id:parityRowLayout;
          spacing:15;
          Label{
              id:parityLabel;
               width:25;
               anchors.verticalCenter: parent.verticalCenter;
              text:                   qsTr("Parity")
              wrapMode:               Text.WordWrap
              horizontalAlignment:    Text.AlignHCenter
          ComboBox{
              objectName: "parityCombox";
              id:parityCombox;
              height: 35;
              anchors.verticalCenter: parent.verticalCenter;
              model: ["NoParity","Even","Odd"];
      Row{
          id:operateRowLayout;
          spacing:15;
          Label{
              id:operateLabel;
              width:25;
              anchors.verticalCenter: parent.verticalCenter;
              text:                   qsTr("Operate")
              wrapMode:               Text.WordWrap
              horizontalAlignment:    Text.AlignHCenter
          Button{
              objectName: "operateBtn";
              id:operateBtn;
              width:140
              height: 35;
              anchors.verticalCenter: parent.verticalCenter;
              text:"Open";          onClicked:MySerialPort.serialPortOperate(comCombox.currentIndex,baudCombox.currentIndex,stopCombox.currentIndex,dataCombox.currentIndex,parityCombox.currentIndex);
  Connections{
      target:MySerialPort;
      onLinkStatusChanged:{
          if(MySerialPort.linkStatus==true){
              operateBtn.text="Close";
              comCombox.enabled=false;
              baudCombox.enabled=false;
              stopCombox.enabled=false;
              dataCombox.enabled=false;
              parityCombox.enabled=false;
          else{
              operateBtn.text="Open";
              comCombox.enabled=true;
              baudCombox.enabled=true;
              stopCombox.enabled=true;
              dataCombox.enabled=true;
              parityCombox.enabled=true;

Qml文件中以一个列布局中定义6个行布局类展示 Label和 Combox 以及最后一行的Button的。显示串口号的Combox中有个model是C++中的属性MySerialPort.portName ,这里的MySerialPort是在main函数中注册的上下文属性。在Button的onClicked槽中调用C++函数,onClicked:MySerialPort.serialPortOperate(comCombox.currentIndex,baudCombox.currentIndex,stopCombox.currentIndex,dataCombox.currentIndex,parityCombox.currentIndex);
带走currentIndex属性到前端操作串口。最后一个Connections连接了C++中linkStatusChanged信号到Qml中更改ComBox使能属性和Button文本。

运行结果如下,我是用Qt 5.11中自带的Qt Quick Stack模板,有个Stack切换三个Page
在这里插入图片描述可以看到能够稳定的解析Mavlink消息
在这里插入图片描述

串口多线程的实现

我们知道在Qt中多线程的实现通常有两种方法,

1.自定义的类继承字QThread,并重新实现父类中的虚函数run
2.利用moveToThread

先来看第一种,定义MyThread类继承字QThread,重新实现run函数

#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QObject>
#include <QDebug>
#include <QThread>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
class MyThread : public QThread
    Q_OBJECT
public:
    MyThread(QString *portName);
    void run();
public slots:
    void read_serial_data();//读取串口数据
private:
    QSerialPort *my_serialPort_thread;
    QByteArray MyByteArray_thread;
    QByteArray DataToAnalysis_thread;
public:
    QString port_Name;
#endif // MYTHREAD_H

在MyThread.cpp中

#include "mythread.h"
#include <QDebug>
#include <QMessageBox>
#include <QByteArray>
#include <QEventLoop>
MyThread::MyThread(QString *portName){
    port_Name=*portName;
void MyThread::run(){
    my_serialPort_thread = new QSerialPort();
    if (my_serialPort_thread->isOpen()) {
       qDebug("COM already open");
       return;
    my_serialPort_thread->setPortName(port_Name);
    my_serialPort_thread->setBaudRate(115200);//波特率
    my_serialPort_thread->setDataBits(QSerialPort::Data8);
    my_serialPort_thread->setParity(QSerialPort::NoParity);
    my_serialPort_thread->setFlowControl( QSerialPort::NoFlowControl );
    my_serialPort_thread->setStopBits(QSerialPort::OneStop);
    bool flag = my_serialPort_thread->open( QIODevice::ReadWrite );
    if(flag==false) {
        qDebug("Uart not exist or being occupied");
        return;
   }else {
        my_serialPort_thread->setDataTerminalReady(true);
        qDebug() << my_serialPort_thread->portName() + " is open";
    connect(my_serialPort_thread,SIGNAL(readyRead()),this,SLOT(read_serial_data()));
    QEventLoop eventLoop;
    eventLoop.exec();
void MyThread::read_serial_data()
    qDebug()<<"SerialReadThread:" <<currentThreadId();
    QString strDisplay_thread;
    QByteArray byte_data = my_serialPort_thread->readAll();
    if(!byte_data.isEmpty())
        MyByteArray_thread.append(byte_data);

这里需要注意的是在run中开始事件循环,否则默认不开启,运行完run中的代码,此线程就结束了,开始事件循环才能在槽中接收到字节。然而脱离的run函数的串口读取槽,并没有在新的线程中运行,而只有run函数中的代码在新线程中等待事件循环。可以通过线程ID查看,这里就不看了。

再来看第二种,利用moveToThread

int main(int argc, char *argv[])
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);
    QThread mySerialPortThread;
    MySerialPort *myport = new MySerialPort();
    myport->moveToThread(&mySerialPortThread);
    mySerialPortThread.start();
    MAVLinkProtocol *myMavlinkProtocol = new MAVLinkProtocol();
    QObject::connect(myport, &MySerialPort::bytesReceived,myMavlinkProtocol, &MAVLinkProtocol::receiveBytes);
    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("MySerialPort",myport);
    engine.rootContext()->setContextProperty("MavlinkProtocol",myMavlinkProtocol);
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;
    return app.exec();

运行后报如下错误
在这里插入图片描述哎,因为要再Qml中访问C++类的属性,将该实例注册到了Qml的上下文,在将该实例moveToThread到另外一个线程,这个C++类和Qml引擎不再同一个线程里……醉了,Qml中这种方法也不能使用……吐血

还好天无绝人之路,CSDN找到了一个CopyFile的例程,给我了启发,再写一个串口和Qml交互的中间类不就可以了吗?接着撸代码吧,这次讲解不细了,直接贴代码吧,只要搞过串口的应该都看的懂
myserialport.h //这里不再有再Qml中需要访问的属性和方法,并定义了一个Timer检查串口设备变化

#ifndef MYSERIALPORT_H
#define MYSERIALPORT_H
#include <QObject>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
#include <QDebug>
#include <QTimer>
#include <QThread>
class MySerialPort : public QObject
    Q_OBJECT
public:
    explicit MySerialPort(QObject *parent = nullptr);
    QStringList getMyPortName(){ return myPortName; }
    bool getLinkStatus(){return myLinkStatus;}
signals:
    void bytesReceived(QByteArray data);
    void linkStatusChanged(bool linkStatus);
    void portNameReady(QStringList portName);
public slots:
    void serialPortOperate(int portNameIndex,int baudRateIndex,int stopbitsIndex,int databitsIndex,int parityIndex , int flowCtrlIndex);
    void readMyCom();
    void checkMySerialPortEvent();
private:
    QList<QSerialPortInfo> availablePorts() ;
    static bool isSystemPort(QSerialPortInfo *port);
private:
    QSerialPort *mySerialPort;
    QList<QSerialPortInfo> mySerialPortInfoList;
    QStringList myPortName;
    bool myLinkStatus=false;
    QTimer *mySerialPortCheckTimer;
#endif // MYSERIALPORT_H

myserialport.cpp

#include "myserialport.h"
MySerialPort::MySerialPort(QObject *parent) : QObject(parent)
//   qDebug()<<"MySerialPort constructor thread:" <<QThread::currentThreadId();
    mySerialPortInfoList = availablePorts();
    mySerialPortCheckTimer= new QTimer(this);
    connect(mySerialPortCheckTimer, SIGNAL(timeout()), this, SLOT(checkMySerialPortEvent()));
    mySerialPortCheckTimer->start(1000);
//    qDebug() << "run  MySerialPort Class Constructor" ;
void MySerialPort::serialPortOperate(int portNameIndex, int baudRateIndex, int stopbitsIndex, int databitsIndex, int parityIndex, int flowCtrlIndex)
//    qDebug() << "run  myserialport operator" ;
    if(myLinkStatus==false) // 目前尚无连接
//        qDebug()<<"portNameIndex:"<<QString::number(portNameIndex);
//        qDebug()<<"baudRateIndex:"<<QString::number(baudRateIndex);
//        qDebug()<<"stopbitsIndex:"<<QString::number(stopbitsIndex);
//        qDebug()<<"databitsIndex:"<<QString::number(databitsIndex);
//        qDebug()<<"parityIndex:"<<QString::number(parityIndex);
//        qDebug()<<"FlowCtrlIndex:"<<QString::number(flowCtrlIndex);
        mySerialPort = new QSerialPort();
        if (mySerialPort->isOpen()){
           qDebug("COM already open");
           return;
        mySerialPort->setPortName(myPortName[portNameIndex]);
        mySerialPort->setBaudRate(115200);
        mySerialPort->setDataBits(QSerialPort::Data8);
        mySerialPort->setParity(QSerialPort::NoParity);
        mySerialPort->setFlowControl( QSerialPort::NoFlowControl );
        mySerialPort->setStopBits(QSerialPort::OneStop);
        myLinkStatus = mySerialPort->open(QIODevice::ReadWrite);
        if(myLinkStatus){
            mySerialPort->setDataTerminalReady(true);
            qDebug() << mySerialPort->portName() + " is open";
            emit linkStatusChanged(myLinkStatus);
        else {
            qDebug("Uart not exist or being occupied");
            return;
        }  QObject::connect(mySerialPort,SIGNAL(readyRead()),this,SLOT(readMyCom()),Qt::QueuedConnection);
    else  //存在连接 要断开
        if((mySerialPort->isOpen())){
             qDebug() << mySerialPort->portName() + " is close";
             mySerialPort->clear();
             mySerialPort->close();
             mySerialPort->deleteLater();
             myLinkStatus=false;
             emit linkStatusChanged(myLinkStatus);
void MySerialPort::readMyCom(){
//    qDebug()<<"SerialReadThread:" <<QThread::currentThreadId();
    qint64 byteCount = mySerialPort->bytesAvailable();
    if (byteCount) {
      QByteArray buffer;
      buffer.resize(byteCount);
      mySerialPort->read(buffer.data(), buffer.size());
      emit bytesReceived(buffer);
    else qWarning() << "Serial port not readable";
//        qDebug()<<"SerialReadThread:" <<QThread::currentThreadId();
//        QByteArray byte_data = mySerialPort->readAll();
//        if(!byte_data.isEmpty())
//        {
            qDebug()<<byte_data.toHex().data();
//            QString str = byte_data.toHex().data();
            qDebug()<<str;
            str = str.toUpper ();
            for(int i = 0;i<str.length();i+=2)
                QString st = str.mid (i,2);
                hexStrDisplay += st;
                hexStrDisplay += " ";
//    //        emit strDisplayChanged(hexStrDisplay);
//        }
void MySerialPort::checkMySerialPortEvent()
    if(myLinkStatus==false){
        availablePorts();
    }else return;
QList<QSerialPortInfo> MySerialPort::availablePorts()
    QList<QSerialPortInfo>    list;
    QStringList portNameLocal;
    foreach(QSerialPortInfo portInfo, QSerialPortInfo::availablePorts()) {
        if (!isSystemPort(&portInfo)) {
            list << *((QSerialPortInfo*)&portInfo);
//            qDebug() << "portName    :" << portInfo.portName();        //调试时可以看的串口信息
//            qDebug() << "Description :" << portInfo.description();
//            qDebug() << "Manufacturer:" << portInfo.manufacturer();
             portNameLocal<<(portInfo.portName());
             myPortName  = portNameLocal;
    emit portNameReady(myPortName);
    return list;
bool MySerialPort::isSystemPort(QSerialPortInfo *port)
    // Known operating system peripherals that are NEVER a peripheral
    // that we should connect to.
    // XXX Add Linux (LTE modems, etc) and Windows as needed
    // MAC OS
    if (port->systemLocation().contains("tty.MALS")
        || port->systemLocation().contains("tty.SOC")
        || port->systemLocation().contains("tty.Bluetooth-Incoming-Port")
        // We open these by their cu.usbserial and cu.usbmodem handles
        // already. We don't want to open them twice and conflict
        // with ourselves.
        || port->systemLocation().contains("tty.usbserial")
        || port->systemLocation().contains("tty.usbmodem")) {
        return true;
    return false;

myserialportqml.h //和Qml交互的类

#ifndef MYSERIALPORTQML_H
#define MYSERIALPORTQML_H
#include <QObject>
#include "myserialport.h"
#include <QThread>
class MySerialPortQml : public QObject
    Q_OBJECT
    Q_PROPERTY(QStringList portNameQml  READ portNameQml NOTIFY portNameQmlChanged)
public:
    explicit MySerialPortQml(QObject *parent = nullptr);
    QStringList portNameQml()const{return myPortNameQml; }
    Q_INVOKABLE void getSerialPortQmlIndex(int portNameIndex,int baudRateIndex,int stopbitsIndex,int databitsIndex,int parityIndex , int flowCtrlIndex);
signals:
    void portNameQmlChanged(QStringList);
    void portLinkStatusQmlChanged(bool linkStatus);
    void setSerialPortQmlIndex(int portNameIndex,int baudRateIndex,int stopbitsIndex,int databitsIndex,int parityIndex , int flowCtrlIndex);
public slots:
    void getPortName(QStringList portName);
    void getSerialPortQmlLinkStatus(bool status);
public:
    MySerialPort *myPortQml;
private:
    QStringList myPortNameQml;
    bool myPortQmlLinkStatus;
#endif // MYSERIALPORTQML_H

myserialportqml.cpp文件

#include "myserialportqml.h"
MySerialPortQml::MySerialPortQml(QObject *parent) : QObject(parent)
//   qDebug()<<"MySerialPortQml constructor thread:" <<QThread::currentThreadId();
   myPortQml = new MySerialPort();
   myPortNameQml = myPortQml->getMyPortName();
   QThread * childThread =  new QThread();
   myPortQml->moveToThread(childThread);
   QObject::connect(childThread, &QThread::finished, childThread, &QObject::deleteLater);
   childThread->start();
//   qDebug()<<" port name from construction:"<<myPortNameQml;
   QObject::connect(myPortQml,SIGNAL(portNameReady(QStringList)),this,SLOT(getPortName(QStringList)));
   QObject::connect(this,SIGNAL(setSerialPortQmlIndex(int,int,int,int,int,int)),myPortQml,SLOT(serialPortOperate(int,int,int,int,int,int)));
   QObject::connect(myPortQml,SIGNAL(linkStatusChanged(bool)),this,SLOT(getSerialPortQmlLinkStatus(bool)));
//   qDebug() << "run  MySerialPortQml Class Constructor" ;
void MySerialPortQml::getSerialPortQmlIndex(int portNameIndex, int baudRateIndex, int stopbitsIndex, int databitsIndex, int parityIndex, int flowCtrlIndex)
    emit setSerialPortQmlIndex(portNameIndex,baudRateIndex,stopbitsIndex,databitsIndex,parityIndex,flowCtrlIndex);
void MySerialPortQml::getPortName(QStringList portName)
//    myPortNameQml.clear();
    myPortNameQml=portName;
    emit portNameQmlChanged(myPortNameQml);
//    qDebug()<<"trig get port name slot:"<<myPortNameQml;
void MySerialPortQml::getSerialPortQmlLinkStatus(bool status)
    myPortQmlLinkStatus=status;
    emit portLinkStatusQmlChanged(myPortQmlLinkStatus);

qml类就不贴了,基本和前面一样的。现在我觉得对于Qml中多线程的实现稍微麻烦一些,也许单纯依靠号信号和槽完成前端Qml和C++交互是更简单的方法,但我目前不想尝试了,主要是花在写博客的时间有点多了……
如果您觉得本文对您有些许帮助,可以小小打赏一下作者哦
在这里插入图片描述

text: model.fileName anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.leftMargin: model.indentation * 10 Image { source: model.isFolder ? "folder.png" : "file.png" width: 16 height: 16 anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right anchors.rightMargin: 5 MouseArea { anchors.fill: parent onClicked: { if (model.isFolder) { model.collapsed = !model.collapsed; } else { selectedFilePath = model.filePath; // 定义 ListView,用于展示文件列表 ListView { id: fileListView width: 200 height: parent.height anchors.left: parent.left anchors.top: parent.top anchors.bottom: parent.bottom spacing: 5 visible: folderPath !== "" model: folderModel delegate: listItemDelegate // 定义纯文本编辑框,用于打开和编辑文件 TextArea { id: textArea width: parent.width - fileListView.width height: parent.height anchors.left: fileListView.right anchors.top: parent.top anchors.bottom: parent.bottom anchors.right: parent.right readOnly: folderPath === "" || selectedFilePath === "" text: selectedFilePath !== "" ? Qt.rgba(0, 0, 0, 1).toHex() + "\n" + Qt.resolvedUrl(selectedFilePath).toLocalFile() : "" font.family: "Courier" wrapMode: TextArea.WrapAnywhere // 定义打开文件夹的按钮,点击后打开文件夹选择框 Button { text: "打开文件夹" anchors.left: parent.left anchors.top: parent.top onClicked: { folderPath = Qt.fileDialog.getExistingDirectory(parent, "选择文件夹", folderPath); 在上述代码,我们使用了 `FolderListModel` 获取指定文件夹下的文件夹和 `.md` 文件,并在 `ListView` 展示了这些文件。同时,在自定义的 `ListItem` 组件添加了 `onClicked` 事件处理函数,实现了双击文件夹展开和收起节点的功能,以及双击 `.md` 文件打开文件的功能。在纯文本编辑框,我们使用了 `TextArea` 组件来打开和编辑文件。