diff options
author | Ece Cinucen <[email protected]> | 2025-01-29 16:30:46 +0100 |
---|---|---|
committer | Friedemann Kleint <[email protected]> | 2025-01-30 10:22:36 +0000 |
commit | 78aedfbbc900f008e02ff73517f4c2b5e07cec30 (patch) | |
tree | a05f99664660eeb64cf9cdc569c4912924f17f6a | |
parent | 6c51abbf86d75828a0898147c568685fb64698f5 (diff) |
Example: Add imageviewer example
Add missing example from c++ (ported qtdoc/09bc4a6861b3a4d0ad1e7dc214b3fe1b6b423504).
Zoom out icon for pdfviewer corrected
Pick-to: 6.8
Change-Id: I1633a564e565d8f49892d7b6703d92a556c654f7
Reviewed-by: Friedemann Kleint <[email protected]>
5 files changed, 188 insertions, 2 deletions
diff --git a/examples/demos/documentviewer/doc/imageviewer.py.rstinc b/examples/demos/documentviewer/doc/imageviewer.py.rstinc new file mode 100644 index 000000000..2aeaaf4a4 --- /dev/null +++ b/examples/demos/documentviewer/doc/imageviewer.py.rstinc @@ -0,0 +1,11 @@ +``ImageViewer`` displays images as supported by ``QImageReader``, using +a QLabel. + +In the constructor, we increase the allocation limit of ``QImageReader`` to +allow for larger photos. + +In the ``openFile()`` function, we load the image and determine its size. +If it is larger than the screen, we downscale it to screen size, maintaining +the aspect ratio. This calculation has to be done in native pixels, and +the device pixel ratio needs to be set on the resulting pixmap for it to +appear crisp. diff --git a/examples/demos/documentviewer/documentviewer.pyproject b/examples/demos/documentviewer/documentviewer.pyproject index 461e3b9d7..fe1a4dbf7 100644 --- a/examples/demos/documentviewer/documentviewer.pyproject +++ b/examples/demos/documentviewer/documentviewer.pyproject @@ -4,6 +4,7 @@ "main.py", "mainwindow.py", "mainwindow.ui", + "imageviewer/imageviewer.py", "jsonviewer/jsonviewer.py", "pdfviewer/pdfviewer.py", "pdfviewer/zoomselector.py", diff --git a/examples/demos/documentviewer/imageviewer/imageviewer.py b/examples/demos/documentviewer/imageviewer/imageviewer.py new file mode 100644 index 000000000..a2155acac --- /dev/null +++ b/examples/demos/documentviewer/imageviewer/imageviewer.py @@ -0,0 +1,173 @@ +# Copyright (C) 2025 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +from __future__ import annotations + +import math + +from PySide6.QtWidgets import QLabel +from PySide6.QtCore import Qt, QDir, QSizeF +from PySide6.QtGui import (QPixmap, QImageReader, QIcon, QKeySequence, + QGuiApplication, QColorSpace, QPainter, QAction) + +from abstractviewer import AbstractViewer + + +def imageFormats(): + result = [] + all_formats = QImageReader.supportedImageFormats() + + for format_bytes in all_formats: + format_str = bytes(format_bytes).decode("utf-8") # Convert QByteArray to str + if format_str not in ["tif", "cur"]: # Exclude duplicate/non-existent formats + result.append(f"image/{format_str}") + + return result + + +def msgOpen(name, image): + description = image.colorSpace().description() if image.colorSpace().isValid() else "unknown" + return 'Opened "{0}", {1}x{2}, Depth: {3} ({4})'.format( + QDir.toNativeSeparators(name), + image.width(), + image.height(), + image.depth(), + description + ) + + +class ImageViewer(AbstractViewer): + + def __init__(self): + super().__init__() + + self.formats = imageFormats() + self.uiInitialized.connect(self.setupImageUi) + QImageReader.setAllocationLimit(1024) # MB + + def init(self, file, parent, mainWindow): + self.image_label = QLabel(parent) + self.image_label.setFrameShape(QLabel.Box) + self.image_label.setAlignment(Qt.AlignCenter) + self.image_label.setScaledContents(True) + + # AbstractViewer.init(file, self.image_label, mainWindow) + super().init(file, self.image_label, mainWindow) + + self.tool_bar = self.addToolBar(self.tr("Images")) + + icon = QIcon.fromTheme(QIcon.ThemeIcon.ZoomIn, + QIcon(":/demos/documentviewer/images/zoom-in.png")) + self.zoom_in_act = QAction(icon, "Zoom &In", self) + self.zoom_in_act.setShortcut(QKeySequence.StandardKey.ZoomIn) + self.zoom_in_act.triggered.connect(self.zoomIn) + self.tool_bar.addAction(self.zoom_in_act) + + icon = QIcon.fromTheme(QIcon.ThemeIcon.ZoomOut, + QIcon(":/demos/documentviewer/images/zoom-out.png")) + self.zoom_out_act = QAction(icon, "Zoom &Out", self) + self.zoom_out_act.setShortcut(QKeySequence.StandardKey.ZoomOut) + self.zoom_out_act.triggered.connect(self.zoomOut) + self.tool_bar.addAction(self.zoom_out_act) + + icon = QIcon.fromTheme(QIcon.ThemeIcon.ZoomFitBest, + QIcon(":/demos/documentviewer/images/zoom-fit-best.png")) + self.reset_zoom_act = QAction(icon, "Reset Zoom", self) + self.reset_zoom_act.setShortcut(QKeySequence + (Qt.KeyboardModifier.ControlModifier | Qt.Key.Key_0)) + self.reset_zoom_act.triggered.connect(self.resetZoom) + self.tool_bar.addAction(self.reset_zoom_act) + + def supportedMimeTypes(self): + return self.formats + + def clear(self): + self.image_label.setPixmap(QPixmap()) + self.max_scale_factor = self.min_scale_factor = 1 + self.initial_scale_factor = self.scale_factor = 1 + + def setupImageUi(self): + self.openFile() + + def openFile(self): + + QGuiApplication.setOverrideCursor(Qt.WaitCursor) + + name = self._file.fileName() + reader = QImageReader(name) + orig_image = reader.read() + + if orig_image.isNull(): + self.statusMessage(f"Cannot read file {name}:\n{reader.errorString()}", "open") + self.disablePrinting() + QGuiApplication.restoreOverrideCursor() + return + + self.clear() + + if orig_image.colorSpace().isValid(): + image = orig_image.convertedToColorSpace(QColorSpace.SRgb) + else: + image = orig_image + + device_pixel_ratio = self.image_label.devicePixelRatioF() + self.image_size = QSizeF(image.size()) / device_pixel_ratio + + pixmap = QPixmap.fromImage(image) + pixmap.setDevicePixelRatio(device_pixel_ratio) + self.image_label.setPixmap(pixmap) + + target_size = self.image_label.parentWidget().size() + if (self.image_size.width() > target_size.width() + or self.image_size.height() > target_size.height()): + self.initial_scale_factor = min(target_size.width() / self.image_size.width(), + target_size.height() / self.image_size.height()) + + self.max_scale_factor = 3 * self.initial_scale_factor + self.min_scale_factor = self.initial_scale_factor / 3 + self.doSetScaleFactor(self.initial_scale_factor) + + self.statusMessage(msgOpen(name, orig_image)) + QGuiApplication.restoreOverrideCursor() + + self.maybeEnablePrinting() + + def setScaleFactor(self, scaleFactor): + if not math.isclose(self.scale_factor, scaleFactor): + self.doSetScaleFactor(scaleFactor) + + def doSetScaleFactor(self, scaleFactor): + self.scale_factor = scaleFactor + label_size = (self.image_size * self.scale_factor).toSize() + self.image_label.setFixedSize(label_size) + self.enableZoomActions() + + def zoomIn(self): + self.setScaleFactor(self.scale_factor * 1.25) + + def zoomOut(self): + self.setScaleFactor(self.scale_factor * 0.8) + + def resetZoom(self): + self.setScaleFactor(self.initial_scale_factor) + + def hasContent(self): + return not self.image_label.pixmap().isNull() + + def enableZoomActions(self): + self.reset_zoom_act.setEnabled(not math.isclose(self.scale_factor, + self.initial_scale_factor)) + self.zoom_in_act.setEnabled(self.scale_factor < self.max_scale_factor) + self.zoom_out_act.setEnabled(self.scale_factor > self.min_scale_factor) + + def printDocument(self, printer): + if not self.hasContent(): + return + + painter = QPainter(printer) + pixmap = self.image_label.pixmap() + rect = painter.viewport() + size = pixmap.size() + size.scale(rect.size(), Qt.KeepAspectRatio) + painter.setViewport(rect.x(), rect.y(), size.width(), size.height()) + painter.setWindow(pixmap.rect()) + painter.drawPixmap(0, 0, pixmap) diff --git a/examples/demos/documentviewer/pdfviewer/pdfviewer.py b/examples/demos/documentviewer/pdfviewer/pdfviewer.py index a1f4e7447..a2de67ada 100644 --- a/examples/demos/documentviewer/pdfviewer/pdfviewer.py +++ b/examples/demos/documentviewer/pdfviewer/pdfviewer.py @@ -67,7 +67,7 @@ class PdfViewer(AbstractViewer): actionZoomIn.setToolTip("Increase zoom level") actionZoomIn.triggered.connect(self.onActionZoomInTriggered) - icon = QIcon.fromTheme(QIcon.ThemeIcon.ZoomIn, + icon = QIcon.fromTheme(QIcon.ThemeIcon.ZoomOut, QIcon(":/demos/documentviewer/images/zoom-out.png")) actionZoomOut = self._toolBar.addAction(icon, "Zoom out", QKeySequence.StandardKey.ZoomOut) actionZoomOut.setToolTip("Decrease zoom level") diff --git a/examples/demos/documentviewer/viewerfactory.py b/examples/demos/documentviewer/viewerfactory.py index 0d32cbfeb..19b9f6a3a 100644 --- a/examples/demos/documentviewer/viewerfactory.py +++ b/examples/demos/documentviewer/viewerfactory.py @@ -10,6 +10,7 @@ from PySide6.QtCore import (QFileInfo, QMimeDatabase, QTimer) from txtviewer.txtviewer import TxtViewer from jsonviewer.jsonviewer import JsonViewer from pdfviewer.pdfviewer import PdfViewer +from imageviewer.imageviewer import ImageViewer class DefaultPolicy(Enum): @@ -29,7 +30,7 @@ class ViewerFactory: self._displayWidget = displayWidget self._mainWindow = mainWindow self._mimeTypes = [] - for v in [PdfViewer(), JsonViewer(), TxtViewer()]: + for v in [PdfViewer(), JsonViewer(), TxtViewer(), ImageViewer()]: self._viewers[v.viewerName()] = v if v.isDefaultViewer(): self._defaultViewer = v |