aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEce Cinucen <[email protected]>2025-01-29 16:30:46 +0100
committerFriedemann Kleint <[email protected]>2025-01-30 10:22:36 +0000
commit78aedfbbc900f008e02ff73517f4c2b5e07cec30 (patch)
treea05f99664660eeb64cf9cdc569c4912924f17f6a
parent6c51abbf86d75828a0898147c568685fb64698f5 (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]>
-rw-r--r--examples/demos/documentviewer/doc/imageviewer.py.rstinc11
-rw-r--r--examples/demos/documentviewer/documentviewer.pyproject1
-rw-r--r--examples/demos/documentviewer/imageviewer/imageviewer.py173
-rw-r--r--examples/demos/documentviewer/pdfviewer/pdfviewer.py2
-rw-r--r--examples/demos/documentviewer/viewerfactory.py3
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