一、屬性繫結
這是最簡單的方式,可以在QML中直接繫結C++ 物件的屬性。透過在C++ 物件中使用Q_PROPERTY宏定義屬性,然後在QML中使用繫結語法將屬性與QML元素關聯起來。
-
person.h
#include <QObject> class Person : public QObject { Q_OBJECT /* 使用 Q_PROPERTY 定義互動的屬性 */ Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameChanged) Q_PROPERTY(int age READ getAge WRITE setAge NOTIFY ageChanged) public: explicit Person(QObject *parent = nullptr) : QObject(parent), m_name(""), m_age(0) { } /* 為屬性提供 getter 和 setter 方法 */ QString getName() const { return m_name; } void setName(const QString& name) { m_name = name; emit nameChanged(); } int getAge() const { return m_age; } void setAge(int age) { m_age = age; emit ageChanged(); } signals: /* 訊號與屬性對應,透過訊號通知其他物件屬性的變化 */ void nameChanged(); void ageChanged(); private: QString m_name; int m_age; };
-
main.cpp
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQmlContext> #include "person.h" int main(int argc, char *argv[]) { /* 啟用Qt應用程式的高DPI縮放功能 */ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); /* 建立一個Qt應用程式的例項 */ QGuiApplication app(argc, argv); // 建立Person物件 Person person; QQmlApplicationEngine engine; /* 將Person物件作為QML上下文屬性 */ engine.rootContext()->setContextProperty("person", &person); const QUrl url(QStringLiteral("qrc:/main.qml")); /* 將 QQmlApplicationEngine 物件的 objectCreated 訊號連線到一個 lambda 函式上 */ /* lambda 函式用於在 QML 檔案中的根物件被建立時進行處理,檢查物件是否成功建立,如果建立失敗則退出應用程式 */ QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); /* 載入QML檔案並顯示使用者介面 */ engine.load(url); return app.exec(); }
-
main.qml
import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 2.5 Window { visible: true width: 480 height: 800 title: qsTr("Hello World") Column { spacing: 10 TextField { placeholderText: "請輸入姓名" text: person.name // 與Person物件的name屬性繫結 onTextChanged: person.name = text // 當文字改變時,更新Person物件的name屬性 } Slider { from: 0 to: 100 value: person.age // 與Person物件的age屬性繫結 onValueChanged: person.age = value // 當滑塊值改變時,更新Person物件的age屬性 } Text { text: "姓名:" + person.name } Text { text: "年齡:" + person.age } } }
二、訊號與槽
C++ 物件可以發出訊號,而QML中的元素可以連線到這些訊號上。這樣,當C++ 物件的狀態發生變化時,可以透過訊號與槽機制將這些變化傳遞給QML介面。
-
myobject.h
#include <QObject> #include <QtDebug> class MyObject : public QObject { Q_OBJECT public: explicit MyObject(QObject *parent = nullptr) : QObject(parent) {} signals: void mySignal(QString message); public slots: void mySlot(const QString& message) { qDebug() << "Received message from QML:" << message; emit mySignal("Hello from C++");} };
-
main.cpp
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQmlContext> #include "myobject.h" int main(int argc, char *argv[]) { /* 啟用Qt應用程式的高DPI縮放功能 */ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); /* 建立一個Qt應用程式的例項 */ QGuiApplication app(argc, argv); /* 將自定義 C++ 型別註冊到 QML 中的函式, 將自定義 C++ 型別註冊到 QML 中的函式 */ qmlRegisterType<MyObject>("com.example", 1, 0, "MyObject"); QQmlApplicationEngine engine; const QUrl url(QStringLiteral("qrc:/main.qml")); /* 將 QQmlApplicationEngine 物件的 objectCreated 訊號連線到一個 lambda 函式上 */ /* lambda 函式用於在 QML 檔案中的根物件被建立時進行處理,檢查物件是否成功建立,如果建立失敗則退出應用程式 */ QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); /* 載入QML檔案並顯示使用者介面 */ engine.load(url); return app.exec(); }
-
main.qml
import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 2.5 import com.example 1.0 Window { visible: true width: 480 height: 800 title: qsTr("Hello World") /* 定義 sendToCpp 訊號 */ signal sendToCpp(string message) /* Connections 元件用於連線 myObject 的 onMySignal 訊號 */ Connections { target: myObject onMySignal: console.log("Received message from C++:", message) } MyObject { id: myObject /* 將 onMySignal 訊號傳遞到 sendToCpp訊號上,便於 QML 處理 */ onMySignal: sendToCpp(message) } Button { text: "Send message to C++" anchors.centerIn: parent /* 單擊按鈕時,會將訊號傳遞到 C++ 的 mySlot 槽上 */ onClicked: myObject.mySlot("Hello from QML") } }
三、模型檢視
模型檢視(Model-View):可以使用C++ 中的資料模型(QStandardItemModel)來提供資料給QML介面。QML中的檢視元素(如ListView或GridView)可以使用這些模型來顯示資料。
-
mymodel.h
#ifndef MYMODEL_H #define MYMODEL_H #include <QAbstractListModel> #include <QList> class MyModel : public QAbstractListModel { Q_OBJECT public: explicit MyModel(QObject *parent = nullptr); enum { NameRole = Qt::UserRole + 1, AgeRole, EmailRole }; // 重寫以下幾個虛擬函式 int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QHash<int, QByteArray> roleNames() const override; private: struct Person { QString name; int age; QString email; }; QList<Person> m_persons; }; #endif // MYMODEL_H
-
mymodel.cpp
#include "mymodel.h" MyModel::MyModel(QObject *parent) : QAbstractListModel(parent) { // 初始化一些資料 m_persons.append({"Alice", 25, "alice@example.com"}); m_persons.append({"Bob", 30, "bob@example.com"}); m_persons.append({"Charlie", 35, "charlie@example.com"}); } int MyModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return m_persons.count(); } QVariant MyModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (index.row() >= m_persons.count() || index.row() < 0) return QVariant(); const Person &person = m_persons[index.row()]; if (role == NameRole) return person.name; else if (role == AgeRole) return person.age; else if (role == EmailRole) return person.email; return QVariant(); } QHash<int, QByteArray> MyModel::roleNames() const { QHash<int, QByteArray> roles; roles[NameRole] = "name"; roles[AgeRole] = "age"; roles[EmailRole] = "email"; return roles; }
-
main.cpp
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQmlContext> #include "mymodel.h" int main(int argc, char *argv[]) { /* 啟用Qt應用程式的高DPI縮放功能 */ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); /* 建立一個Qt應用程式的例項 */ QGuiApplication app(argc, argv); QQmlApplicationEngine engine; MyModel myModel; engine.rootContext()->setContextProperty("myModel", &myModel); const QUrl url(QStringLiteral("qrc:/main.qml")); /* 將 QQmlApplicationEngine 物件的 objectCreated 訊號連線到一個 lambda 函式上 */ /* lambda 函式用於在 QML 檔案中的根物件被建立時進行處理,檢查物件是否成功建立,如果建立失敗則退出應用程式 */ QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); /* 載入QML檔案並顯示使用者介面 */ engine.load(url); return app.exec(); }
-
main.qml
import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 2.5 Window { visible: true width: 480 height: 800 title: qsTr("Hello World") ListView { anchors.fill: parent model: myModel delegate: Item { width: parent.width height: 60 Column { Text { text: name } Text { text: age } Text { text: email } } } } }
-
執行效果
四、QML型別註冊
QML型別註冊(QML Type Registration):可以將C++ 物件註冊為自定義的QML型別,使得QML可以直接建立和使用這些物件。透過在C++ 中使用 Q_PROPERTY 宏和 Q_INVOKABLE 函式,可以將C++ 類註冊為QML型別。我需要這樣一個案例
-
myobject.h
#include <QQmlEngine> #include "QDebug" class MyObject : public QObject { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) public: explicit MyObject(QObject *parent = nullptr) : QObject(parent) {} QString name() const { return m_name; } void setName(const QString &name) { m_name = name; emit nameChanged(); } Q_INVOKABLE void printName() { qDebug() << "Name:" << m_name; } static void registerQmlType() { qmlRegisterType<MyObject>("com.example", 1, 0, "MyObject"); } signals: void nameChanged(); private: QString m_name; };
-
main.cpp
#include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQmlContext> #include "myobject.h" int main(int argc, char *argv[]) { /* 啟用Qt應用程式的高DPI縮放功能 */ QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); /* 建立一個Qt應用程式的例項 */ QGuiApplication app(argc, argv); QQmlApplicationEngine engine; MyObject::registerQmlType(); const QUrl url(QStringLiteral("qrc:/main.qml")); /* 將 QQmlApplicationEngine 物件的 objectCreated 訊號連線到一個 lambda 函式上 */ /* lambda 函式用於在 QML 檔案中的根物件被建立時進行處理,檢查物件是否成功建立,如果建立失敗則退出應用程式 */ QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); /* 載入QML檔案並顯示使用者介面 */ engine.load(url); return app.exec(); }
-
main.qml
import QtQuick 2.12 import QtQuick.Window 2.12 import QtQuick.Controls 2.5 import com.example 1.0 Window { visible: true width: 480 height: 800 title: qsTr("Hello World") MyObject { id: myObject name: "John" } /* 垂直佈置元件 */ Column { anchors.fill: parent // 大小為父元件的大小 anchors.margins: 40 // 與父元件四周的間隔 spacing: 10 // 子元件之間的間隔 Text { text: myObject.name } Button { text: "Print Name" onClicked: myObject.printName() } } }