-
-
Save acbetter/32c575803ec361c3e82064e60db4e3e0 to your computer and use it in GitHub Desktop.
#!/usr/bin/python3 | |
# -*- coding: utf-8 -*- | |
from PyQt5.QtCore import Qt | |
from PyQt5.QtGui import QImage, QPixmap, QPalette, QPainter | |
from PyQt5.QtPrintSupport import QPrintDialog, QPrinter | |
from PyQt5.QtWidgets import QLabel, QSizePolicy, QScrollArea, QMessageBox, QMainWindow, QMenu, QAction, \ | |
qApp, QFileDialog | |
class QImageViewer(QMainWindow): | |
def __init__(self): | |
super().__init__() | |
self.printer = QPrinter() | |
self.scaleFactor = 0.0 | |
self.imageLabel = QLabel() | |
self.imageLabel.setBackgroundRole(QPalette.Base) | |
self.imageLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) | |
self.imageLabel.setScaledContents(True) | |
self.scrollArea = QScrollArea() | |
self.scrollArea.setBackgroundRole(QPalette.Dark) | |
self.scrollArea.setWidget(self.imageLabel) | |
self.scrollArea.setVisible(False) | |
self.setCentralWidget(self.scrollArea) | |
self.createActions() | |
self.createMenus() | |
self.setWindowTitle("Image Viewer") | |
self.resize(800, 600) | |
def open(self): | |
options = QFileDialog.Options() | |
# fileName = QFileDialog.getOpenFileName(self, "Open File", QDir.currentPath()) | |
fileName, _ = QFileDialog.getOpenFileName(self, 'QFileDialog.getOpenFileName()', '', | |
'Images (*.png *.jpeg *.jpg *.bmp *.gif)', options=options) | |
if fileName: | |
image = QImage(fileName) | |
if image.isNull(): | |
QMessageBox.information(self, "Image Viewer", "Cannot load %s." % fileName) | |
return | |
self.imageLabel.setPixmap(QPixmap.fromImage(image)) | |
self.scaleFactor = 1.0 | |
self.scrollArea.setVisible(True) | |
self.printAct.setEnabled(True) | |
self.fitToWindowAct.setEnabled(True) | |
self.updateActions() | |
if not self.fitToWindowAct.isChecked(): | |
self.imageLabel.adjustSize() | |
def print_(self): | |
dialog = QPrintDialog(self.printer, self) | |
if dialog.exec_(): | |
painter = QPainter(self.printer) | |
rect = painter.viewport() | |
size = self.imageLabel.pixmap().size() | |
size.scale(rect.size(), Qt.KeepAspectRatio) | |
painter.setViewport(rect.x(), rect.y(), size.width(), size.height()) | |
painter.setWindow(self.imageLabel.pixmap().rect()) | |
painter.drawPixmap(0, 0, self.imageLabel.pixmap()) | |
def zoomIn(self): | |
self.scaleImage(1.25) | |
def zoomOut(self): | |
self.scaleImage(0.8) | |
def normalSize(self): | |
self.imageLabel.adjustSize() | |
self.scaleFactor = 1.0 | |
def fitToWindow(self): | |
fitToWindow = self.fitToWindowAct.isChecked() | |
self.scrollArea.setWidgetResizable(fitToWindow) | |
if not fitToWindow: | |
self.normalSize() | |
self.updateActions() | |
def about(self): | |
QMessageBox.about(self, "About Image Viewer", | |
"<p>The <b>Image Viewer</b> example shows how to combine " | |
"QLabel and QScrollArea to display an image. QLabel is " | |
"typically used for displaying text, but it can also display " | |
"an image. QScrollArea provides a scrolling view around " | |
"another widget. If the child widget exceeds the size of the " | |
"frame, QScrollArea automatically provides scroll bars.</p>" | |
"<p>The example demonstrates how QLabel's ability to scale " | |
"its contents (QLabel.scaledContents), and QScrollArea's " | |
"ability to automatically resize its contents " | |
"(QScrollArea.widgetResizable), can be used to implement " | |
"zooming and scaling features.</p>" | |
"<p>In addition the example shows how to use QPainter to " | |
"print an image.</p>") | |
def createActions(self): | |
self.openAct = QAction("&Open...", self, shortcut="Ctrl+O", triggered=self.open) | |
self.printAct = QAction("&Print...", self, shortcut="Ctrl+P", enabled=False, triggered=self.print_) | |
self.exitAct = QAction("E&xit", self, shortcut="Ctrl+Q", triggered=self.close) | |
self.zoomInAct = QAction("Zoom &In (25%)", self, shortcut="Ctrl++", enabled=False, triggered=self.zoomIn) | |
self.zoomOutAct = QAction("Zoom &Out (25%)", self, shortcut="Ctrl+-", enabled=False, triggered=self.zoomOut) | |
self.normalSizeAct = QAction("&Normal Size", self, shortcut="Ctrl+S", enabled=False, triggered=self.normalSize) | |
self.fitToWindowAct = QAction("&Fit to Window", self, enabled=False, checkable=True, shortcut="Ctrl+F", | |
triggered=self.fitToWindow) | |
self.aboutAct = QAction("&About", self, triggered=self.about) | |
self.aboutQtAct = QAction("About &Qt", self, triggered=qApp.aboutQt) | |
def createMenus(self): | |
self.fileMenu = QMenu("&File", self) | |
self.fileMenu.addAction(self.openAct) | |
self.fileMenu.addAction(self.printAct) | |
self.fileMenu.addSeparator() | |
self.fileMenu.addAction(self.exitAct) | |
self.viewMenu = QMenu("&View", self) | |
self.viewMenu.addAction(self.zoomInAct) | |
self.viewMenu.addAction(self.zoomOutAct) | |
self.viewMenu.addAction(self.normalSizeAct) | |
self.viewMenu.addSeparator() | |
self.viewMenu.addAction(self.fitToWindowAct) | |
self.helpMenu = QMenu("&Help", self) | |
self.helpMenu.addAction(self.aboutAct) | |
self.helpMenu.addAction(self.aboutQtAct) | |
self.menuBar().addMenu(self.fileMenu) | |
self.menuBar().addMenu(self.viewMenu) | |
self.menuBar().addMenu(self.helpMenu) | |
def updateActions(self): | |
self.zoomInAct.setEnabled(not self.fitToWindowAct.isChecked()) | |
self.zoomOutAct.setEnabled(not self.fitToWindowAct.isChecked()) | |
self.normalSizeAct.setEnabled(not self.fitToWindowAct.isChecked()) | |
def scaleImage(self, factor): | |
self.scaleFactor *= factor | |
self.imageLabel.resize(self.scaleFactor * self.imageLabel.pixmap().size()) | |
self.adjustScrollBar(self.scrollArea.horizontalScrollBar(), factor) | |
self.adjustScrollBar(self.scrollArea.verticalScrollBar(), factor) | |
self.zoomInAct.setEnabled(self.scaleFactor < 3.0) | |
self.zoomOutAct.setEnabled(self.scaleFactor > 0.333) | |
def adjustScrollBar(self, scrollBar, factor): | |
scrollBar.setValue(int(factor * scrollBar.value() | |
+ ((factor - 1) * scrollBar.pageStep() / 2))) | |
if __name__ == '__main__': | |
import sys | |
from PyQt5.QtWidgets import QApplication | |
app = QApplication(sys.argv) | |
imageViewer = QImageViewer() | |
imageViewer.show() | |
sys.exit(app.exec_()) | |
# TODO QScrollArea support mouse | |
# base on https://github.com/baoboa/pyqt5/blob/master/examples/widgets/imageviewer.py | |
# | |
# if you need Two Image Synchronous Scrolling in the window by PyQt5 and Python 3 | |
# please visit https://gist.github.com/acbetter/e7d0c600fdc0865f4b0ee05a17b858f2 |
Thank you for this code. How can I make "Fit to window" not distort the image?
Added Fit To Width Function Maestro67au/QT5_Image_Viewer
For those that are interested, added OCR, directory selection, file selection. Just an example of what can be done.
https://github.com/Maestro67au/QT5_OCR_Tool
amazing !!!
@Maestro67au The shared link is broken, but, nice job. I think you meant to share this: https://github.com/Maestro67au/QT5_OCR_Tool
Hello @acbetter . Very good code, great job, keep up the good work! I'm using part of your code and I have a problem that I can't solve. I want to create a vertical line in this place by clicking the mouse on an image (pixmap) (as I understand it, I need to work with Qpainter), but for reasons unknown to me I can’t do this. If you were able to make such a large application, I think you might have some ideas about it. Thank you very much in advance!
Thank @Lightning228 , I do appreciate that. I think you can listen mouse move/click event and use this event to generate a new(modified) image and replace it by self.imageLabel.setPixmap(QPixmap.fromImage(your_new_image))
There's a example of mouse event listening but verticalScrollBar https://gist.github.com/acbetter/e7d0c600fdc0865f4b0ee05a17b858f2
Thanks man!