qml界面和c++后端程序交互

打印 上一主题 下一主题

主题 1531|帖子 1531|积分 4593

qml界面和c++后端程序交互


  
1.C++ 数据袒露给qml进行交互

注意事项:
  1. 要想c++类传递到qml,必须是QObject类型的;如果是QWidget或者其他界面类型的c++类则不生效
复制代码
(1)setContextProperty方式(在c++端实例化对象)

serialportmanager.h
  1. #ifndef SERIALPORTMANAGER_H
  2. #define SERIALPORTMANAGER_H
  3. #include <QObject>
  4. #include <QStringList>
  5. #include <QSerialPortInfo>
  6. #include <QTimer>
  7. #include <QDebug>
  8. class SerialPortManager : public QObject
  9. {
  10.     Q_OBJECT
  11.     Q_PROPERTY(QStringList portList READ portList NOTIFY portListChanged)
  12. public:
  13.     explicit SerialPortManager(QObject *parent = nullptr);
  14.     QStringList portList() const;
  15. signals:
  16.     void portListChanged();  // 当串口列表更新时发送信号
  17. public slots:
  18.     void updatePortList();   // 更新串口列表
  19. private:
  20.     QStringList m_portList;  // 存储当前串口列表
  21.     QTimer *m_timer;         // 用于定时更新串口列表
  22.     bool m_isFirstUpdate = true;    // 标志是否是第一次获取串口列表
  23. };
  24. #endif // SERIALPORTMANAGER_H
复制代码
重点:

serialportmanager.cpp
  1. #include "serialportmanager.h"
  2. #include <QSerialPortInfo>
  3. #include <QTimer>
  4. SerialPortManager::SerialPortManager(QObject *parent)
  5.     : QObject(parent), m_timer(new QTimer(this)), m_isFirstUpdate(true)
  6. {
  7.     // 初始时立即获取可用串口列表,并发送信号
  8.     updatePortList();
  9.     // 设置定时器每秒更新一次串口列表
  10.     connect(m_timer, &QTimer::timeout, this, &SerialPortManager::updatePortList);
  11.     m_timer->start(1000);  // 每1000毫秒更新一次串口列表
  12. }
  13. QStringList SerialPortManager::portList() const {
  14.     return m_portList;
  15. }
  16. void SerialPortManager::updatePortList() {
  17.     QStringList ports;
  18.     // 获取所有可用的串口并更新到 ports 列表中
  19.     foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
  20.         ports.append(info.portName());
  21.     }
  22.     // 对串口列表进行排序,确保是从小到大的顺序
  23.     ports.sort();
  24.     // 第一次获取串口列表时,直接发出信号
  25.     if (m_isFirstUpdate) {
  26.         m_portList = ports;
  27.         emit portListChanged();  // 发出串口列表已更新的信号
  28.         qDebug() << "First update, signal emitted.";
  29.         m_isFirstUpdate = false; // 标记为非第一次更新
  30.     }
  31.     else {
  32.         // 如果串口列表发生变化,则更新并发出信号
  33.         if (ports != m_portList) {
  34.             m_portList = ports;
  35.             emit portListChanged();  // 发出串口列表已更新的信号
  36.             qDebug() << "List updated, signal emitted.";
  37.         }
  38.     }
  39. }
复制代码

main.cpp
  1. #include <QGuiApplication>
  2. #include <QQmlApplicationEngine>
  3. #include <QGuiApplication>
  4. #include <QQmlApplicationEngine>
  5. #include <QQmlContext>
  6. #include "SerialPortManager.h"
  7. int main(int argc, char *argv[])
  8. {
  9.     QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
  10.     QGuiApplication app(argc, argv);
  11.     QQmlApplicationEngine engine;
  12.     const QUrl url(QStringLiteral("qrc:/main.qml"));
  13.     QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
  14.                      &app, [url](QObject *obj, const QUrl &objUrl) {
  15.         if (!obj && url == objUrl)
  16.             QCoreApplication::exit(-1);
  17.     }, Qt::QueuedConnection);
  18.     // 创建 SerialPortManager 实例
  19.     SerialPortManager serialPortManager;
  20.     // 将 SerialPortManager 实例暴露给 QML
  21.     engine.rootContext()->setContextProperty("serialPortManager", &serialPortManager);
  22.     // 加载 QML 文件
  23.     engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
  24.     return app.exec();
  25. }
