diff --git a/orangewidget/io.py b/orangewidget/io.py index 0b85f3cdf..1cb6f905b 100644 --- a/orangewidget/io.py +++ b/orangewidget/io.py @@ -130,8 +130,7 @@ def save_pyqtgraph(): exporter = cls._get_exporter() scene = object.scene() if scene is None: - cls._export(exporter(scene), filename) - return + return cls._export(exporter(scene), filename) views = scene.views() if views: # preserve scene rect and background brush @@ -141,13 +140,13 @@ def save_pyqtgraph(): view = scene.views()[0] scene.setSceneRect(view.sceneRect()) scene.setBackgroundBrush(effective_background(scene, view)) - cls._export(exporter(scene), filename) + return cls._export(exporter(scene), filename) finally: # reset scene rect and background brush scene.setBackgroundBrush(backgroundbrush) scene.setSceneRect(scenerect) else: - cls._export(exporter(scene), filename) + return cls._export(exporter(scene), filename) def save_scene(): assert isinstance(object, QGraphicsScene) @@ -155,8 +154,7 @@ def save_scene(): views = object.views() if not views: rect = object.itemsBoundingRect() - _render(rect, ratio, rect.size(), object) - return + return _render(rect, ratio, rect.size(), object) # Pick the first view. If there's a widget with multiple views that # cares which one is used, it must set graph_name to view, not scene @@ -166,11 +164,11 @@ def save_scene(): source_rect = QRect( int(target_rect.x()), int(target_rect.y()), int(target_rect.width()), int(target_rect.height())) - _render(source_rect, ratio, target_rect.size(), view) + return _render(source_rect, ratio, target_rect.size(), view) def save_widget(): assert isinstance(object, QWidget) - _render(object.rect(), object.devicePixelRatio(), object.size(), + return _render(object.rect(), object.devicePixelRatio(), object.size(), object) def _render( @@ -203,13 +201,16 @@ def _render( # not a core dump painter.end() cls._save_buffer(buffer, filename) + return dict(width=buffer.size().width(), + height=buffer.size().height(), + pixel_ratio=pixel_ratio) if isinstance(object, GraphicsItem): - save_pyqtgraph() + return save_pyqtgraph() elif isinstance(object, QGraphicsScene): - save_scene() + return save_scene() elif isinstance(object, QWidget): # this includes QGraphicsView - save_widget() + return save_widget() else: raise TypeError(f"{cls.__name__} " f"cannot imagine {type(object).__name__}") @@ -218,7 +219,7 @@ def _render( def write(cls, filename, scene): if type(scene) == dict: scene = scene['scene'] - cls.write_image(filename, scene) + return cls.write_image(filename, scene) @classproperty def img_writers(cls): # type: () -> Mapping[str, Type[ImgFormat]] @@ -260,7 +261,11 @@ def _setup_painter(cls, painter, object, source_rect, buffer): @staticmethod def _save_buffer(buffer, filename): - buffer.save(filename, "png") + image = buffer.toImage() + dpm = 2835 * image.devicePixelRatio() + image.setDotsPerMeterX(dpm) + image.setDotsPerMeterY(dpm) + image.save(filename, "png") @staticmethod def _get_exporter(): @@ -300,6 +305,9 @@ def export(self, fileName=None, toBytes=False, copy=False): QtGui.QImage.Format.Format_ARGB32) self.png.fill(self.params['background']) self.png.setDevicePixelRatio(self.ratio) + dpm = 2835 * self.ratio + self.png.setDotsPerMeterX(dpm) + self.png.setDotsPerMeterY(dpm) ## set resolution of image: origTargetRect = self.getTargetRect() @@ -348,6 +356,9 @@ def export(self, fileName=None, toBytes=False, copy=False): def _export(exporter, filename): buffer = exporter.export(toBytes=True) buffer.save(filename, "png") + return dict(width=buffer.size().width(), + height=buffer.size().height(), + pixel_ratio=buffer.devicePixelRatio()) class ClipboardFormat(PngFormat): diff --git a/orangewidget/report/report.py b/orangewidget/report/report.py index 15f8c1bb8..c2d3910c7 100644 --- a/orangewidget/report/report.py +++ b/orangewidget/report/report.py @@ -627,13 +627,17 @@ def get_html_img( byte_array = QByteArray() filename = QBuffer(byte_array) filename.open(QIODevice.WriteOnly) - PngFormat.write(filename, scene) + img_data = PngFormat.write(filename, scene) + img_encoded = byte_array.toBase64().data().decode("utf-8") - return ''.format( - ("" if max_height is None - else 'style="max-height: {}px"'.format(max_height)), - img_encoded - ) + + style_opts = [] + if max_height is not None: + style_opts.append(f"max-height: {max_height}px") + if img_data is not None and (ratio := img_data.get("pixel_ratio", 1)) != 1: + style_opts.append(f"zoom: {1 / ratio:.1f}") + style = f' style="{"; ".join(style_opts)}"' if style_opts else '' + return f'' def get_icon_html(icon: QIcon, size: QSize) -> str: