How to integrate a custom GraphicsItem into a QML scene?

后端 未结 1 585
南旧
南旧 2021-01-22 06:36

Assume you have created the following custom QGraphicsRectItem in C++:

class MyCustomItem : public QGraphicsRectItem
{
  public:
    MyCustomItem(My         


        
1条回答
  •  一整个雨季
    2021-01-22 07:21

    I can't speak for Qt 4, but in Qt 5, you have several options for custom drawing:

    QQuickPaintedItem

    A QPainter-based QQuickItem. This sounds the closest to what you want. A snippet from the documentation of one of the examples:

    void TextBalloon::paint(QPainter *painter)
    {
        QBrush brush(QColor("#007430"));
    
        painter->setBrush(brush);
        painter->setPen(Qt::NoPen);
        painter->setRenderHint(QPainter::Antialiasing);
    
        painter->drawRoundedRect(0, 0, boundingRect().width(), boundingRect().height() - 10, 10, 10);
    
        if (rightAligned)
        {
            const QPointF points[3] = {
                QPointF(boundingRect().width() - 10.0, boundingRect().height() - 10.0),
                QPointF(boundingRect().width() - 20.0, boundingRect().height()),
                QPointF(boundingRect().width() - 30.0, boundingRect().height() - 10.0),
            };
            painter->drawConvexPolygon(points, 3);
        }
        else
        {
            const QPointF points[3] = {
                QPointF(10.0, boundingRect().height() - 10.0),
                QPointF(20.0, boundingRect().height()),
                QPointF(30.0, boundingRect().height() - 10.0),
            };
            painter->drawConvexPolygon(points, 3);
        }
    }
    

    Canvas

    JavaScript-based drawing QML type with an HTML5-like API. A snippet from one of the examples:

    Canvas {
        id: canvas
        width: 320
        height: 250
        antialiasing: true
    
        property color strokeStyle: Qt.darker(fillStyle, 1.2)
        property color fillStyle: "#6400aa"
    
        property int lineWidth: 2
        property int nSize: nCtrl.value
        property real radius: rCtrl.value
        property bool fill: true
        property bool stroke: false
        property real px: width/2
        property real py: height/2 + 10
        property real alpha: 1.0
    
        onRadiusChanged: requestPaint();
        onLineWidthChanged: requestPaint();
        onNSizeChanged: requestPaint();
        onFillChanged: requestPaint();
        onStrokeChanged: requestPaint();
    
        onPaint: squcirle();
    
        function squcirle() {
            var ctx = canvas.getContext("2d");
            var N = canvas.nSize;
            var R = canvas.radius;
    
            N=Math.abs(N);
            var M=N;
            if (N>100) M=100;
            if (N<0.00000000001) M=0.00000000001;
    
            ctx.save();
            ctx.globalAlpha =canvas.alpha;
            ctx.fillStyle = "white";
            ctx.fillRect(0, 0, canvas.width, canvas.height);
    
            ctx.strokeStyle = canvas.strokeStyle;
            ctx.fillStyle = canvas.fillStyle;
            ctx.lineWidth = canvas.lineWidth;
    
            ctx.beginPath();
            var i = 0, x, y;
            for (i=0; i<(2*R+1); i++){
                x = Math.round(i-R) + canvas.px;
                y = Math.round(Math.pow(Math.abs(Math.pow(R,M)-Math.pow(Math.abs(i-R),M)),1/M)) + canvas.py;
    
                if (i == 0)
                    ctx.moveTo(x, y);
                else
                    ctx.lineTo(x, y);
            }
    
            for (i=(2*R); i<(4*R+1); i++){
                x =Math.round(3*R-i)+canvas.px;
                y = Math.round(-Math.pow(Math.abs(Math.pow(R,M)-Math.pow(Math.abs(3*R-i),M)),1/M)) + canvas.py;
                ctx.lineTo(x, y);
            }
            ctx.closePath();
            if (canvas.stroke) {
                ctx.stroke();
            }
    
            if (canvas.fill) {
                ctx.fill();
            }
            ctx.restore();
        }
    }
    

    QSGGeometryNode

    As mentioned in this answer, you could take advantage of the Qt Quick Scene Graph. A snippet from one of the examples:

    QSGNode *BezierCurve::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
    {
        QSGGeometryNode *node = 0;
        QSGGeometry *geometry = 0;
    
        if (!oldNode) {
            node = new QSGGeometryNode;
            geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), m_segmentCount);
            geometry->setLineWidth(2);
            geometry->setDrawingMode(GL_LINE_STRIP);
            node->setGeometry(geometry);
            node->setFlag(QSGNode::OwnsGeometry);
            QSGFlatColorMaterial *material = new QSGFlatColorMaterial;
            material->setColor(QColor(255, 0, 0));
            node->setMaterial(material);
            node->setFlag(QSGNode::OwnsMaterial);
        } else {
            node = static_cast(oldNode);
            geometry = node->geometry();
            geometry->allocate(m_segmentCount);
        }
    
        QRectF bounds = boundingRect();
        QSGGeometry::Point2D *vertices = geometry->vertexDataAsPoint2D();
        for (int i = 0; i < m_segmentCount; ++i) {
            qreal t = i / qreal(m_segmentCount - 1);
            qreal invt = 1 - t;
    
            QPointF pos = invt * invt * invt * m_p1
                        + 3 * invt * invt * t * m_p2
                        + 3 * invt * t * t * m_p3
                        + t * t * t * m_p4;
    
            float x = bounds.x() + pos.x() * bounds.width();
            float y = bounds.y() + pos.y() * bounds.height();
    
            vertices[i].set(x, y);
        }
        node->markDirty(QSGNode::DirtyGeometry);
    
        return node;
    }
    

    QQuickWidget

    If you really want to use QGraphicsItem subclasses, you could go the opposite direction, and have a widget-based app that contains certain "Qt Quick Widgets", though this is not optimal (see Qt Weekly #16: QQuickWidget for more information).

    0 讨论(0)
提交回复
热议问题