简述 在使用 Qt Widgets 时,经常要实现一些比较炫酷的效果(例如:滑动、翻页),这时选择 QML 会显得非常简单。 那么,问题来了: 能不能将 QML 和 Qt Widgets 结合在一起使用? 如果能,都有什么方式? QML 和 Qt Widgets 之间又该如何交互? | 版权声明:一去、二三里,未经博主允许不得转载。 一个简单的 QML 创建了一个简单的 QML 文件,用于显示一个绿色的矩形,其中包含一个文本。为了说明 QML 与 Qt Widgets 的交互,在矩形中添加了两个信号。 import QtQuick 2.1 Rectangle { id: root color: "green" width: 200 height: 200 // 发送给 Qt Widgets 的信号 signal qmlSignal // 从 Qt Widgets 接收到的信号 signal cSignal Text { id: myText text: "Click me" font.pointSize: 14 anchors.centerIn: parent } MouseArea { anchors.fill: parent onClicked: qmlSignal() } // 信号处理程序(处理从 Qt Widgets 接收到的信号) onCSignal: { root.color = "blue" myText.text = "Call the qml signal handler" } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 如果需要,可以将像往常一样使用 qmlscene 来运行。 加载和显示 QML 要加载和显示 QML,可以使用以下两种方式: QQuickWidget QQuickView 无论使用哪种方式,一旦主源文件的 URL 被指定,都将自动加载和显示 QML 视图。 注意: 在 QML运行环境 中,还介绍了另一种方式 - QQmlEngine + QQmlComponent,但是这种方式不能以 view 的形式加载。 使用 QQuickWidget QQuickWidget 是 Qt 5.3 中提供的一个类,继承自 QWidget,它是 QQuickWindow 一个很方便的包装器,用于显示 Qt Quick 用户界面。 使用方式: QQuickWidget *view = new QQuickWidget; view->setSource(QUrl::fromLocalFile("myqmlfile.qml")); view->show(); 1 2 3 使用 QQuickView QQuickView 是 Qt 5.0 中提供的一个类,继承自 QQuickWindow(继承自 QWindow),用于显示 Qt Quick 用户界面。 使用方式: QQuickView *view = new QQuickView; view->setSource(QUrl::fromLocalFile("myqmlfile.qml")); view->show(); 1 2 3 可以看到,两者的使用方式几乎没什么区别。但是,QQuickWidget 基于 QWidget,而 QQuickView 基于 QWindow,所以需要将其转换为 QWidget,才能与 Qt Widgets 相结合。 这样,就需要再引入一个函数: [static] QWidget *QWidget::createWindowContainer(QWindow *window, QWidget *parent = Q_NULLPTR, Qt::WindowFlags flags = Qt::WindowFlags()) 该函数用于创建一个 QWidget,可以将 window 嵌入到基于 QWidget 的应用程序中。 所以,要将 QQuickView 转为 QWidget,可以使用下述方式: QQuickView *view = new QQuickView(); QWidget *widget = QWidget::createWindowContainer(view, this); view->setSource(QUrl("qrc:/main.qml")); 1 2 3 将 QML 与 Qt Widgets 相结合 当一切准备就绪,就可以将 QML 与 Qt Widgets 完美结合在一起了: 最终效果: 源码如下: #include "widget.h" #include #include #include Widget::Widget(QWidget *parent) : QWidget(parent) { resize(300, 300); // 方式一 // QQuickView *pView = new QQuickView(); // QWidget *pWidget = QWidget::createWindowContainer(pView, this); // pView->setResizeMode(QQuickView::SizeRootObjectToView); // pView->setSource(QUrl("qrc:/main.qml")); // 方式二 QQuickWidget *pWidget = new QQuickWidget(); pWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); pWidget->setSource(QUrl("qrc:/main.qml")); m_pButton = new QPushButton(this); m_pButton->setText("Qt Widgets..."); QVBoxLayout *pLayout = new QVBoxLayout(); pLayout->addWidget(pWidget); pLayout->addWidget(m_pButton); pLayout->setSpacing(10); pLayout->setContentsMargins(10, 10, 10, 10); setLayout(pLayout); // QML 与 Qt Widgets 通信 // QObject *pRoot = (QObject*)pView->rootObject(); QObject *pRoot = (QObject*)pWidget->rootObject(); if (pRoot != NULL) { connect(pRoot, SIGNAL(qmlSignal()), this, SLOT(receiveFromQml())); connect(m_pButton, SIGNAL(clicked(bool)), pRoot, SIGNAL(cSignal())); } } void Widget::receiveFromQml() { m_pButton->setText("Call the C++ slot"); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 结合之后,少不了通信,这里采用信号槽的方式,通过 QML 的根元素 root 来发送和接收信号。