如何将自定义 GraphicsItem 集成到 QML 场景中? [英] How to integrate a custom GraphicsItem into a QML scene?

查看:68
本文介绍了如何将自定义 GraphicsItem 集成到 QML 场景中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

    // specific methods

  private:
    // specific data
};

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

Assume also that you have defined in a QML script an 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
}

我想做的简单任务是在该 ApplicationWindow 中显示 MyCustomItem 的实例.我想做以下事情:

The simple task I would like to do is to display an instance of MyCustomItem in that ApplicationWindow. I wanted to do the following:

// 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 中才能完成任务?在我看来,那太可怕了.没有更好的选择吗?

But of course this doesn't work, because MyCustomItem is neither a QObject nor a QVariant. I don't want my item to be anything else than a QGraphicsRectItem. Isn't that possible to display that graphics item? That should be simple as hell, shouldn't it? Is there a way with QDeclarativeItem or something? I can't find how to solve this problem, that's very frustrating. Would I implement my application with "normal" Qt, the problem would already be solved, because in this case you have a scene, and the scene has a member method addItem() and I don't need to do complicated stuff to add my custom graphics item to my scene. Do I have to wrap this item in a QDeclarativeItem or a QObject in order to get the thing done? That would be so awful, in my opinion. Aren't there better options?

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

Can that be that QGraphicsRectItem is not the right class to inherit from and that something like QQuickPaintedItem (as suggested in the comments) would be more appropriate?

推荐答案

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

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

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

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);
    }
}

画布

基于 JavaScript 的绘图 QML 类型,带有 HTML5-like 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();
    }
}

QSGGeometryNode

答案中所述,您可以利用Qt 快速场景图.摘自其中一个示例:>

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<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 了解更多信息).

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).

这篇关于如何将自定义 GraphicsItem 集成到 QML 场景中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