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

    1. The update() slot of the outer Rectangle (xy)
    2. 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 the update() 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?



  • 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()
        }

Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.