1#!/usr/bin/env python 2 3 4############################################################################# 5## 6## Copyright (C) 2013 Riverbank Computing Limited. 7## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). 8## All rights reserved. 9## 10## This file is part of the examples of PyQt. 11## 12## $QT_BEGIN_LICENSE:BSD$ 13## You may use this file under the terms of the BSD license as follows: 14## 15## "Redistribution and use in source and binary forms, with or without 16## modification, are permitted provided that the following conditions are 17## met: 18## * Redistributions of source code must retain the above copyright 19## notice, this list of conditions and the following disclaimer. 20## * Redistributions in binary form must reproduce the above copyright 21## notice, this list of conditions and the following disclaimer in 22## the documentation and/or other materials provided with the 23## distribution. 24## * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor 25## the names of its contributors may be used to endorse or promote 26## products derived from this software without specific prior written 27## permission. 28## 29## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 30## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 31## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 32## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 33## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 34## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 35## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 36## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 38## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 39## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 40## $QT_END_LICENSE$ 41## 42############################################################################# 43 44 45from PyQt5.QtCore import QDir, Qt 46from PyQt5.QtGui import QImage, QPainter, QPalette, QPixmap 47from PyQt5.QtWidgets import (QAction, QApplication, QFileDialog, QLabel, 48 QMainWindow, QMenu, QMessageBox, QScrollArea, QSizePolicy) 49from PyQt5.QtPrintSupport import QPrintDialog, QPrinter 50 51 52class ImageViewer(QMainWindow): 53 def __init__(self): 54 super(ImageViewer, self).__init__() 55 56 self.printer = QPrinter() 57 self.scaleFactor = 0.0 58 59 self.imageLabel = QLabel() 60 self.imageLabel.setBackgroundRole(QPalette.Base) 61 self.imageLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) 62 self.imageLabel.setScaledContents(True) 63 64 self.scrollArea = QScrollArea() 65 self.scrollArea.setBackgroundRole(QPalette.Dark) 66 self.scrollArea.setWidget(self.imageLabel) 67 self.setCentralWidget(self.scrollArea) 68 69 self.createActions() 70 self.createMenus() 71 72 self.setWindowTitle("Image Viewer") 73 self.resize(500, 400) 74 75 def open(self): 76 fileName, _ = QFileDialog.getOpenFileName(self, "Open File", 77 QDir.currentPath()) 78 if fileName: 79 image = QImage(fileName) 80 if image.isNull(): 81 QMessageBox.information(self, "Image Viewer", 82 "Cannot load %s." % fileName) 83 return 84 85 self.imageLabel.setPixmap(QPixmap.fromImage(image)) 86 self.scaleFactor = 1.0 87 88 self.printAct.setEnabled(True) 89 self.fitToWindowAct.setEnabled(True) 90 self.updateActions() 91 92 if not self.fitToWindowAct.isChecked(): 93 self.imageLabel.adjustSize() 94 95 def print_(self): 96 dialog = QPrintDialog(self.printer, self) 97 if dialog.exec_(): 98 painter = QPainter(self.printer) 99 rect = painter.viewport() 100 size = self.imageLabel.pixmap().size() 101 size.scale(rect.size(), Qt.KeepAspectRatio) 102 painter.setViewport(rect.x(), rect.y(), size.width(), size.height()) 103 painter.setWindow(self.imageLabel.pixmap().rect()) 104 painter.drawPixmap(0, 0, self.imageLabel.pixmap()) 105 106 def zoomIn(self): 107 self.scaleImage(1.25) 108 109 def zoomOut(self): 110 self.scaleImage(0.8) 111 112 def normalSize(self): 113 self.imageLabel.adjustSize() 114 self.scaleFactor = 1.0 115 116 def fitToWindow(self): 117 fitToWindow = self.fitToWindowAct.isChecked() 118 self.scrollArea.setWidgetResizable(fitToWindow) 119 if not fitToWindow: 120 self.normalSize() 121 122 self.updateActions() 123 124 def about(self): 125 QMessageBox.about(self, "About Image Viewer", 126 "<p>The <b>Image Viewer</b> example shows how to combine " 127 "QLabel and QScrollArea to display an image. QLabel is " 128 "typically used for displaying text, but it can also display " 129 "an image. QScrollArea provides a scrolling view around " 130 "another widget. If the child widget exceeds the size of the " 131 "frame, QScrollArea automatically provides scroll bars.</p>" 132 "<p>The example demonstrates how QLabel's ability to scale " 133 "its contents (QLabel.scaledContents), and QScrollArea's " 134 "ability to automatically resize its contents " 135 "(QScrollArea.widgetResizable), can be used to implement " 136 "zooming and scaling features.</p>" 137 "<p>In addition the example shows how to use QPainter to " 138 "print an image.</p>") 139 140 def createActions(self): 141 self.openAct = QAction("&Open...", self, shortcut="Ctrl+O", 142 triggered=self.open) 143 144 self.printAct = QAction("&Print...", self, shortcut="Ctrl+P", 145 enabled=False, triggered=self.print_) 146 147 self.exitAct = QAction("E&xit", self, shortcut="Ctrl+Q", 148 triggered=self.close) 149 150 self.zoomInAct = QAction("Zoom &In (25%)", self, shortcut="Ctrl++", 151 enabled=False, triggered=self.zoomIn) 152 153 self.zoomOutAct = QAction("Zoom &Out (25%)", self, shortcut="Ctrl+-", 154 enabled=False, triggered=self.zoomOut) 155 156 self.normalSizeAct = QAction("&Normal Size", self, shortcut="Ctrl+S", 157 enabled=False, triggered=self.normalSize) 158 159 self.fitToWindowAct = QAction("&Fit to Window", self, enabled=False, 160 checkable=True, shortcut="Ctrl+F", triggered=self.fitToWindow) 161 162 self.aboutAct = QAction("&About", self, triggered=self.about) 163 164 self.aboutQtAct = QAction("About &Qt", self, 165 triggered=QApplication.instance().aboutQt) 166 167 def createMenus(self): 168 self.fileMenu = QMenu("&File", self) 169 self.fileMenu.addAction(self.openAct) 170 self.fileMenu.addAction(self.printAct) 171 self.fileMenu.addSeparator() 172 self.fileMenu.addAction(self.exitAct) 173 174 self.viewMenu = QMenu("&View", self) 175 self.viewMenu.addAction(self.zoomInAct) 176 self.viewMenu.addAction(self.zoomOutAct) 177 self.viewMenu.addAction(self.normalSizeAct) 178 self.viewMenu.addSeparator() 179 self.viewMenu.addAction(self.fitToWindowAct) 180 181 self.helpMenu = QMenu("&Help", self) 182 self.helpMenu.addAction(self.aboutAct) 183 self.helpMenu.addAction(self.aboutQtAct) 184 185 self.menuBar().addMenu(self.fileMenu) 186 self.menuBar().addMenu(self.viewMenu) 187 self.menuBar().addMenu(self.helpMenu) 188 189 def updateActions(self): 190 self.zoomInAct.setEnabled(not self.fitToWindowAct.isChecked()) 191 self.zoomOutAct.setEnabled(not self.fitToWindowAct.isChecked()) 192 self.normalSizeAct.setEnabled(not self.fitToWindowAct.isChecked()) 193 194 def scaleImage(self, factor): 195 self.scaleFactor *= factor 196 self.imageLabel.resize(self.scaleFactor * self.imageLabel.pixmap().size()) 197 198 self.adjustScrollBar(self.scrollArea.horizontalScrollBar(), factor) 199 self.adjustScrollBar(self.scrollArea.verticalScrollBar(), factor) 200 201 self.zoomInAct.setEnabled(self.scaleFactor < 3.0) 202 self.zoomOutAct.setEnabled(self.scaleFactor > 0.333) 203 204 def adjustScrollBar(self, scrollBar, factor): 205 scrollBar.setValue(int(factor * scrollBar.value() 206 + ((factor - 1) * scrollBar.pageStep()/2))) 207 208 209if __name__ == '__main__': 210 211 import sys 212 213 app = QApplication(sys.argv) 214 imageViewer = ImageViewer() 215 imageViewer.show() 216 sys.exit(app.exec_()) 217