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

时间:2015-07-26 10:07:39

标签: qt qml qtquick2 qgraphicsitem

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

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

    // specific methods

  private:
    // specific data
};

假设您已在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
}

我想要做的简单任务是在MyCustomItem中显示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();
}

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

修改

这可能是QGraphicsRectItem不是正确的继承类,而QQuickPaintedItem之类的东西(如评论中所建议的)会更合适吗?

1 个答案:

答案 0 :(得分:4)

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

QQuickPaintedItem

基于QPainter的QQuickItem。这听起来最接近你想要的。 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的绘制带有HTML5-like API的QML类型。来自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

正如this回答中提到的,您可以利用Qt Quick Scene Graph。来自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<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;
}

QQuickWidget

如果你真的想使用QGraphicsItem子类,你可以采用相反的方向,并拥有一个包含某些“Qt Quick Widgets”的基于小部件的应用程序,尽管这不是最佳的(参见Qt Weekly #16: QQuickWidget }了解更多信息)。