如何将自定义 GraphicsItem 集成到 QML 场景中? [英] How to integrate a custom GraphicsItem into a QML scene?
问题描述
假设您已经在 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()
而我不需要做一些复杂的事情来将我的自定义图形项目添加到我的场景中.我是否必须将此项目包装在 QDeclarativeItem
或 QObject
中才能完成任务?在我看来,那太可怕了.没有更好的选择吗?
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屋!