Skip to content

Instantly share code, notes, and snippets.

@acbetter
Last active June 30, 2024 20:06
Show Gist options
  • Save acbetter/32c575803ec361c3e82064e60db4e3e0 to your computer and use it in GitHub Desktop.
Save acbetter/32c575803ec361c3e82064e60db4e3e0 to your computer and use it in GitHub Desktop.
Image Viewer Example by PyQt5 and Python 3
#!/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
@neoronbug
Copy link

Thanks man!

@agaitanis
Copy link

Thank you for this code. How can I make "Fit to window" not distort the image?

@Maestro67au
Copy link

Maestro67au commented Jan 1, 2023

Added Fit To Width Function Maestro67au/QT5_Image_Viewer

@Maestro67au
Copy link

Maestro67au commented Jan 2, 2023

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

@irfanykywz
Copy link

amazing !!!

@Tuhin-thinks
Copy link

@Maestro67au The shared link is broken, but, nice job. I think you meant to share this: https://github.com/Maestro67au/QT5_OCR_Tool

@Lightning228
Copy link

Lightning228 commented Feb 21, 2024

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!

@acbetter
Copy link
Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment