qml界面和c++后端程序交互
1.C++ 数据袒露给qml进行交互
注意事项:
- 要想c++类传递到qml,必须是QObject类型的;如果是QWidget或者其他界面类型的c++类则不生效
复制代码 (1)setContextProperty方式(在c++端实例化对象)
serialportmanager.h
- #ifndef SERIALPORTMANAGER_H
- #define SERIALPORTMANAGER_H
- #include <QObject>
- #include <QStringList>
- #include <QSerialPortInfo>
- #include <QTimer>
- #include <QDebug>
- class SerialPortManager : public QObject
- {
- Q_OBJECT
- Q_PROPERTY(QStringList portList READ portList NOTIFY portListChanged)
- public:
- explicit SerialPortManager(QObject *parent = nullptr);
- QStringList portList() const;
- signals:
- void portListChanged(); // 当串口列表更新时发送信号
- public slots:
- void updatePortList(); // 更新串口列表
- private:
- QStringList m_portList; // 存储当前串口列表
- QTimer *m_timer; // 用于定时更新串口列表
- bool m_isFirstUpdate = true; // 标志是否是第一次获取串口列表
- };
- #endif // SERIALPORTMANAGER_H
复制代码 重点:
serialportmanager.cpp
- #include "serialportmanager.h"
- #include <QSerialPortInfo>
- #include <QTimer>
- SerialPortManager::SerialPortManager(QObject *parent)
- : QObject(parent), m_timer(new QTimer(this)), m_isFirstUpdate(true)
- {
- // 初始时立即获取可用串口列表,并发送信号
- updatePortList();
- // 设置定时器每秒更新一次串口列表
- connect(m_timer, &QTimer::timeout, this, &SerialPortManager::updatePortList);
- m_timer->start(1000); // 每1000毫秒更新一次串口列表
- }
- QStringList SerialPortManager::portList() const {
- return m_portList;
- }
- void SerialPortManager::updatePortList() {
- QStringList ports;
- // 获取所有可用的串口并更新到 ports 列表中
- foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
- ports.append(info.portName());
- }
- // 对串口列表进行排序,确保是从小到大的顺序
- ports.sort();
- // 第一次获取串口列表时,直接发出信号
- if (m_isFirstUpdate) {
- m_portList = ports;
- emit portListChanged(); // 发出串口列表已更新的信号
- qDebug() << "First update, signal emitted.";
- m_isFirstUpdate = false; // 标记为非第一次更新
- }
- else {
- // 如果串口列表发生变化,则更新并发出信号
- if (ports != m_portList) {
- m_portList = ports;
- emit portListChanged(); // 发出串口列表已更新的信号
- qDebug() << "List updated, signal emitted.";
- }
- }
- }
复制代码
main.cpp
- #include <QGuiApplication>
- #include <QQmlApplicationEngine>
- #include <QGuiApplication>
- #include <QQmlApplicationEngine>
- #include <QQmlContext>
- #include "SerialPortManager.h"
- int main(int argc, char *argv[])
- {
- QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
- QGuiApplication app(argc, argv);
- QQmlApplicationEngine engine;
- const QUrl url(QStringLiteral("qrc:/main.qml"));
- QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
- &app, [url](QObject *obj, const QUrl &objUrl) {
- if (!obj && url == objUrl)
- QCoreApplication::exit(-1);
- }, Qt::QueuedConnection);
- // 创建 SerialPortManager 实例
- SerialPortManager serialPortManager;
- // 将 SerialPortManager 实例暴露给 QML
- engine.rootContext()->setContextProperty("serialPortManager", &serialPortManager);
- // 加载 QML 文件
- engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
- return app.exec();
- }
复制代码
main.qml
- import QtQuick 2.12
- import QtQuick.Window 2.12
- import QtQuick.Controls 2.12
- //import QtQuick.Dialogs 1.12
- ApplicationWindow {
- visible: true
- width: 640
- height: 480
- title: "Serial Port List"
- ListView {
- anchors.fill: parent
- model: ListModel {
- // 绑定到 SerialPortManager 的 portList
- ListElement { portName: "COM1" }
- ListElement { portName: "COM2" }
- }
- delegate: Item {
- width: parent.width
- height: 50
- Text {
- anchors.centerIn: parent
- text: portName
- }
- }
- // 定义一个更新串口列表并刷新模型的函数
- function updateSerialPortList() {
- // console.log("portListChanged 信号已触发");
- // 清空现有模型
- model.clear();
- // console.log("模型已清空");
- // 打印获取到的可用串口列表
- console.log("获取到的串口列表: " + serialPortManager.portList.join(", "));
- // 更新模型,填充新的串口列表
- for (var i = 0; i < serialPortManager.portList.length; i++) {
- console.log("串口名称: " + serialPortManager.portList[i]);
- model.append({ portName: serialPortManager.portList[i] });
- }
- // console.log("模型更新完毕");
- }
- Component.onCompleted: {
- // console.log("Component.onCompleted: 组件已完成加载");
- // 检查 serialPortManager 是否已初始化
- if (serialPortManager && serialPortManager.portList) {
- // console.log("serialPortManager 已初始化,开始处理 portList");
- // 连接 portListChanged 信号到独立的更新函数
- serialPortManager.portListChanged.connect(updateSerialPortList);
- // 主动调用 C++ 端获取串口列表
- // console.log("调用 C++ 端 updatePortList 获取串口列表");
- updateSerialPortList();
- } else {
- // 如果 C++ 端尚未初始化,打印提示
- console.log("后端程序尚未初始化");
- }
- }
- }
- }
复制代码
征象:
初始状态:

串口变化时:

总结:
- c++注册并实例化对象; 发送信号
- qml界面connect信号并实现槽函数响应;
- 注册后的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++ 类,包罗一个整数属性和一个信号,用来更新计数器的值。
- cppCopy Code// counter.h
- #ifndef COUNTER_H
- #define COUNTER_H
- #include <QObject>
- class Counter : public QObject
- {
- Q_OBJECT
- Q_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged)
- public:
- explicit Counter(QObject *parent = nullptr);
- int count() const;
- void setCount(int newCount);
- signals:
- void countChanged();
- private:
- int m_count;
- };
- #endif // COUNTER_H
- cppCopy Code// counter.cpp
- #include "counter.h"
- Counter::Counter(QObject *parent) : QObject(parent), m_count(0)
- {
- }
- int Counter::count() const
- {
- return m_count;
- }
- void Counter::setCount(int newCount)
- {
- if (m_count != newCount) {
- m_count = newCount;
- emit countChanged();
- }
- }
复制代码 3.2 注册 C++ 类到 QML
在应用程序的入口文件中(通常是 main.cpp),我们通过 qmlRegisterType 注册 C++ 类到 QML 环境中。
- cppCopy Code#include <QGuiApplication>
- #include <QQmlApplicationEngine>
- #include <QQmlContext>
- #include "counter.h"
- int main(int argc, char *argv[])
- {
- QGuiApplication app(argc, argv);
- // 注册 C++ 类到 QML
- qmlRegisterType<Counter>("com.example", 1, 0, "Counter");
- QQmlApplicationEngine engine;
- // 加载 QML 文件
- engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
-
- return app.exec();
- }
复制代码 在这里,qmlRegisterType<Counter>("com.example", 1, 0, "Counter"); 将 Counter 类注册到 QML 中,并指定了模块名为 com.example,版本为 1.0,QML 中对应的类型名为 Counter。
3.3 在 QML 中使用 C++ 类
接下来,在 QML 中创建一个 Counter 对象,并与其进行交互。我们可以使用 Text 元向来显示 count 属性,并使用按钮来更新计数器的值。
- qmlCopy Code// main.qml
- import QtQuick 2.15
- import QtQuick.Controls 2.15
- import com.example 1.0
- ApplicationWindow {
- visible: true
- width: 640
- height: 480
- // 创建 Counter 对象
- Counter {
- id: counter
- }
- Column {
- anchors.centerIn: parent
- Text {
- text: "Count: " + counter.count
- font.pixelSize: 32
- }
- Button {
- text: "Increment"
- onClicked: counter.setCount(counter.count + 1)
- }
- }
- }
复制代码 在这里,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++ 代码中访问并修改它。
- // Main.qml
- import QtQuick 2.15
- import QtQuick.Controls 2.15
- ApplicationWindow {
- visible: true
- width: 640
- height: 480
- title: "QML to C++ Example"
- Text {
- id: label
- text: "Hello from QML!"
- anchors.centerIn: parent
- }
- }
复制代码 在 C++ 中,我们可以通过以下代码获取并修改这个 Text 对象的 text 属性。
- // main.cpp
- #include <QCoreApplication>
- #include <QQmlApplicationEngine>
- #include <QQmlContext>
- #include <QQuickItem>
- int main(int argc, char *argv[])
- {
- QCoreApplication app(argc, argv);
- QQmlApplicationEngine engine;
- // 加载 QML 文件
- engine.load(QUrl(QStringLiteral("qrc:/Main.qml")));
- // 获取根对象
- QObject *rootObject = engine.rootObjects().first();
- // 查找 QML 中的 Text 对象
- QObject *labelObject = rootObject->findChild<QObject*>("label");
- if (labelObject) {
- // 修改 QML 中的 Text 对象的 text 属性
- labelObject->setProperty("text", "Modified by C++!");
- }
- return app.exec();
- }
复制代码 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++ 能够接收到这个信号并进行处理:
- // Main.qml
- import QtQuick 2.15
- import QtQuick.Controls 2.15
- ApplicationWindow {
- visible: true
- width: 640
- height: 480
- title: "Signal from QML to C++"
- Button {
- text: "Click Me"
- onClicked: {
- console.log("Button clicked!")
- }
- }
- }
复制代码 在 C++ 中,我们可以连接 QML 中的 onClicked 信号:
- // main.cpp
- #include <QCoreApplication>
- #include <QQmlApplicationEngine>
- #include <QQmlContext>
- #include <QQuickItem>
- class MyObject : public QObject {
- Q_OBJECT
- public:
- MyObject() {}
- public slots:
- void onButtonClicked() {
- qDebug() << "Button clicked in QML!";
- }
- };
- int main(int argc, char *argv[])
- {
- QCoreApplication app(argc, argv);
- QQmlApplicationEngine engine;
- // 创建 C++ 对象
- MyObject myObject;
- // 加载 QML 文件
- engine.load(QUrl(QStringLiteral("qrc:/Main.qml")));
- // 获取根对象
- QObject *rootObject = engine.rootObjects().first();
- // 查找 QML 中的 Button 对象
- QObject *buttonObject = rootObject->findChild<QObject*>("button");
- if (buttonObject) {
- // 连接 QML 信号到 C++ 槽
- QObject::connect(buttonObject, SIGNAL(clicked()), &myObject, SLOT(onButtonClicked()));
- }
- return app.exec();
- }
复制代码 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) 使用 QTimer 和信号和槽进行及时更新
参考开篇第一个串口示例即可;
(2) 使用 QWebSocket 或 QNetworkAccessManager 实现及时数据通信
- 通过 WebSocket 或网络通信协议可以实现跨进程、跨机器的及时数据传输。
示例(QWebSocket):
C++ 后端:
- #include <QWebSocketServer>
- #include <QWebSocket>
- class WebSocketServer : public QObject {
- Q_OBJECT
- public:
- WebSocketServer(QObject *parent = nullptr) : QObject(parent) {
- server = new QWebSocketServer("Real-Time Server", QWebSocketServer::NonSecureMode, this);
- connect(server, &QWebSocketServer::newConnection, this, &WebSocketServer::onNewConnection);
- server->listen(QHostAddress::Any, 12345);
- }
- private slots:
- void onNewConnection() {
- QWebSocket *socket = server->nextPendingConnection();
- connect(socket, &QWebSocket::textMessageReceived, this, &WebSocketServer::onMessageReceived);
- }
- void onMessageReceived(const QString &message) {
- qDebug() << "Received message:" << message;
- }
- private:
- QWebSocketServer *server;
- };
复制代码 QML 前端(WebSocket):
- import QtQuick 2.15
- import QtQuick.Controls 2.15
- import QtWebSockets 1.2
- ApplicationWindow {
- visible: true
- width: 640
- height: 480
- WebSocket {
- id: socket
- url: "ws://localhost:12345"
- onTextMessageReceived: {
- console.log("Received message: " + message);
- }
- Component.onCompleted: {
- socket.open();
- }
- }
- }
复制代码 总结
- Q_PROPERTY 和 信号槽: 用于简单的及时数据通报,尤其是数据较少或更新频率较低的场景。
- QQmlContext::setContextProperty: 可以将 C++ 对象袒露给 QML,QML 可以通过信号与槽获取及时数据。
- QTimer: 用于定期更新数据,可以联合其他方式实现及时数据通报。
- 网络通信(QWebSocket, QNetworkAccessManager): 用于跨进程或跨机器的及时数据通信。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |