如何将自定义 GraphicsItem 集成到 QML 场景中?

Lau*_*hel 2 qt qgraphicsitem qml qtquick2

假设您已经QGraphicsRectItem在 C++ 中创建了以下自定义:

class MyCustomItem : public QGraphicsRectItem
{
  public:
    MyCustomItem(MyCustomItem* a_Parent = 0);
    virtual ~MyCustomItem();

    // specific methods

  private:
    // specific data
};
Run Code Online (Sandbox Code Playgroud)

还假设您在 QML 脚本中定义了一个ApplicationWindow

// main.qml
import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Window 2.0

ApplicationWindow {
    id: myWindow
    title: qsTr("My Window")
    width: 640
    height: 480
    visible: true
}
Run Code Online (Sandbox Code Playgroud)

我想做的简单任务是显示MyCustomItemin that的实例ApplicationWindow。我想做以下事情:

// part of main.cpp
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    MyCustomItem* myItem;
    engine.rootContext()->setContextProperty("MyCustomItem", myItem);

    return app.exec();
}
Run Code Online (Sandbox Code Playgroud)

但这当然不起作用,因为MyCustomItem既不是 aQObject也不是 a QVariant。我不希望我的项目是QGraphicsRectItem. 那不能显示那个图形项目吗?那应该很简单,不是吗?有什么办法QDeclarativeItem吗?我找不到如何解决这个问题,这很令人沮丧。我会用“普通”Qt 实现我的应用程序,问题已经解决了,因为在这种情况下你有一个场景,并且场景有一个成员方法addItem(),我不需要做复杂的事情来添加我的自定义图形项到我的场景。我是否必须用 aQDeclarativeItem或 a包装这个项目QObject才能完成这件事?在我看来,那太可怕了。没有更好的选择吗?

编辑

这可能QGraphicsRectItem不是继承的正确类,并且类似QQuickPaintedItem(如评论中建议的那样)会更合适吗?

Mit*_*tch 6

我不能说 Qt 4,但在 Qt 5 中,您有多种自定义绘图选项:

QQuickPaintedItem

基于 QPainter 的QQuickItem. 这听起来最接近你想要的。其中一个示例的文档中的片段:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

Canvas

基于 JavaScript 的绘图 QML 类型,带有类似 HTML5 的API。其中一个示例的片段:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

QSGGeometryNode

答案所述,您可以利用Qt Quick Scene Graph其中一个示例的片段:

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<QSGGeometryNode *>(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;
}
Run Code Online (Sandbox Code Playgroud)

QQuickWidget

如果您真的想使用QGraphicsItem子类,您可以反其道而行之,并拥有一个基于小部件的应用程序,其中包含某些“Qt Quick Widgets”,尽管这不是最佳选择(有关更多信息,请参阅Qt Weekly #16:QQuickWidget)。