Search code examples
pythonexportpngpyqtgraph

PyQTGraph ImageExporter bug related to image size


I am using pyqtgraph to produce plots in a PyQT window. I want to export PNG image of the graphs.

I have this error when I try to export my plots:

ImageExporter.py", line 70, in export bg = np.empty((self.params['width'], self.params['height'], 4), >dtype=np.ubyte) TypeError: 'float' object cannot be interpreted as an integer

I noticed that the self.params['width'] and self.params['height'] are floats. But np.empty can not create an erray using float sizes.

Even if i set the width and height manually using :

exporter.parameters()['width'] = self.raw_DataPlot.width()
exporter.parameters()['height'] = self.raw_DataPlot.height()

The results are floats.

I noticed that If I change the line 70 of ImageExporter.py to :

bg = np.empty((int(self.params['width']), int(self.params['height']), 4),dtype=np.ubyte)

The export works fine.

Is it possible to address this issue and update the library? Or is there a workaround that doesn't push me to change the pyqtgraph library itself.

Thanks


Solution

  • To solve this problem, I created a new ImageExporter that I integrated to my project. I called this exporter PQG_ImageExporter so that we don't get confused.

    I signaled this problem on github. I hope they fix it soon. When the problem will be solved, I can get back to the classic ImageExporter :

    from pyqtgraph.exporters import Exporter
    from pyqtgraph.parametertree import Parameter
    from pyqtgraph.Qt import QtGui, QtCore, QtSvg, USE_PYSIDE
    from pyqtgraph import functions as fn
    import numpy as np
    import pyqtgraph as pg
    
    __all__ = ['PQG_ImageExporter']
    
    
    class PQG_ImageExporter(Exporter):
        Name = "Image File (PNG, TIF, JPG, ...)"
        allowCopy = True
    
        def __init__(self, item):
            Exporter.__init__(self, item)
            tr = self.getTargetRect()
            if isinstance(item, QtGui.QGraphicsItem):
                scene = item.scene()
            else:
                scene = item
            # scene.views()[0].backgroundBrush()
            bgbrush = pg.mkBrush('w')
            bg = bgbrush.color()
            if bgbrush.style() == QtCore.Qt.NoBrush:
                bg.setAlpha(0)
    
            self.params = Parameter(name='params', type='group', children=[
                {'name': 'width', 'type': 'int',
                    'value': tr.width(), 'limits': (0, None)},
                {'name': 'height', 'type': 'int',
                    'value': tr.height(), 'limits': (0, None)},
                {'name': 'antialias', 'type': 'bool', 'value': True},
                {'name': 'background', 'type': 'color', 'value': bg},
            ])
            self.params.param('width').sigValueChanged.connect(self.widthChanged)
            self.params.param('height').sigValueChanged.connect(self.heightChanged)
    
        def widthChanged(self):
            sr = self.getSourceRect()
            ar = float(sr.height()) / sr.width()
            self.params.param('height').setValue(
                self.params['width'] * ar, blockSignal=self.heightChanged)
    
        def heightChanged(self):
            sr = self.getSourceRect()
            ar = float(sr.width()) / sr.height()
            self.params.param('width').setValue(
                self.params['height'] * ar, blockSignal=self.widthChanged)
    
        def parameters(self):
            return self.params
    
        def export(self, fileName=None, toBytes=False, copy=False):
            if fileName is None and not toBytes and not copy:
                if USE_PYSIDE:
                    filter = ["*."+str(f)
                              for f in QtGui.QImageWriter.supportedImageFormats()]
                else:
                    filter = ["*."+bytes(f).decode('utf-8')
                              for f in QtGui.QImageWriter.supportedImageFormats()]
                preferred = ['*.png', '*.tif', '*.jpg']
                for p in preferred[::-1]:
                    if p in filter:
                        filter.remove(p)
                        filter.insert(0, p)
                self.fileSaveDialog(filter=filter)
                return
    
            targetRect = QtCore.QRect(
                0, 0, self.params['width'], self.params['height'])
            sourceRect = self.getSourceRect()
    
            #self.png = QtGui.QImage(targetRect.size(), QtGui.QImage.Format_ARGB32)
            # self.png.fill(pyqtgraph.mkColor(self.params['background']))
            w, h = self.params['width'], self.params['height']
            if w == 0 or h == 0:
                raise Exception(
                    "Cannot export image with size=0 (requested export size is %dx%d)" % (w, h))
            bg = np.empty((int(self.params['width']), int(
                self.params['height']), 4), dtype=np.ubyte)
            color = self.params['background']
            bg[:, :, 0] = color.blue()
            bg[:, :, 1] = color.green()
            bg[:, :, 2] = color.red()
            bg[:, :, 3] = color.alpha()
            self.png = fn.makeQImage(bg, alpha=True)
    
            # set resolution of image:
            origTargetRect = self.getTargetRect()
            resolutionScale = targetRect.width() / origTargetRect.width()
            #self.png.setDotsPerMeterX(self.png.dotsPerMeterX() * resolutionScale)
            #self.png.setDotsPerMeterY(self.png.dotsPerMeterY() * resolutionScale)
    
            painter = QtGui.QPainter(self.png)
            #dtr = painter.deviceTransform()
            try:
                self.setExportMode(True, {
                                   'antialias': self.params['antialias'], 'background': self.params['background'], 'painter': painter, 'resolutionScale': resolutionScale})
                painter.setRenderHint(
                    QtGui.QPainter.Antialiasing, self.params['antialias'])
                self.getScene().render(painter, QtCore.QRectF(
                    targetRect), QtCore.QRectF(sourceRect))
            finally:
                self.setExportMode(False)
            painter.end()
    
            if copy:
                QtGui.QApplication.clipboard().setImage(self.png)
            elif toBytes:
                return self.png
            else:
                self.png.save(fileName)
    
    
    PQG_ImageExporter.register()