QWidgets 与 Qml 相互嵌入方法,以及其中的一些坑

【写在前面】

        有时候,当我们接手一个老项目( 也可能不老-.- )时,会碰上这些情况:

        1、老项目使用 QWidgets,想要部分或全部迁移到 Qml

        2、老项目使用 Qml,想要部分或全部迁移到 QWidgets

        然而,很多时候全部迁移几乎不现实,此时,折中的方案应运而生:QWidgets 和 Qml 相互嵌套使用。

        本篇主要内容:

        1、Qml 嵌入 QWidgets 中的方法及一些坑;

        2、QWidgets 嵌入 Qml 中的方法及一些坑;


【正文开始】

        Qml 嵌入 QWidgets 中相当容易,细数有两种方式:

        1、使用 static QWidget *QWidget::createWindowContainer(QWindow *, QWidget *parent, Qt::WindowFlags flags)。

        这种方式以 QWindow 为参数来创建一个 QWidget 容器,而 QWindow 又是 Qml 的容器。

    QWidget *widget = new QWidget;
    QQuickWindow *quickWindow = new QQuickWindow;
    QWidget *center = QWidget::createWindowContainer(quickWindow, widget);

        此时便已经将 Qml 嵌入QWidget 中,后面所有的 Qml 项在此 quickWindow 中即可。

        另外,QQuickView 继承自 QQuickWindow ,可以根据喜好来使用。

        要将 quickWindow 脱离出来也相当容易,使用 setParent(nullptr) 即可。

        然而,文档中也提到,这种方式有很多缺点:

  • 可能会出现性能问题。

  • 堆叠顺序,可能无法正确处理覆盖问题。

  • 焦点问题,可能无法正确传递焦点。

  • QWidget 使用过多的窗口容器将损坏整个程序的性能。

        综上,这并不是一个很好的嵌入方案。 

        2、推荐方案的关键类为 QQuickWidget

要使用 QQuickWidget,需 QT += quickwidgets

        使用方法也相当简单:

    QQuickWidget *view = new QQuickWidget;
    view->setSource(QUrl::fromLocalFile("QmlComponent.qml"));
    view->show();

        然而,在使用时发现这种方法有一些坑。

        首先来看看代码 QmlComponent.qml:

import QtQuick 2.12

Rectangle {
    width: 100
    height: 100
    color: "#cc00ff00"

    property int count: 0

    Text {
        anchors.centerIn: parent
        text: count
        color: "red"
        font.pointSize: 14
    }

    MouseArea {
        anchors.fill: parent
        propagateComposedEvents: true
        onClicked: {
            print('click');
            ++parent.count;
            //mouse.accepted = false;
        }
        onPressed: {
            parent.x += 1;
            parent.y += 1;
            //mouse.accepted = false;
        }
        onReleased: {
            parent.x -= 1;
            parent.y -= 1;
            mouse.accepted = false;
        }
    }
}

        main.cpp:

#include <QApplication>
#include <QLabel>
#include <QWidget>
#include <QQuickWidget>

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

protected:
    virtual void mousePressEvent(QMouseEvent *event);
    virtual void mouseReleaseEvent(QMouseEvent *event);
};

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    resize(800, 600);

    QLabel *label = new QLabel("text", this);
    auto s = (size() - label->size()) / 2;
    label->move(s.width(), s.height());
    label->show();

    QQuickWidget *qmlWidget = new QQuickWidget(QUrl("qrc:/QmlComponent.qml"), this);
    qmlWidget->setAttribute(Qt::WA_AlwaysStackOnTop);
    qmlWidget->setClearColor(Qt::transparent);
    qmlWidget->setResizeMode(QQuickWidget::SizeViewToRootObject);
    s = (size() - qmlWidget->size()) / 2;
    qmlWidget->move(s.width(), s.height());
    qmlWidget->show();

    update();
}

Widget::~Widget()
{

}

void Widget::mousePressEvent(QMouseEvent *event)
{
    qDebug() << "Widget mosue press event";
    event->ignore();
}

void Widget::mouseReleaseEvent(QMouseEvent *event)
{
    qDebug() << "Widget mosue release event";
    event->ignore();
}

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    Widget w;
    w.show();

    app.exec();
}

#include "main.moc"

        具体有两个大坑:

  • 背景透明:

        将其他小部件放在下面并使 QQuickWidget 透明不会导致预期的结果:下面的小部件将不可见。

        原因是 QQuickWidget 是在所有其他常规的非 OpenGL 小部件之前绘制的。

        解决方案是:将其置于顶层并设置清除颜色:

    qmlWidget->setAttribute(Qt::WA_AlwaysStackOnTop);
    qmlWidget->setClearColor(Qt::transparent);
  • 事件传播:

        如果 Qml 中使用了 MouseArea,则一些事件不能正常传播:

        例如 clicked released

        另一方面,传递了 pressed 后,自身的 ( MouseArea ) 将无法接受到 released 事件。

        这个坑目前没有找到好的解决方案 ( Ծ‸ Ծ  )。

        QWidgets 嵌入 Qml 中则更加简单:

        1、使用 QWidget 的构造函数,传入父 WidgetQWidget::QWidget(QWidget *parent)

        2、或者使用 QWidget::setParent() 设置父 Widget,本质与上面一样。

        由于 QQuickWidget 继承自 QWidget,因此可以直接成为其他 QWidget 的父 Widget

    QQuickWidget *qmlWidget = new QQuickWidget(QUrl("qrc:/QmlComponent.qml"));
    qmlWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
    qmlWidget->resize(800, 600);
    qmlWidget->show();

    QLabel *label = new QLabel("text", qmlWidget);
    label->show();

        注意:此时添加的 QWidget 将会处于 QQuickWidget 上方。


【结语】

        呼~最后,虽然 Qml QWidgets 相互嵌套有不少坑,但是在一些场合,确实是相当有用的。

评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦起丶

您的鼓励和支持是我创作最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值