Qt5。在 QML 中嵌入 QWidget 对象
Posted
技术标签:
【中文标题】Qt5。在 QML 中嵌入 QWidget 对象【英文标题】:Qt5. Embed QWidget object in QML 【发布时间】:2012-10-22 15:14:17 【问题描述】:我正在使用 Qt5 beta 并尝试将基于 QWidget 的对象嵌入到 QML 中。目标是尽可能多地使用 QML,并且只在 QML 不能满足我需要的地方使用 QWidget 对象。我找到了一个解释如何为 Qt4.7 执行此操作的链接,但我没有找到任何解释如何在 Qt5 中执行此操作的信息。
http://doc.qt.digia.com/4.7/declarative-cppextensions-qwidgets.html
同样的示例也可以在 Qt5 示例文件夹中找到:
示例\qtquick1\declarative\cppextensions\qwidgets
不幸的是,这个例子使用的是 QtQuick 1,而不是 QtQuick 2,我想使用 Qt5 的新特性。我实际上想嵌入一个 qwt 小部件,但作为第一步,我很乐意嵌入任何基于 QWidget 的简单对象。
任何人都可以帮助我获得在 Qt5 / QtQuick 2 下运行的示例吗?
【问题讨论】:
一些相关新闻:QtQuickWidget解决了这个问题的逆向;也就是说,将 Qt Quick 项目嵌入到小部件中。它将在 Qt 5.3 中可用。 【参考方案1】:Qt Quick 2 使用场景图在 GPU 上进行高效渲染。不幸的是,这使得将经典小部件嵌入场景中变得不可能。在 QGraphicsProxyWidget
的帮助下嵌入此类小部件的旧方法仅适用于 Qt Quick 1,因为在内部它使用 QGraphicsView
来完成所有繁重的工作,而 QGraphicsProxyWidget
旨在与它一起使用。
到目前为止,我还没有计划将经典的 QWidgets 嵌入到我所知道的场景图中。我认为这不太可能改变,因为 QPainter 的概念、用于经典小部件的绘画框架和新的场景图不能很好地相互配合。
为了满足 QML 的需求而专门开发了一些新的小部件集,但没有一个像经典小部件那样强大和成熟。最突出的是QML Quick Controls,自 5.1 版以来与 Qt 捆绑在一起。
如果您真的依赖 QWT,我的建议是暂时坚持使用 Qt Quick 1.1。它仍然与 Qt 5 捆绑在一起,可能适用于像您这样的情况。不过,这样您就不会利用新的场景图。
【讨论】:
感谢您非常清楚的解释。我的项目还很新,所以我还没有完全致力于 QWT。 更新:自发布此答案以来,纯 QML 小部件(又名组件)已经成熟了很多。 -1:将常规 QWidgets 嵌入到 Qt Quick 2 场景中是“不可能的”,这是完全错误的。这不仅是可能的,而且效果也很好。我在我的应用程序中这样做,性能非常好。有关示例,请参阅this 和 this 答案和此 repository。【参考方案2】:可以做的是将小部件渲染为图像并作为纹理上传。对于交互,有人需要从sceneGraph转发mouseClick或keyPressed等事件,转换为小部件坐标,传递,渲染并再次上传纹理。只是一个想法:)
【讨论】:
没错。但这种方法需要一个有创造力和大胆的个人。【参考方案3】:您可以使用 QQuickPaintedItem 类将 QWidget 嵌入到 QML: http://doc.qt.io/qt-5/qquickpainteditem.html
Qt5 有一个例子: http://doc.qt.io/qt-5/qtquick-customitems-painteditem-example.html
您应该使用您想要嵌入的私有小部件属性实现 QQuickPaintedItem 的固有属性。提供paint方法,只渲染QtWidget,并提供鼠标等事件从QQuickPaintedItem的继承传递到嵌入QtWidget。
还有 QSG(Qt 场景图 API),但我在这方面的体验并不顺利。我相信多线程中的线索(在不同的线程中执行渲染(不是 Qt GUI 线程,但是在 Windows 上这不是真的,所有都在主 GUI 线程中完成)。
我已经实现了 QCustomPlot 的嵌入,这里是链接:github.com/mosolovsa/qmlplot
【讨论】:
【参考方案4】:推荐的方法是使用基于 QWidget 的应用程序并使用 QWidget::createWindowContainer 嵌入 QML 部分。
【讨论】:
-1:即使我同意你的观点,这也不能回答问题。问题是关于在 QML 场景中嵌入 QWidget。虽然保持应用程序基于 QWidget 通常更好,但您可能仍希望在 QML 视图中嵌入 QWidget(例如,我的应用程序就是这种情况)。【参考方案5】:进一步 Julien 的回答 - 实现此目的的一个简单方法是使用 QQuickWidget 显示 QML 场景,然后添加一个常规 QWidget 作为 QQuickWidget 的子级。您还可以添加一个简单的中间 QObject 以将 QWidget 锚定到场景中的项目。
例如:
在 main.qml 中:
Item
... // layouts, extra items, what have you
Item
objectName: "layoutItem"
anchors.fill: parent
... // more layouts, extra items, etc.
widgetanchor.h:
class WidgetAnchor: public QObject
ptr<QWidget> _pWidget;
QPointer<QQuickItem> _pQuickItem;
public:
WidgetAnchor(QWidget* pWidget, QQuickItem* pItem)
: QObject(pWidget), _pWidget(pWidget), _pQuickItem(pItem)
connect(_pQuickItem, &QQuickItem::xChanged, this, &WidgetAnchor::updateGeometry);
connect(_pQuickItem, &QQuickItem::yChanged, this, &WidgetAnchor::updateGeometry);
connect(_pQuickItem, &QQuickItem::widthChanged, this, &WidgetAnchor::updateGeometry);
connect(_pQuickItem, &QQuickItem::heightChanged, this, &WidgetAnchor::updateGeometry);
updateGeometry();
private:
void updateGeometry()
if (_pQuickItem)
QRectF r = _pQuickItem->mapRectToItem(0, QRectF(_pQuickItem->x(), _pQuickItem->y(), _pQuickItem->width(), _pQuickItem->height()));
_pWidget->setGeometry(r.toRect());
;
在 main.cpp 中:
int main(int argc, char *argv[])
QApplication app(argc, argv);
auto pqw = new QQuickWidget;
pqw->setSource(QUrl::fromLocalFile("main.qml"));
pqw->setResizeMode(QQuickWidget::SizeRootObjectToView);
pqw->setAttribute(Qt::WA_DeleteOnClose);
auto pOwt = new MyWidget(pqw);
if (auto pOverlayItem = pqw->rootObject()->findChild<QQuickItem*>("overlayItem"))
new WidgetAnchor(pOwt, pOverlayItem);
pqw->show();
return app.exec();
文档指出,使用 QQuickWidget 比 QQuickView 和 QWidget::createWindowContainer 具有优势,例如对堆叠顺序没有限制,但有“轻微的性能损失”。
希望对您有所帮助。
【讨论】:
嘿,谢谢!我喜欢你的方法。当应用使用QQmlApplicationEngine而不是QQuickWidget时可以做到吗? 不需要mapRectToItem
并且 ptr 是一个智能指针。总的来说,这是一个很好的方法。
效果很好。对于 Qt6,您需要添加 QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGLRhi);
qt doc以上是关于Qt5。在 QML 中嵌入 QWidget 对象的主要内容,如果未能解决你的问题,请参考以下文章
Q_INVOKABLE 是不是需要在 Qt5 中从 QML 调用公共 QObject 函数?
Qt Qml,在 iOS 上将地图(OSM Mapbox Mapboxgl)嵌入到 QQuickWidget C++ 对象中
使用 QQuickView 或 QQuickWidget 在 QWidget 应用程序中添加大量 QML 对象会带来性能问题