diff options
Diffstat (limited to 'examples/demos/documentviewer/imageviewer/imageviewer.py')
| -rw-r--r-- | examples/demos/documentviewer/imageviewer/imageviewer.py | 173 |
1 files changed, 173 insertions, 0 deletions
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) |
