Saving QML's ChartView to image file in PyQt5

704 Views Asked by At

My approach was pretty much as such:

Chart.qml:

Window {
    ChartView {
        id: chartView
        // ...
    }

    Button {
        text: "save"
        onClicked: manager.save(chartView)
    }
}

Manager.py:

class Manager(QObject):
    @pyqtSlot(QObject)
    def save(self, chartview):
        img = chartview.grab()
        img.save(MY_DEFAULT_PATH)

The problem is, of course, that inside the Manager.save() the chartview is equivalent of C++'s QObject* which does not have the 'grab' method. But it could be downcasted to QChartView* (or at least QWidget*) which, such method, has. In C++ I would do it probably with qobject_cast.

My questions are:

  1. Is such downcasting doable in PyQt?
  2. If not - do you have any idea how could it be done differently?
1

There are 1 best solutions below

0
On BEST ANSWER

Before giving the answer it is better to clarify some errors that the post has:

  • The QML ChartView is an Item unlike the QChartView which is a QWidget. And that Item is painted using the information from a QGraphicsScene and QChart. Considering the above, one method could be to access the QGraphicsScene and QChart, and use the render() method of QGraphicsScene.

  • You can do casting using sip through the sip.cast() method.

  • In this case, it is not necessary to do a downcasting either, since it is enough to indicate in the signature to:

class Manager(QtCore.QObject):
    @QtCore.pyqtSlot(QtQuick.QQuickItem)
    def save(self, chartview):
        self.result = chartview.grabToImage()

        def handle_ready():
            self.result.saveToFile(MY_DEFAULT_PATH)
            delattr(self, "result")

        self.result.ready.connect(handle_ready)

But instead of getting too complicated you can use the grabToImage method since it is an Item:

import QtQuick 2.12
import QtQuick.Controls 2.12
import QtCharts 2.2

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    Button {
        id: btn
        text: "Save"
        anchors.top: parent.top
        onClicked: saveChart("chart.png") 
    }
    ChartView {
        id: chartView
        anchors.top: btn.bottom
        width: parent.width
        anchors.bottom: parent.bottom
        LineSeries {
            XYPoint { x: 0; y: 0 }
            XYPoint { x: 1.1; y: 2.1 }
        }
    }

    function saveChart(filename){
        chartView.grabToImage(function(result) {
            result.saveToFile(filename);
        });
    }   
}