QQuickPaintedItem在QML Designer中不可见的技术求助
QQuickPaintedItem Component I get it—having your custom TextBalloon work at runtime but not show up in QML Designer is super frustrating. Let’s break down why this happens and how to fix it quickly.
Why QQuickPaintedItem Fails in Designer
QML Designer uses a Qt Quick Scene Graph-based preview engine, and QQuickPaintedItem relies on software rendering via QPainter. Designer often skips executing custom paint() methods because it doesn’t fully initialize the software drawing context during previews.
The best workaround is to rewrite your component using Qt Quick Scene Graph directly (inheriting QQuickItem instead of QQuickPaintedItem), which Designer supports natively.
Solution 1: Rewrite with QQuickItem and Scene Graph
Here’s how to convert your TextBalloon to use Scene Graph, which will render correctly in both runtime and Designer:
Updated Header File (textballoon.h)
#include <QQuickItem> #include <QSGNode> class TextBalloon : public QQuickItem { Q_OBJECT Q_PROPERTY(bool rightAligned READ isRightAligned WRITE setRightAligned NOTIFY rightAlignedChanged) public: TextBalloon(QQuickItem *parent = nullptr); bool isRightAligned() const; void setRightAligned(bool rightAligned); protected: QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) override; signals: void rightAlignedChanged(); private: bool m_rightAligned = false; };
Updated Implementation (textballoon.cpp)
#include "textballoon.h" #include <QSGFlatColorMaterial> #include <QSGGeometryNode> #include <QSGGeometry> #include <cmath> TextBalloon::TextBalloon(QQuickItem *parent) : QQuickItem(parent) { setFlag(ItemHasContents, true); // Required to trigger updatePaintNode } bool TextBalloon::isRightAligned() const { return m_rightAligned; } void TextBalloon::setRightAligned(bool rightAligned) { if (m_rightAligned == rightAligned) return; m_rightAligned = rightAligned; emit rightAlignedChanged(); update(); // Tell Scene Graph to refresh the node } QSGNode *TextBalloon::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *) { QSGGeometryNode *node = static_cast<QSGGeometryNode*>(oldNode); if (!node) { node = new QSGGeometryNode; // Set up the green material for the balloon QSGFlatColorMaterial *material = new QSGFlatColorMaterial; material->setColor(QColor("#007430")); node->setMaterial(material); node->setFlag(QSGNode::OwnsMaterial); } const QSizeF itemSize = size(); const qreal radius = 10; const qreal tailHeight = 10; QVector<QSGGeometry::Point2D> vertices; // Build the rounded rectangle shape // Top-left corner arc vertices << QSGGeometry::Point2D(radius, 0); for (int i = 1; i < 9; ++i) { const qreal angle = M_PI_2 * i / 8; vertices << QSGGeometry::Point2D(radius - radius * cos(angle), radius * sin(angle)); } // Top edge vertices << QSGGeometry::Point2D(itemSize.width() - radius, 0); // Top-right corner arc for (int i = 1; i < 9; ++i) { const qreal angle = M_PI_2 * i / 8; vertices << QSGGeometry::Point2D(itemSize.width() - radius * sin(angle), radius - radius * cos(angle)); } // Right edge vertices << QSGGeometry::Point2D(itemSize.width(), itemSize.height() - tailHeight - radius); // Handle right-aligned vs left-aligned tail if (!m_rightAligned) { // Bottom-right corner arc (no tail here) for (int i = 1; i < 9; ++i) { const qreal angle = M_PI_2 * i / 8; vertices << QSGGeometry::Point2D(itemSize.width() - radius + radius * cos(angle), itemSize.height() - tailHeight - radius + radius * sin(angle)); } // Bottom edge to tail start vertices << QSGGeometry::Point2D(30, itemSize.height() - tailHeight); // Left-aligned tail vertices << QSGGeometry::Point2D(20, itemSize.height()); vertices << QSGGeometry::Point2D(10, itemSize.height() - tailHeight); } else { // Right-aligned tail vertices << QSGGeometry::Point2D(itemSize.width() - 10, itemSize.height() - tailHeight); vertices << QSGGeometry::Point2D(itemSize.width() - 20, itemSize.height()); vertices << QSGGeometry::Point2D(itemSize.width() - 30, itemSize.height() - tailHeight); } // Bottom-left corner arc vertices << QSGGeometry::Point2D(radius, itemSize.height() - tailHeight); for (int i = 1; i < 9; ++i) { const qreal angle = M_PI_2 * i / 8; vertices << QSGGeometry::Point2D(radius - radius * sin(angle), itemSize.height() - tailHeight - radius + radius * cos(angle)); } // Update the geometry data QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), vertices.size()); geometry->setDrawingMode(GL_TRIANGLE_FAN); QSGGeometry::Point2D *data = geometry->vertexDataAsPoint2D(); for (int i = 0; i < vertices.size(); ++i) { data[i] = vertices[i]; } node->setGeometry(geometry); node->setFlag(QSGNode::OwnsGeometry); return node; }
Key Changes Explained
- We inherit
QQuickItemand enableItemHasContentsto signal that the component renders custom content. - The
updatePaintNodemethod builds a Scene Graph node using vertex data (instead ofQPainter), which Designer can render natively. - Property changes trigger an
update()to refresh the Scene Graph node.
Solution 2: Create a QML Designer Plugin (Advanced)
If you need to keep using QQuickPaintedItem, you can build a Designer plugin to add preview support. This is more complex but useful for reusable components:
- Create a Qt Designer custom widget plugin project.
- Implement
QDesignerCustomWidgetInterfaceto define your component’s metadata (name, icon, preview behavior). - In
createWidget(), return an instance ofTextBalloonwrapped in aQQuickWidgetfor preview. - Compile and install the plugin to Qt’s Designer plugin directory.
This approach requires more setup, so start with Solution 1 for a quick fix.
内容的提问来源于stack exchange,提问作者Shivendra

