Solved Force periodic update on QML Canvas from C++
-
I would like to draw streaming data (generated in C++) onto a Canvas within a Rectangle:
Rectangle { id: xy ... Canvas { id: xyCanvas ... onPaint: { var ctx = getContext("2d"); // Draw stuff here console.log("QML XY Update!"); } }
I connected a 60 Hz timer to
- The update() slot of the outer Rectangle (
xy
) - The update() slot of the Canvas (
xyCanvas
) directly
I expect xyCanvas.onPaint() to get called (and my console message to get printed). But to my surprise, neither works. There have been posts on forcing update on custom QQuickItems:
https://stackoverflow.com/questions/19455518/periodically-redraw-qquickitem
https://forum.qt.io/topic/80962/how-to-redraw-a-custom-qquickitem
But how can this be done for a built-in Item?I also tried connecting the
QQuickWindow::frameSwapped()
signal of the top level window to theupdate()
slot of the QQuickWindow as suggested in the SO post, but the frameSwapped() itself does not get emitted unless I keep a dummy Animation looping infinitely. With the dummy Animation in place, I do get the frameSwapped() signal, but that does not result in the xyCanvas.onPaint() getting called.Finally, I tried adding a Connections object directly in QML, again to no avail:
Connections { target: mainWnd onFrameSwapped: xyCanvas.update() }
The only time that onPaint() gets called is when the QML component is initially loaded.
How can I force a periodic onPaint() call on the Canvas?
- The update() slot of the outer Rectangle (
-
Hi! The key is to call
requestPaint()
whenever your data has changed.import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.3 ApplicationWindow { visible: true width: 640 height: 480 title: qsTr("Hello World") Canvas { property real dd: 0 SequentialAnimation on dd { loops: Animation.Infinite PropertyAnimation { duration: 2000; to: 1 } PropertyAnimation { duration: 2000; to: 0 } } onDdChanged: requestPaint() anchors.fill: parent onPaint: { var ctx = getContext("2d"); ctx.fillStyle = "black" ctx.fillRect(0, 0, width, height); ctx.fillStyle = Qt.rgba(0.25, 0.8, 0.32, 1); ctx.fillRect(dd * width, dd * height, 100, 100); } } }
-
@Wieland This works perfectly, thanks! I set up a Timer to request a refresh:
Timer { interval: 16 running: true repeat: true onTriggered: xyCanvas.requestPaint() }