目前QT官方主推的界面开发方式是采用QML进行界面设计,但在QML未流行之前,很多的项目都是采用QWidget开发的,把之前的代码全部转换为QML代码显然工作量非常大,如果能将QWidget窗口嵌入到QML界面中,那么开发效率将提高很多,且QML无法实现的功能也能借助于QWidget框架进行实现。
本篇文章将介绍一种方式来实现QML和QWidget的混合开发
其中,参考了两篇文章:
第一篇:震惊!QWidget竟然可以嵌入到QML中,QMl窗口句柄竟然是这样获取,这篇文章能够成功将QWidget窗口嵌入到QML界面中,但无法实现QWidget窗口的的任意布局。
第二篇:在QML 中,嵌入QWidget 对象,这篇文章介绍了另一种思路,按照其说法是可以实现QWidget窗口在QML界面中任意布局的,但其博客里提供的代码有残缺,真正移植到自己的项目中还是无法实现功能。
所以,本文章结合上述两位大佬的思路,融合者两种方法,实现了预期效果。
效果展示:
QML_QWidget
整体的项目框架如图所示:因为要使用到QWidget,所以要在项目的.pro文件中加入quickwidgets库:QT += quick quickwidgets
既然是要将QWidget窗口嵌入到QML界面中,那么先创建一个QWidget窗口,使用自带的Qt Designer设计大师进行简单布局,在其中放置一个tabWidget控件,简单设置一下样式,如下图所示:
这里只是简单的设计了一下QWdiget的窗口,并没有实现什么功能,所以代码相对简单,如下所示:
unitywidget.h:
#ifndef UNITYWIDGET_H
#define UNITYWIDGET_H#include namespace Ui {
class unityWidget;
}class unityWidget : public QWidget
{Q_OBJECTpublic:explicit unityWidget(QWidget *parent = nullptr);~unityWidget();private:Ui::unityWidget *ui;
};#endif // UNITYWIDGET_H
unitywidget.cpp:
#include "unitywidget.h"
#include "ui_unitywidget.h"unityWidget::unityWidget(QWidget *parent) :QWidget(parent),ui(new Ui::unityWidget)
{ui->setupUi(this);
}unityWidget::~unityWidget()
{delete ui;
}
因为想要在QML界面中像其他控件一样使用锚布局对QWdiget窗口进行布局,但是直接加进来的QWidget窗口是无法实现的,所以要自定义一个辅助类,这个辅助类就是专门对QWdiget窗口进行布局的,该类的构造函数需要两个参数,一个是要加载到QML界面中的QWdiget窗口,另一个是QML界面中的一个Item控件,这个Item控件将会成为QWdiget窗口的父对象,在QML界面中对这个Item布局,即可间接实现对QWdiget窗口布局。辅助类的代码如下:
WdigetAnchorHelper.h:
#ifndef WDIGETANCHORHELPER_H
#define WDIGETANCHORHELPER_H#include
#include
#include
#include class WdigetAnchorHelper : public QObject
{Q_OBJECTpublic:explicit WdigetAnchorHelper(QWidget* mWidget, QQuickItem* mItem);~WdigetAnchorHelper();private://实时更新widge的位置void updateGeometry();private://QPointer用于创建一个指针 (效果等同于 QWidget* _mWidgetQPointer _mWidget;QPointer _mQuickItem;
};#endif // WDIGETANCHORHELPER_H
WdigetAnchorHelper.cpp:
#include "wdigetanchorhelper.h"WdigetAnchorHelper::WdigetAnchorHelper(QWidget* mWidget, QQuickItem* mItem):_mWidget(mWidget), _mQuickItem(mItem)
{//QML中的Item相关属性发生变化时,执行更新槽函数connect(_mQuickItem, &QQuickItem::xChanged, this, &WdigetAnchorHelper::updateGeometry);connect(_mQuickItem, &QQuickItem::yChanged, this, &WdigetAnchorHelper::updateGeometry);connect(_mQuickItem, &QQuickItem::widthChanged, this, &WdigetAnchorHelper::updateGeometry);connect(_mQuickItem, &QQuickItem::heightChanged, this, &WdigetAnchorHelper::updateGeometry);//初始化位置updateGeometry();
}WdigetAnchorHelper::~WdigetAnchorHelper()
{}void WdigetAnchorHelper::updateGeometry()
{if (_mQuickItem){_mWidget->setGeometry(_mQuickItem->x(),_mQuickItem->y(),_mQuickItem->width(),_mQuickItem->height());}
}
在main.qml文件中,在根节点Window内添加一个Item控件,这个控件是用来装载即将加载进来的QWidget窗口的,本代码中,左部分显示一张图片,右部分显示QWidget窗口,代码如下所示:
import QtQuick 2.15
import QtQuick.Window 2.15Window {width: 500height: 300visible: truetitle: qsTr("QML QWidget")Image{width: parent.width/2height: parent.heightanchors.left: parent.leftsource: "qrc:/images/katong.jfif"}Item{objectName: "widgetItem"width: parent.width/2height: parent.heightanchors.right: parent.right}
}
在项目的main.cpp中,需要获取QML中的根元素以及相关控件对象,并设置自定义的窗口句柄和其父窗口,一些其他的注意事项和具体解释详看代码注释,整体代码如下:
#include
#include
#include
#include
#include
#include #include "unitywidget.h"
#include "wdigetanchorhelper.h"int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif//创建项目时默认使用的是QGuiApplication,但因为要嵌入QWidget窗口//所以要使用QApplicationQApplication 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);engine.load(url);QObject *QmlObj = engine.rootObjects().at(0);//获取QML当中的根对象QWindow *QmlWindow = qobject_cast(QmlObj);//将跟对象转换成QWindow类型WId parentHWND = QmlWindow->winId();//获取Qml窗口的句柄QQuickItem* unityItem = QmlObj->findChild("widgetItem");//获取QML中用于装载widget的ItemunityWidget* mUnityWidget = new unityWidget(); //自定义widget窗体mUnityWidget->setProperty("_q_embedded_native_parent_handle",QVariant(parentHWND));//给widget父对象句柄赋值mUnityWidget->winId();//必须调用,才能为widget创建句柄,否则将会失败mUnityWidget->windowHandle()->setParent(QmlWindow);//通过窗口句柄设置父窗口new WdigetAnchorHelper(mUnityWidget, unityItem);//将widget和Item加入自定义辅助类中,管理widget在QML中的布局mUnityWidget->show();//在QML界面中显示自定义widget窗口return app.exec();
}