复制代码

main.qml
  1. import QtQuick 2.12
  2. import QtQuick.Window 2.12
  3. import QtQuick.Controls 2.12
  4. //import QtQuick.Dialogs 1.12
  5. ApplicationWindow {
  6.     visible: true
  7.     width: 640
  8.     height: 480
  9.     title: "Serial Port List"
  10.     ListView {
  11.         anchors.fill: parent
  12.         model: ListModel {
  13.             // 绑定到 SerialPortManager 的 portList
  14.             ListElement { portName: "COM1" }
  15.             ListElement { portName: "COM2" }
  16.         }
  17.         delegate: Item {
  18.             width: parent.width
  19.             height: 50
  20.             Text {
  21.                 anchors.centerIn: parent
  22.                 text: portName
  23.             }
  24.         }
  25.         // 定义一个更新串口列表并刷新模型的函数
  26.         function updateSerialPortList() {
  27. //            console.log("portListChanged 信号已触发");
  28.             // 清空现有模型
  29.             model.clear();
  30. //            console.log("模型已清空");
  31.             // 打印获取到的可用串口列表
  32.             console.log("获取到的串口列表: " + serialPortManager.portList.join(", "));
  33.             // 更新模型,填充新的串口列表
  34.             for (var i = 0; i < serialPortManager.portList.length; i++) {
  35.                 console.log("串口名称: " + serialPortManager.portList[i]);
  36.                 model.append({ portName: serialPortManager.portList[i] });
  37.             }
  38. //            console.log("模型更新完毕");
  39.         }
  40.         Component.onCompleted: {
  41. //            console.log("Component.onCompleted: 组件已完成加载");
  42.             // 检查 serialPortManager 是否已初始化
  43.             if (serialPortManager && serialPortManager.portList) {
  44. //                console.log("serialPortManager 已初始化,开始处理 portList");
  45.                 // 连接 portListChanged 信号到独立的更新函数
  46.                 serialPortManager.portListChanged.connect(updateSerialPortList);
  47.                 // 主动调用 C++ 端获取串口列表
  48. //                console.log("调用 C++ 端 updatePortList 获取串口列表");
  49.                 updateSerialPortList();
  50.             } else {
  51.                 // 如果 C++ 端尚未初始化,打印提示
  52.                 console.log("后端程序尚未初始化");
  53.             }
  54.         }
  55.     }
  56. }
复制代码

征象:
初始状态:

串口变化时:

总结:
  1. c++注册并实例化对象;  发送信号
  2. qml界面connect信号并实现槽函数响应;
  3. 注册后的c++对象,qml可以进行访问
复制代码
(2)使用qmlRegisterType方式

1. qmlRegisterType 概述
qmlRegisterType 是 Qt 的一项功能,允许我们将 C++ 类注册到 QML 中。通过这种方式,QML 可以实例化 C++ 类,并与其交互。此机制非常适用于将 C++ 的复杂逻辑袒露给 QML,以便在 UI 层中使用。
在 qmlRegisterType 中,我们可以指定 C++ 类的名称、QML 类型、版本以及需要注册的元类型。这使得 C++ 类能够成为 QML 环境中的一个平凡对象。

  • 基本步调
为了实现 C++ 和 QML 的数据通信,基本的步调如下:

  • 定义 C++ 类:首先,我们定义一个包罗数据成员、信号、槽和 Q_PROPERTY 的 C++ 类。
  • 注册 C++ 类到 QML:使用 qmlRegisterType 将 C++ 类注册到 QML。
  • 在 QML 中使用 C++ 类:在 QML 中创建和使用 C++ 类的实例,并通过信号与槽机制进行数据通信。
  • 示例实现
