如何修复‘Created graphical object was not placed in the graphics scene’错误
我之前踩过一模一样的坑!你碰到的这个QQmlComponent: Created graphical object was not placed in the graphics scene错误,本质是QWidget和Qt Quick的渲染体系完全不兼容导致的——QWidget依赖传统的QWidget层级绘图系统,而QQuickPaintedItem属于Qt Quick的场景图架构,直接把QWidget的内容硬画上去,很容易触发上下文冲突,甚至直接崩溃。
为什么会出现这个错误?
QWidget的绘制逻辑绑定在自身的paintEvent上,它必须属于一个有效的QWidget上下文(比如有父Widget、或者在QApplication的窗口树里)。而你创建的QPushButton如果没有被正确挂载到QWidget层级里,又强行让它参与Qt Quick的渲染,Qt就会检测到这个"无家可归"的图形对象,抛出这个错误,后续的非法内存访问直接导致程序终止。
两种可行的解决办法
方法一:静态渲染QWidget外观(适合只需要展示,不需要交互的场景)
最稳妥的方式是把QWidget渲染到离线的QImage里,再把QImage画到QQuickPaintedItem上,完全隔离两个渲染体系:
- 创建一个离线的QWidget实例(不要给它设置父对象,也不要加入任何QWidget窗口/布局)
- 调用
QWidget::render()把QWidget的内容渲染到QImage - 在QQuickPaintedItem的
paint()方法里绘制这个QImage
示例代码:
#include <QQuickPaintedItem> #include <QPushButton> #include <QPainter> class WidgetPreviewItem : public QQuickPaintedItem { Q_OBJECT public: explicit WidgetPreviewItem(QQuickItem *parent = nullptr) : QQuickPaintedItem(parent) { // 创建离线按钮,不加入任何QWidget层级 m_button = new QPushButton("Sample Button"); m_button->resize(120, 40); // 提前渲染初始图像 updatePreviewImage(); } void paint(QPainter *painter) override { // 把渲染好的图像画到QQuickPaintedItem上 painter->drawImage(rect(), m_previewImage, m_button->rect()); } private slots: // 当QWidget状态变化时(比如文本修改),调用这个方法更新图像 void updatePreviewImage() { m_previewImage = QImage(m_button->size(), QImage::Format_ARGB32_Premultiplied); m_previewImage.fill(Qt::transparent); QPainter imgPainter(&m_previewImage); m_button->render(&imgPainter); // 触发QQuickPaintedItem重绘 update(); } private: QPushButton *m_button; QImage m_previewImage; };
方法二:嵌入可交互的QWidget(需要QWidget能响应用户操作的场景)
如果你的QPushButton需要是可点击、可交互的,静态渲染就满足不了了。这时候可以用QWidget::createWindowContainer()把QWidget包装成Qt Quick可识别的Item,嵌入到QML场景中:
// 假设你已经有了QQuickView或QQuickWidget实例 QQuickView *view = new QQuickView; view->setSource(QUrl("qrc:/main.qml")); QObject *rootObj = view->rootObject(); // 创建QPushButton QPushButton *interactiveBtn = new QPushButton("Click Me!"); interactiveBtn->resize(120, 40); // 把QWidget包装成Qt Quick的容器Item QQuickWidget *container = QWidget::createWindowContainer(interactiveBtn, rootObj); container->setPosition(60, 60); // 设置在QML场景中的位置 container->setSize(interactiveBtn->size()); view->show();
⚠️ 注意:这种方式会让QWidget在Qt Quick场景上单独占用一个窗口层级,可能会有渲染层级、性能损耗的问题,除非必须用QWidget的特定功能,否则优先用Qt Quick原生的Button组件。
避坑提醒
- 绝对不要在QQuickPaintedItem的
paint()方法里直接创建QWidget或调用它的paintEvent,这会导致线程/上下文冲突,大概率崩溃 - 如果QWidget有复杂子控件,
render()会一并渲染,但要确保QImage的尺寸和QWidget完全匹配 - 尽量避免混合QWidget和Qt Quick技术栈,两者的渲染逻辑差异太大,后期维护成本很高
内容的提问来源于stack exchange,提问作者mike510a

