I am trying to understand QML Canvas
I want draw a circle with radial gradient and 3 lines connecting the circle from a point outside the circle.
Here's what is expected :
Here's the code :
Window {
id: root
visible: true
width: 640
height: 480
title: qsTr("Hello World")
color: "black"
Rectangle {
color: "black"
anchors.fill: parent
Canvas {
id: canvas
anchors.fill: parent
width: parent.width
height: parent/1.3
opacity: 0.5
onPaint: {
var ctx = getContext('2d');
var offset = canvas.height/3
var x = canvas.height/2 + offset,
y = canvas.height/2,
// Radii of the white glow.
innerRadius = 5,
outerRadius = y-10,
// Radius of the entire circle.
radius = canvas.height/2;
//Draw the circle with gradient
var gradient = ctx.createRadialGradient((x+(x*0.25)), (y+(y*0.25)), innerRadius, x, y, outerRadius);
gradient.addColorStop(0, '#232323');
gradient.addColorStop(1, '#6E6E6E');
ctx.arc(x, y, radius, 0, 2 * Math.PI);
ctx.fillStyle = gradient;
ctx.fill();
ctx.lineWidth = 1
ctx.strokeStyle = "grey"
ctx.beginPath()
//Straight line
ctx.moveTo(0, y)
ctx.lineTo(offset, y)
//Down tangent line
ctx.moveTo(0, y+30)
ctx.lineTo(x/1.25, (2*y)-5)
//Up tangent line
ctx.moveTo(0, y-30)
ctx.lineTo(x/1.25, 5)
ctx.stroke()
}
}
}
But the issue is as soon I resize the window the canvas is distorted I am getting the below result :
I am not able to figure out what is going on when I resize the window.
Any help would be appreciated
Thanks.
You are missing a call to beginPath() before you draw the arc. This is causing the arc to become part of the path that was started on the first onPaint in the subsequent calls to onPaint.
So, just add right above ctx.arc(...):
ctx.beginPath()
Related
I am new to qml but I want to add a circle gauge to the QQuickWidget by referring to the dashboard of the QT example.
Below is my codes.
guagetest.pro
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets quickwidgets
CONFIG += c++11
DEFINES += QT_DEPRECATED_WARNINGS
SOURCES += \
main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h
FORMS += \
mainwindow.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
RESOURCES += \
dashboard.qrc
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->quickWidget->setSource(QUrl("qrc:/qml/qml/test.qml"));
ui->quickWidget->show();
}
MainWindow::~MainWindow()
{
delete ui;
}
test.qml
import QtQuick 2.2
import QtQuick.Window 2.1
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtQuick.Extras 1.4
Item {
id: container
width: parent.width
height: parent.height
anchors.centerIn: parent.Center
Row {
id: gaugeRow
spacing: container.width * 0.2
anchors.centerIn: parent
CircularGauge {
id: speedometer
value: valueSource.kph
anchors.verticalCenter: parent.verticalCenter
maximumValue: 280
// We set the width to the height, because the height will always be
// the more limited factor. Also, all circular controls letterbox
// their contents to ensure that they remain circular. However, we
// don't want to extra space on the left and right of our gauges,
// because they're laid out horizontally, and that would create
// large horizontal gaps between gauges on wide screens.
width: height
height: container.height * 0.8
style: DashboardGaugeStyle {}
}
}
}
DashboardGaugeStyle.qml
import QtQuick 2.2
import QtQuick.Controls.Styles 1.4
CircularGaugeStyle {
tickmarkInset: toPixels(0.04) // gauge graduation radius
minorTickmarkInset: tickmarkInset
labelStepSize: 20 // gauge graduation text
labelInset: toPixels(0.23) // gauge graduation text position
property real xCenter: outerRadius
property real yCenter: outerRadius
property real needleLength: outerRadius - tickmarkInset * 1.25
property real needleTipWidth: toPixels(0.02)
property real needleBaseWidth: toPixels(0.06)
property bool halfGauge: false
function toPixels(percentage) {
return percentage * outerRadius;
}
function degToRad(degrees) {
return degrees * (Math.PI / 180);
}
function radToDeg(radians) {
return radians * (180 / Math.PI);
}
function paintBackground(ctx) {
if (halfGauge) {
ctx.beginPath();
ctx.rect(0, 0, ctx.canvas.width, ctx.canvas.height / 2);
ctx.clip();
}
ctx.beginPath();
ctx.fillStyle = "black";
ctx.ellipse(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.fill();
ctx.beginPath();
ctx.lineWidth = tickmarkInset;
ctx.strokeStyle = "black";
ctx.arc(xCenter, yCenter, outerRadius - ctx.lineWidth / 2, outerRadius -ctx.lineWidth / 2, 0, Math.PI * 2);
ctx.stroke();
ctx.beginPath();
ctx.lineWidth = tickmarkInset / 2;
ctx.strokeStyle = "#222";
ctx.arc(xCenter, yCenter, outerRadius - ctx.lineWidth / 2, outerRadius -ctx.lineWidth / 2, 0, Math.PI * 2);
ctx.stroke();
ctx.beginPath();
var gradient = ctx.createRadialGradient(xCenter, yCenter, 0, xCenter, yCenter, outerRadius * 1.5);
gradient.addColorStop(0, Qt.rgba(1, 1, 1, 0));
gradient.addColorStop(0.7, Qt.rgba(1, 1, 1, 0.13));
gradient.addColorStop(1, Qt.rgba(1, 1, 1, 1));
ctx.fillStyle = gradient;
ctx.arc(xCenter, yCenter, outerRadius - tickmarkInset, outerRadius - tickmarkInset, 0, Math.PI * 2);
ctx.fill();
}
background: Canvas {
onPaint: {
var ctx = getContext("2d");
ctx.reset();
paintBackground(ctx);
}
Text {
id: speedText
font.pixelSize: toPixels(0.3)
text: kphInt
color: "white"
horizontalAlignment: Text.AlignRight
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.verticalCenter
anchors.topMargin: toPixels(0.1)
readonly property int kphInt: control.value
}
Text {
text: "km/h"
color: "white"
font.pixelSize: toPixels(0.09)
anchors.top: speedText.bottom
anchors.horizontalCenter: parent.horizontalCenter
}
}
needle: Canvas {
implicitWidth: needleBaseWidth
implicitHeight: needleLength
property real xCenter: width / 2
property real yCenter: height / 2
onPaint: {
var ctx = getContext("2d");
ctx.reset();
ctx.beginPath();
ctx.moveTo(xCenter, height);
ctx.lineTo(xCenter - needleBaseWidth / 2, height - needleBaseWidth / 2);
ctx.lineTo(xCenter - needleTipWidth / 2, 0);
ctx.lineTo(xCenter, yCenter - needleLength);
ctx.lineTo(xCenter, 0);
ctx.closePath();
ctx.fillStyle = Qt.rgba(0.66, 0, 0, 0.66);
ctx.fill();
ctx.beginPath();
ctx.moveTo(xCenter, height)
ctx.lineTo(width, height - needleBaseWidth / 2);
ctx.lineTo(xCenter + needleTipWidth / 2, 0);
ctx.lineTo(xCenter, 0);
ctx.closePath();
ctx.fillStyle = Qt.lighter(Qt.rgba(0.66, 0, 0, 0.66));
ctx.fill();
}
}
foreground: null
}
When I compile, the following message appears.
qrc:/qml/qml/test.qml:9: TypeError: Cannot read property 'width' of null
qrc:/qml/qml/test.qml:10: TypeError: Cannot read property 'height' of null
qrc:/qml/qml/test.qml:20: ReferenceError: valueSource is not defined
qrc:/qml/qml/test.qml:11: TypeError: Cannot read property 'Center' of null
I want to know why that message comes out and how to solve it.
and How can i change background color of QQuickWidget?
Please help me.
Root object needs an initial size. And no need to center in parent, cause there is no parent. Let's say size is 500x300.
Item {
id: container
width: 500
height: 300
Or if you don't want to give a constant size, but to make size exactly to fit content childrenRect can be used. Make sure your content size does not depend on root and has a valid width and height before use it. And it might cause "binding loop detected for width/height." warnings.
Item {
id: container
width: childrenRect.width
height: childrenRect.height
And if you want your scene to resize respect to QQuickWidget's size dynamically set resize mode.
ui->quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
Let's get to coloring point. To change root item's color we can use Rectangle.color property. So change root object from Item to Rectangle. Let's make background red.
Rectangle {
id: container
width: childrenRect.width
height: childrenRect.height
color: "red"
Or if you want to change window color of QQuickWidget, set the palette. But since your scene going to cover it, I doubt that is what you need.
auto palette = ui->quickWidget->palette();
palette.setColor(QPalette::Window, QColor(Qt::red));
ui->quickWidget->setPalette(palette);
And you have one more problem:
qrc:/qml/qml/test.qml:20: ReferenceError: valueSource is not defined
I have no idea what valueSource is, either make sure you have it or get rid of it.
I started learning the Qt framework, and I wanted to create a MultiColored Gradient (like below image) background for the Window:
I went through the official documentation on Linear/Conical/Radial/Shape Gradient QML type and also did Google. But couldn't see any option to achieve this.
Is there any other way to make it possible? Preferably in QML.
Edit 1:
As stated in Comments, GradientStop can be used for multiple colors. But its can't be used it to produce the result as in the given image.
At first familiarize with Qt example quick\scenegraph\fboitem. It demonstrates creating custom QML control with C++/OpenGL backend.
Also you can implement it with ShaderEffect QML control. I recommend this site https://www.shadertoy.com/new for debugging your shaders. Also you can use, for example, Canvas for passing arrays to your shader as texture (ShaderEffect doesn't allow passing arrays normally, https://bugreports.qt.io/browse/QTBUG-50493).
Here is possible implementation of your control with dynamic points count, colors and positions.
import QtQuick 2.12
import QtQuick.Controls 2.5
ApplicationWindow {
id: window
visible: true
width: 640
height: 480
title: "Custom gradient"
Item {
anchors.fill: parent
layer.enabled: true
layer.effect: ShaderEffect {
readonly property var dataTex: dataCanvas
readonly property int count: dataCanvas.colors.length
fragmentShader: "
varying highp vec2 qt_TexCoord0;
uniform lowp float qt_Opacity;
uniform sampler2D dataTex;
const uniform lowp int count;
void main() {
vec3 col = 0.0;
for (int i = 0; i < count; i++) {
vec4 color = texture2D(dataTex, vec2(i / (float)count + 0.01, 0.0));
vec2 point = texture2D(dataTex, vec2(i / (float)count + 0.01, 1.0)).rg;
float dist = distance(qt_TexCoord0, point);
col += color * (1.0 - dist);
}
gl_FragColor = vec4(col, 1.0);
}
";
}
// Because ShaderEffect doesn't allow passing arrays to shader, we will
// convert arrays to graphical representation and pass them as texture.
Canvas {
id: dataCanvas
readonly property var colors: ["cyan", "red", "green", "darkblue", "yellow"]
readonly property var positions: [Qt.point(0,0),
Qt.point(10, parent.height - 20),
Qt.point(parent.width / 2, parent.height / 4),
Qt.point(parent.width, 0),
Qt.point(parent.width, parent.height)]
height: 2
width: colors.length
antialiasing: false
visible: false
onPaint: {
if (colors.length !== positions.length) {
console.error("Array size of 'colors' doesn't equal array size of 'positions'");
return;
}
var ctx = getContext("2d");
ctx.reset();
ctx.lineWidth = 1;
for (var i = 0; i < colors.length; i++) {
ctx.beginPath();
ctx.strokeStyle = colors[i];
ctx.moveTo(i, 0);
ctx.lineTo(i+1, 0);
ctx.stroke();
ctx.closePath();
ctx.beginPath();
ctx.strokeStyle = Qt.rgba(positions[i].x / parent.width, positions[i].y / parent.height, 0, 1);
ctx.moveTo(i, 1);
ctx.lineTo(i+1, 1);
ctx.stroke();
ctx.closePath();
}
}
}
}
}
That is how it looks
I'm not OpenGL guru, so this code isn't perfect. I'll be glad if someone will edit this answer to make it better.
Ihor's answer is good. I found that Qt actually provides an example that may be of use as well. Check out their Squircle example. I won't reproduce it all here, but it creates a QQuickItem in C++ and does some custom OpenGL rendering on it. The object can then be used in QML.
I am using qml to create a resizable rectangle that can be dragged inside it's parent pane, but cannot be dragged outside (Fig. 1). When I resize it e.g. using the top left handle, it works great and can't be resized outside it's parent (Fig. 2). But when I drag the entire rectangle, it can be dragged outside its parent (Fig. 3). My code attempts to use the same logic for both, i.e. once the rectangle's left side reaches it's parent's left side, it cancels the drag. Works fine for resizing, but doesn't when I drag the rectangle. What am I doing wrong?
Pane {
id: compPane
implicitHeight: imageListModel.reqViewHeight
implicitWidth: imageListModel.reqViewWidth
leftPadding: 0
topPadding: 0
clip: true
background: Rectangle{
id: backgroundRect
anchors.fill: parent
color: "gray"
border.color: "black"
border.width: 6
}
// Resizable rectangle that can be dragged
Rectangle {
id: indRect
x:parent.width / 4
y: parent.height / 4
width: parent.width / 2
height: parent.width / 2
border {
width: 2
color: "steelblue"
}
color: "#354682B4"
MouseArea {
id: indImagesDragArea
anchors.fill: parent
anchors.margins: 8
drag.target: parent
onPositionChanged: {
if (drag.active){
if (indRect.x <= 0 + backgroundRect.border.width)
Drag.cancel()
if (indRect.x + indRect.width >= (compPane.width - backgroundRect.border.width) )
Drag.cancel()
}
}
}
// Top Left handle - works well
Rectangle {
width: 15
height: 15
color: "steelblue"
anchors.verticalCenter:parent.top
anchors.horizontalCenter: parent.left
MouseArea {
anchors.fill: parent
cursorShape: Qt.SizeFDiagCursor
drag{ target: parent; axis: Drag.XAndYAxis }
onPositionChanged: {
if(drag.active){
//var delta = Math.max(mouseX, mouseY)
var newWidth = indRect.width - mouseX
var newHeight = indRect.height - mouseY;
if (newWidth < width || newHeight < height)
return
if (indRect.x <= 0 + backgroundRect.border.width && mouseX < 0){
Drag.cancel()
indRect.x = 0 + backgroundRect.border.width
}
else {
indRect.width = newWidth
indRect.x = indRect.x + mouseX
}
if (indRect.y <= 0 + backgroundRect.border.width && mouseY < 0){
Drag.cancel()
indRect.y = 0 + backgroundRect.border.width
}
else{
indRect.height = newHeight
indRect.y = indRect.y + mouseY
}
}
}
}
}
}
}
Drag.cancel() is actually intended for use when implementing drag and drop types of interactions. In this case, the calls to it are doing nothing because you are never actually starting one of those sequences. Your upper left handle example works fine if you comment out the calls to Drag.cancel().
Why your upper left example is working is because you are limiting the x and y on position updates to clip them to the scene via explicit updates of indRect.x and indRect.y. You just need to implement the same technique for the main drag scenario.
For instance:
onPositionChanged: {
if (drag.active){
if (indRect.x <= 0 + backgroundRect.border.width)
indRect.x = 0 + backgroundRect.border.width;
if (indRect.x + indRect.width >= (compPane.width - backgroundRect.border.width) )
indRect.x = compPane.width - backgroundRect.border.width - indRect.width;
}
}
You could try to use the property of drag.minimum and drag.maximum.
See the documentation:
https://doc.qt.io/qt-5/qml-qtquick-mousearea.html#drag.threshold-prop
I am trying to implement a functionality, where the color of drawn polygon can be dynamically changed. At the moment I have a simple test App where I am able to draw a triangle and rotate it. I then Added 2 buttons which are used to change the colors of all objects.
The rectangles and text colors change correctly but the drawn polygon do not. After random clicking the color change buttons eventually a new polygon is drawn but in an incorrect spot. I really can't tell what could be the problem.
Here is the code:
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 2.2
Window {
visible: true
width: 480
height: 800
Ucolors
{
id: colDay
canvas: "#eaedf1"
text: "#0e0e12"
spiderBckgrnd: "#f7fbff"
spiderLines: "#C8CBD0"
}
Ucolors
{
id: colNight
canvas: "#22212c"
text: "#ffffff"
spiderBckgrnd: "#0e0e12"
spiderLines: "#3C3C3F"
}
property var colGlob: colDay
Rectangle
{
id: rectMain
anchors.centerIn: parent
width: parent.width
height: parent.height
color: colGlob.canvas
Text
{
anchors.right: parent.right
color: colGlob.text
text: qsTr("text")
}
Button
{
id: btn1
anchors.left: parent.left
text: "button1"
onClicked:
{
colGlob = colNight;
}
}
Button
{
id: btn2
anchors.left: btn1.right
text: "button2"
onClicked:
{
colGlob = colDay;
}
}
Rectangle
{
id: rectTemp
anchors.centerIn: parent
width: 374
height: 432
//color: "transparent"
color: "red"
Utriangle
{
trbase: 183
rotAngle: 30
fillColor: colGlob.spiderBckgrnd
strokeColor: colGlob.spiderLines
anchors.centerIn: parent
}
}
}
}
Ucolors.qml
import QtQuick 2.9
/**
* #brief Holds the color parameters of the whole UI
*
*/
Item
{
property var canvas
property var text
property var spiderBckgrnd
property var spiderLines
}
Utriangle.qml
import QtQuick 2.9
/**
* #brief This object draws an equilateral triangle in the middle of the
* parent object. The triangle at \p rotAngle 0 is being drawn
starting from one of the corners facing down.
\p hFactor of 1 will draw a triangle with the height that coresponds
to the set \p base. Fractional values will make the triangle height
shorter accordingly.
*
*/
Canvas
{
anchors.fill: parent
// set properties with default values
property real hFactor: 1 // height factor
property int trbase: 50
property color strokeColor: "black"
property color fillColor: "yellow"
property int lineWidth: 1
property real alpha: 1
property real rotAngle: 0
onStrokeColorChanged: requestPaint();
onFillColorChanged: requestPaint();
onLineWidthChanged: requestPaint();
onPaint:
{
var ctx = getContext("2d") // get context to draw with
ctx.lineWidth = lineWidth
ctx.strokeStyle = strokeColor
ctx.fillStyle = fillColor
ctx.globalAlpha = alpha
ctx.beginPath()
ctx.translate(parent.width / 2, parent.height / 2)
ctx.rotate((Math.PI / 180) * rotAngle)
ctx.moveTo(0, 0)
// drawing part, first calculate height using Pythagoras equation
var trheight = Math.sqrt(Math.pow(trbase, 2) - Math.pow(trbase / 2, 2))
trheight = trheight * Math.abs(hFactor)
var hfBase = trbase * Math.abs(hFactor)
ctx.lineTo(hfBase / -2, trheight) // left arm
ctx.lineTo(hfBase / 2, trheight) // right arm
ctx.closePath(); // base drawn aoutomatically
ctx.fill()
ctx.stroke()
}
}
The gui before changing the colors:
The gui after changing the colors:
After clicking the button for a while, eventually the triangle shows up in the wrong spot:
The color is changed but in a rotated triangle since the operations of rotation and translation are maintained, so after a certain amount of clicked you see half of a triangle of the right color. The solution is to save the state prior to the transformations and restore it after painting with save() and restore().
onPaint:
{
var ctx = getContext("2d") // get context to draw with
ctx.lineWidth = lineWidth
ctx.strokeStyle = strokeColor
ctx.fillStyle = fillColor
ctx.globalAlpha = alpha
ctx.save(); // <---
ctx.beginPath()
ctx.translate(parent.width / 2, parent.height / 2)
ctx.rotate((Math.PI / 180) * rotAngle)
ctx.moveTo(0, 0)
// drawing part, first calculate height using Pythagoras equation
var trheight = Math.sqrt(Math.pow(trbase, 2) - Math.pow(trbase / 2, 2))
trheight = trheight * Math.abs(hFactor)
var hfBase = trbase * Math.abs(hFactor)
ctx.lineTo(hfBase / -2, trheight) // left arm
ctx.lineTo(hfBase / 2, trheight) // right arm
ctx.closePath(); // base drawn aoutomatically
ctx.fill()
ctx.stroke()
ctx.restore(); // <---
}
I am using Qt 5.8. I am attempting to draw circular points using QSGGeometry::DrawTriangles (using QSGGeometry::DrawPoints draws points as rectangles/squares--not desired). In order to do this I am drawing 8 triangles that gives the illusion of a circle. Each data point will have 8 triangles associated with it. The number of data points can vary at any given time. After a (user) specified amount of time as a data point is added, one data point is removed. There seems to be an error in the allocation of data when it's drawn. I used setVertexDataPattern(QSGGeometry::StreamPattern); in the construction of the QSGGeometryNode; in hopes of getting the desired output.
On each draw call, I call m_geometry.allocate(m_pts.size() * MAX_VERTICES), where MAX_VERTICES = 24 in case the number of points since the last draw call has changed. I have attempted to use GL_POLYGON (since it would require fewer vertices), but the same problem happens. There seems to be an attempt to draw a shape from one slice of the the first data point to another slice of the last data point. Is there something wrong with reallocating for every draw call? What is the proper way to handle drawing data with varying sizes?
Update I think it may deal with a size issue. I have sample code that only draws 1 triangle (instead of 8) and once you get to about 25000 (times 3 for each triangle) the odd line appears and seems to stop drawing additional triangles. In the following sample code (when using a smaller number of points) the last triangle is white.
main.qml
import QtQuick 2.8
import QtQuick.Window 2.2
import TestModule 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle {
color: "black"
width: parent.width
height: parent.height * .90
anchors.top: parent.top
MouseArea {
anchors.fill: parent
onClicked: {
Qt.quit();
}
}
TestItem {
id: testItem
anchors.fill: parent
ptCount: 25000
color: "green"
}
}
Rectangle {
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
height: parent.height * .10
width: parent.width
border.color: "pink"
color: "lightgray"
TextInput {
anchors.fill: parent
anchors.centerIn: parent
id: textInput
text: "enter max number of points here"
horizontalAlignment: TextInput.AlignHCenter
verticalAlignment: TextInput.AlignVCenter
color: "steelblue"
onEditingFinished: testItem.ptCount = parseInt(textInput.text)
validator: IntValidator{bottom: 1}
}
}
}
TestItem.h
#include <QQuickItem>
class QSGGeometryNode;
class TestItem : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
Q_PROPERTY(qint32 ptCount READ ptCount WRITE setPtCount NOTIFY ptCountChanged)
public:
explicit TestItem(QQuickItem *parent = 0);
QColor color();
void setColor(const QColor &color);
void setPtCount(const qint32& newVal);
qint32 ptCount();
signals:
void colorChanged();
void ptCountChanged();
protected:
QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry);
QColor m_color;
qint32 m_ptCount;
};
TestItem.cpp
#include "TestItem.h"
#include <QSGNode>
#include <QSGVertexColorMaterial>
TestItem::TestItem(QQuickItem *parent) : QQuickItem(parent), m_color(Qt::green), m_ptCount(25000)
{
setFlag(ItemHasContents, true);
}
QColor TestItem::color()
{
return m_color;
}
void TestItem::setColor(const QColor &color)
{
m_color = color;
update();
emit colorChanged();
}
void TestItem::setPtCount(const qint32 &newVal)
{
if (newVal < 0)
m_ptCount = 25000;
else
m_ptCount = newVal;
update();
emit ptCountChanged();
}
qint32 TestItem::ptCount()
{
return m_ptCount;
}
QSGNode *TestItem::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
{
QSGGeometryNode *node = nullptr;
QSGGeometry *geometry = nullptr;
if (!oldNode)
{
node = new QSGGeometryNode;
geometry = new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), m_ptCount * 3);
geometry->setDrawingMode(GL_TRIANGLES);
node->setGeometry(geometry);
node->setFlag(QSGNode::OwnsGeometry);
QSGVertexColorMaterial *material = new QSGVertexColorMaterial;
node->setMaterial(material);
node->setFlag(QSGNode::OwnsMaterial);
}
else
{
node = static_cast<QSGGeometryNode *>(oldNode);
geometry = node->geometry();
geometry->allocate(m_ptCount * 3);
}
QSGGeometry::ColoredPoint2D *vertices = geometry->vertexDataAsColoredPoint2D();
qreal triWidth = 250/boundingRect().width() + 10;
for (int i = 0; i < m_ptCount; ++i)
{
QColor color;
if (i == m_ptCount - 1)
color = Qt::white;
else
color = m_color;
qreal x0 = (boundingRect().width() * .90/m_ptCount) * i ;
qreal y0 = 60 * sinf(x0* 3.14/180); // 60 just varies the height of the wave
qreal x1 = x0 + 0.05 * boundingRect().width(); // 0.05 so that we have 5% space on each side
qreal y1 = y0 + boundingRect().height()/2;
vertices[i * 3].set(x1, y1, color.red(), color.green(), color.blue(), color.alpha());
vertices[i * 3 + 1].set(x1 + triWidth, y1, color.red(), color.green(), color.blue(), color.alpha());
vertices[i * 3 + 2].set(x1 + triWidth, y1 + triWidth, color.red(), color.green(), color.blue(), color.alpha());
}
node->markDirty(QSGNode::DirtyGeometry);
return node;
}
void TestItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
{
update();
QQuickItem::geometryChanged(newGeometry, oldGeometry);
}
Any help with determining if this is a Qt bug or my error somewhere?
It turns out I needed to change the geometry's constructor to use UnsignedIntType; it defaults to UnsignedShortType.
geometry = new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), m_ptCount * 3, 0, QSGGeometry::UnsignedIntType);