下面我们通过一个简单的计数器示例,演示怎样使用 qmlRegisterType 实现 C++ 和 QML 的数据通信。
3.1 C++ 类定义
首先,我们定义一个 C++ 类,包罗一个整数属性和一个信号,用来更新计数器的值。
  1. cppCopy Code// counter.h
  2. #ifndef COUNTER_H
  3. #define COUNTER_H
  4. #include <QObject>
  5. class Counter : public QObject
  6. {
  7.     Q_OBJECT
  8.     Q_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged)
  9. public:
  10.     explicit Counter(QObject *parent = nullptr);
  11.     int count() const;
  12.     void setCount(int newCount);
  13. signals:
  14.     void countChanged();
  15. private:
  16.     int m_count;
  17. };
  18. #endif // COUNTER_H
  19. cppCopy Code// counter.cpp
  20. #include "counter.h"
  21. Counter::Counter(QObject *parent) : QObject(parent), m_count(0)
  22. {
  23. }
  24. int Counter::count() const
  25. {
  26.     return m_count;
  27. }
  28. void Counter::setCount(int newCount)
  29. {
  30.     if (m_count != newCount) {
  31.         m_count = newCount;
  32.         emit countChanged();
  33.     }
  34. }
复制代码
3.2 注册 C++ 类到 QML
在应用程序的入口文件中(通常是 main.cpp),我们通过 qmlRegisterType 注册 C++ 类到 QML 环境中。
  1. cppCopy Code#include <QGuiApplication>
  2. #include <QQmlApplicationEngine>
  3. #include <QQmlContext>
  4. #include "counter.h"
  5. int main(int argc, char *argv[])
  6. {
  7.     QGuiApplication app(argc, argv);
  8.     // 注册 C++ 类到 QML
  9.     qmlRegisterType<Counter>("com.example", 1, 0, "Counter");
  10.     QQmlApplicationEngine engine;
  11.     // 加载 QML 文件
  12.     engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
  13.    
  14.     return app.exec();
  15. }
复制代码
在这里,qmlRegisterType<Counter>("com.example", 1, 0, "Counter"); 将 Counter 类注册到 QML 中,并指定了模块名为 com.example,版本为 1.0,QML 中对应的类型名为 Counter。
3.3 在 QML 中使用 C++ 类
接下来,在 QML 中创建一个 Counter 对象,并与其进行交互。我们可以使用 Text 元向来显示 count 属性,并使用按钮来更新计数器的值。
  1. qmlCopy Code// main.qml
  2. import QtQuick 2.15
  3. import QtQuick.Controls 2.15
  4. import com.example 1.0
  5. ApplicationWindow {
  6.     visible: true
  7.     width: 640
  8.     height: 480
  9.     // 创建 Counter 对象
  10.     Counter {
  11.         id: counter
  12.     }
  13.     Column {
  14.         anchors.centerIn: parent
  15.         Text {
  16.             text: "Count: " + counter.count
  17.             font.pixelSize: 32
  18.         }
  19.         Button {
  20.             text: "Increment"
  21.             onClicked: counter.setCount(counter.count + 1)
  22.         }
  23.     }
  24. }
复制代码
在这里,Counter { id: counter } 创建了一个 Counter 对象,并使用 counter.count 显示当前计数值。点击按钮时,onClicked 触发 counter.setCount(counter.count + 1),更新 count 属性的值。由于我们在 C++ 类中使用了 Q_PROPERTY 和信号机制,当 count 属性变化时,QML 主动更新显示。
3.4 运行效果
当你运行这个应用时,QML 界面会显示一个初始计数值 0,并有一个按钮“Increment”。每点击一次按钮,计数值会增加并显示在界面上。C++ 和 QML 之间通过信号与属性的绑定实现了及时的数据通信。
2.qml数据袒露给c++

2.1. C++ 获取 QML 中的对象

C++ 可以通过 QQmlApplicationEngine 或 QQmlComponent 来加载 QML 文件,并通过 rootObject() 或 findChild() 方法获取 QML 对象。
示例代码:

假设我们有一个 QML 文件 Main.qml,此中有一个 Text 对象,我们希望从 C++ 代码中访问并修改它。
  1. // Main.qml
  2. import QtQuick 2.15
  3. import QtQuick.Controls 2.15
  4. ApplicationWindow {
  5.     visible: true
  6.     width: 640
  7.     height: 480
  8.     title: "QML to C++ Example"
  9.     Text {
  10.         id: label
  11.         text: "Hello from QML!"
  12.         anchors.centerIn: parent
  13.     }
  14. }
复制代码
在 C++ 中,我们可以通过以下代码获取并修改这个 Text 对象的 text 属性。
  1. // main.cpp
  2. #include <QCoreApplication>
  3. #include <QQmlApplicationEngine>
  4. #include <QQmlContext>
  5. #include <QQuickItem>
  6. int main(int argc, char *argv[])
  7. {
  8.     QCoreApplication app(argc, argv);
  9.     QQmlApplicationEngine engine;
  10.     // 加载 QML 文件
  11.     engine.load(QUrl(QStringLiteral("qrc:/Main.qml")));
  12.     // 获取根对象
  13.     QObject *rootObject = engine.rootObjects().first();
  14.     // 查找 QML 中的 Text 对象
  15.     QObject *labelObject = rootObject->findChild<QObject*>("label");
  16.     if (labelObject) {
  17.         // 修改 QML 中的 Text 对象的 text 属性
  18.         labelObject->setProperty("text", "Modified by C++!");
  19.     }
  20.     return app.exec();
  21. }
复制代码
2.2. 步调分析



  • 加载 QML 文件:通过 engine.load() 加载 QML 文件。
  • 获取根对象:rootObjects().first() 获取 QML 文件中定义的根对象(ApplicationWindow)。
  • 查找 QML 对象:通过 findChild<QObject*>("label") 查找 id 为 label 的 QML 对象。
  • 修改属性:通过 setProperty 修改 QML 对象的属性,在这里我们修改了 Text 对象的 text 属性。
2.3. 通过信号与槽交互

C++ 也可以连接 QML 中的信号与槽。QML 中的信号可以通过 QObject::connect 方法连接到 C++ 的槽函数。
示例:

假设 QML 中有一个按钮,当按钮被点击时,我们希望 C++ 能够接收到这个信号并进行处理:
  1. // Main.qml
  2. import QtQuick 2.15
  3. import QtQuick.Controls 2.15
  4. ApplicationWindow {
  5.     visible: true
  6.     width: 640
  7.     height: 480
  8.     title: "Signal from QML to C++"
  9.     Button {
  10.         text: "Click Me"
  11.         onClicked: {
  12.             console.log("Button clicked!")
  13.         }
  14.     }
  15. }
复制代码
在 C++ 中,我们可以连接 QML 中的 onClicked 信号:
  1. // main.cpp
  2. #include <QCoreApplication>
  3. #include <QQmlApplicationEngine>
  4. #include <QQmlContext>
  5. #include <QQuickItem>
  6. class MyObject : public QObject {
  7.     Q_OBJECT
  8. public:
  9.     MyObject() {}
  10. public slots:
  11.     void onButtonClicked() {
  12.         qDebug() << "Button clicked in QML!";
  13.     }
  14. };
  15. int main(int argc, char *argv[])
  16. {
  17.     QCoreApplication app(argc, argv);
  18.     QQmlApplicationEngine engine;
  19.     // 创建 C++ 对象
  20.     MyObject myObject;
  21.     // 加载 QML 文件
  22.     engine.load(QUrl(QStringLiteral("qrc:/Main.qml")));
  23.     // 获取根对象
  24.     QObject *rootObject = engine.rootObjects().first();
  25.     // 查找 QML 中的 Button 对象
  26.     QObject *buttonObject = rootObject->findChild<QObject*>("button");
  27.     if (buttonObject) {
  28.         // 连接 QML 信号到 C++ 槽
  29.         QObject::connect(buttonObject, SIGNAL(clicked()), &myObject, SLOT(onButtonClicked()));
  30.     }
  31.     return app.exec();
  32. }
复制代码
2.4. 总结



  • 袒露 QML 对象给 C++:虽然 QML 本身不提供类似袒露 C++ 对象的机制,但 C++ 可以通过 QQmlApplicationEngine 或 findChild() 等方法获取 QML 中的对象并与之交互。
  • 信号和槽:C++ 可以连接 QML 中的信号与槽,从而实现 C++ 与 QML 之间的交互。
  • 属性访问:C++ 可以访问并修改 QML 对象的属性。
3.及时数据通报


  • Q_PROPERTY 和 信号槽: 用于简单的及时数据通报,尤其是数据较少或更新频率较低的场景。
  • QQmlContext::setContextProperty: 可以将 C++ 对象袒露给 QML,QML 可以通过信号与槽获取及时数据。
  • QTimer: 用于定期更新数据,可以联合其他方式实现及时数据通报。
  • 网络通信(QWebSocket, QNetworkAccessManager): 用于跨进程或跨机器的及时数据通信。
    1. 基本上就是信号和槽结合定时器实现实时数据传递,
    2. 网络通讯的特殊场景提供简单示例仅供参考
    复制代码
(1) 使用 QTimer 和信号和槽进行及时更新

参考开篇第一个串口示例即可;
(2) 使用 QWebSocket 或 QNetworkAccessManager 实现及时数据通信



  • 通过 WebSocket 或网络通信协议可以实现跨进程、跨机器的及时数据传输。
示例(QWebSocket)
C++ 后端:
  1. #include <QWebSocketServer>
  2. #include <QWebSocket>
  3. class WebSocketServer : public QObject {
  4.     Q_OBJECT
  5. public:
  6.     WebSocketServer(QObject *parent = nullptr) : QObject(parent) {
  7.         server = new QWebSocketServer("Real-Time Server", QWebSocketServer::NonSecureMode, this);
  8.         connect(server, &QWebSocketServer::newConnection, this, &WebSocketServer::onNewConnection);
  9.         server->listen(QHostAddress::Any, 12345);
  10.     }
  11. private slots:
  12.     void onNewConnection() {
  13.         QWebSocket *socket = server->nextPendingConnection();
  14.         connect(socket, &QWebSocket::textMessageReceived, this, &WebSocketServer::onMessageReceived);
  15.     }
  16.     void onMessageReceived(const QString &message) {
  17.         qDebug() << "Received message:" << message;
  18.     }
  19. private:
  20.     QWebSocketServer *server;
  21. };
复制代码
QML 前端(WebSocket)
  1. import QtQuick 2.15
  2. import QtQuick.Controls 2.15
  3. import QtWebSockets 1.2
  4. ApplicationWindow {
  5.     visible: true
  6.     width: 640
  7.     height: 480
  8.     WebSocket {
  9.         id: socket
  10.         url: "ws://localhost:12345"
  11.         onTextMessageReceived: {
  12.             console.log("Received message: " + message);
  13.         }
  14.         Component.onCompleted: {
  15.             socket.open();
  16.         }
  17.     }
  18. }
复制代码
总结

  • Q_PROPERTY 和 信号槽: 用于简单的及时数据通报,尤其是数据较少或更新频率较低的场景。
  • QQmlContext::setContextProperty: 可以将 C++ 对象袒露给 QML,QML 可以通过信号与槽获取及时数据。
  • QTimer: 用于定期更新数据,可以联合其他方式实现及时数据通报。
  • 网络通信(QWebSocket, QNetworkAccessManager): 用于跨进程或跨机器的及时数据通信。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

慢吞云雾缓吐愁

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表