Skip to content

Instantly share code, notes, and snippets.

@acbetter
Created July 26, 2018 01:53
Show Gist options
  • Select an option

  • Save acbetter/e7d0c600fdc0865f4b0ee05a17b858f2 to your computer and use it in GitHub Desktop.

Select an option

Save acbetter/e7d0c600fdc0865f4b0ee05a17b858f2 to your computer and use it in GitHub Desktop.
Two Image Synchronous Scrolling in the window 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, QWidget, QHBoxLayout
class QImageViewSync(QWidget):
def __init__(self, window=None):
super().__init__()
self.window = window
self.printer = QPrinter()
self.scaleFactor = 0.0
self.imageLabelLeft = QLabel()
self.imageLabelLeft.setBackgroundRole(QPalette.Base)
self.imageLabelLeft.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
self.imageLabelLeft.setScaledContents(True)
self.scrollAreaLeft = QScrollArea()
self.scrollAreaLeft.setBackgroundRole(QPalette.Dark)
self.scrollAreaLeft.setWidget(self.imageLabelLeft)
self.scrollAreaLeft.setVisible(False)
self.imageLabelRight = QLabel()
self.imageLabelRight.setBackgroundRole(QPalette.Base)
self.imageLabelRight.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
self.imageLabelRight.setScaledContents(True)
self.scrollAreaRight = QScrollArea()
self.scrollAreaRight.setBackgroundRole(QPalette.Dark)
self.scrollAreaRight.setWidget(self.imageLabelRight)
self.scrollAreaRight.setVisible(False)
self.centralWidget = QWidget()
self.layout = QHBoxLayout(self.centralWidget)
self.layout.addWidget(self.scrollAreaLeft)
self.layout.addWidget(self.scrollAreaRight)
self.scrollAreaLeft.verticalScrollBar().valueChanged.connect(
self.scrollAreaRight.verticalScrollBar().setValue)
self.scrollAreaLeft.horizontalScrollBar().valueChanged.connect(
self.scrollAreaRight.horizontalScrollBar().setValue)
self.scrollAreaRight.verticalScrollBar().valueChanged.connect(
self.scrollAreaLeft.verticalScrollBar().setValue)
self.scrollAreaRight.horizontalScrollBar().valueChanged.connect(
self.scrollAreaLeft.horizontalScrollBar().setValue)
self.scrollAreaLeft.mouseMoveEvent = self.mouseMoveEventLeft
self.scrollAreaLeft.mousePressEvent = self.mousePressEventLeft
self.scrollAreaLeft.mouseReleaseEvent = self.mouseReleaseEventLeft
self.scrollAreaRight.mouseMoveEvent = self.mouseMoveEventRight
self.scrollAreaRight.mousePressEvent = self.mousePressEventRight
self.scrollAreaRight.mouseReleaseEvent = self.mouseReleaseEventRight
self.imageLabelLeft.setCursor(Qt.OpenHandCursor)
self.imageLabelRight.setCursor(Qt.OpenHandCursor)
def mousePressEventLeft(self, event):
self.pressed = True
self.imageLabelLeft.setCursor(Qt.ClosedHandCursor)
self.initialPosX = self.scrollAreaLeft.horizontalScrollBar().value() + event.pos().x()
self.initialPosY = self.scrollAreaLeft.verticalScrollBar().value() + event.pos().y()
def mouseReleaseEventLeft(self, event):
self.pressed = False
self.imageLabelLeft.setCursor(Qt.OpenHandCursor)
self.initialPosX = self.scrollAreaLeft.horizontalScrollBar().value()
self.initialPosY = self.scrollAreaLeft.verticalScrollBar().value()
def mouseMoveEventLeft(self, event):
if self.pressed:
self.scrollAreaLeft.horizontalScrollBar().setValue(self.initialPosX - event.pos().x())
self.scrollAreaLeft.verticalScrollBar().setValue(self.initialPosY - event.pos().y())
def mousePressEventRight(self, event):
self.pressed = True
self.imageLabelRight.setCursor(Qt.ClosedHandCursor)
self.initialPosX = self.scrollAreaRight.horizontalScrollBar().value() + event.pos().x()
self.initialPosY = self.scrollAreaRight.verticalScrollBar().value() + event.pos().y()
def mouseReleaseEventRight(self, event):
self.pressed = False
self.imageLabelRight.setCursor(Qt.OpenHandCursor)
self.initialPosX = self.scrollAreaRight.horizontalScrollBar().value()
self.initialPosY = self.scrollAreaRight.verticalScrollBar().value()
def mouseMoveEventRight(self, event):
if self.pressed:
self.scrollAreaRight.horizontalScrollBar().setValue(self.initialPosX - event.pos().x())
self.scrollAreaRight.verticalScrollBar().setValue(self.initialPosY - event.pos().y())
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:
print(fileName)
image = QImage(fileName)
if image.isNull():
QMessageBox.information(self, "Image Viewer", "Cannot load %s." % fileName)
return
self.imageLabelLeft.setPixmap(QPixmap.fromImage(image))
self.imageLabelRight.setPixmap(QPixmap.fromImage(image))
self.scaleFactor = 1.0
self.scrollAreaLeft.setVisible(True)
self.scrollAreaRight.setVisible(True)
self.window.printLeftAct.setEnabled(True)
self.window.printRightAct.setEnabled(True)
self.window.fitToWindowAct.setEnabled(True)
self.updateActions()
if not self.window.fitToWindowAct.isChecked():
self.imageLabelLeft.adjustSize()
self.imageLabelRight.adjustSize()
def openLeft(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:
print(fileName)
image = QImage(fileName)
if image.isNull():
QMessageBox.information(self, "Image Viewer", "Cannot load %s." % fileName)
return
self.imageLabelLeft.setPixmap(QPixmap.fromImage(image))
self.scaleFactor = 1.0
self.scrollAreaLeft.setVisible(True)
self.window.printLeftAct.setEnabled(True)
self.window.fitToWindowAct.setEnabled(True)
self.updateActions()
if not self.window.fitToWindowAct.isChecked():
self.imageLabelLeft.adjustSize()
def openRight(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:
print(fileName)
image = QImage(fileName)
if image.isNull():
QMessageBox.information(self, "Image Viewer", "Cannot load %s." % fileName)
return
self.imageLabelRight.setPixmap(QPixmap.fromImage(image))
self.scaleFactor = 1.0
self.scrollAreaRight.setVisible(True)
self.window.printRightAct.setEnabled(True)
self.window.fitToWindowAct.setEnabled(True)
self.updateActions()
if not self.window.fitToWindowAct.isChecked():
self.imageLabelRight.adjustSize()
def printLeft(self):
dialog = QPrintDialog(self.printer, self)
if dialog.exec_():
painter = QPainter(self.printer)
rect = painter.viewport()
size = self.imageLabelLeft.pixmap().size()
size.scale(rect.size(), Qt.KeepAspectRatio)
painter.setViewport(rect.x(), rect.y(), size.width(), size.height())
painter.setWindow(self.imageLabelLeft.pixmap().rect())
painter.drawPixmap(0, 0, self.imageLabelLeft.pixmap())
def printRight(self):
dialog = QPrintDialog(self.printer, self)
if dialog.exec_():
painter = QPainter(self.printer)
rect = painter.viewport()
size = self.imageLabelRight.pixmap().size()
size.scale(rect.size(), Qt.KeepAspectRatio)
painter.setViewport(rect.x(), rect.y(), size.width(), size.height())
painter.setWindow(self.imageLabelRight.pixmap().rect())
painter.drawPixmap(0, 0, self.imageLabelRight.pixmap())
def zoomIn(self):
self.scaleImage(1.25)
def zoomOut(self):
self.scaleImage(0.8)
def normalSize(self):
self.imageLabelLeft.adjustSize()
self.imageLabelRight.adjustSize()
self.scaleFactor = 1.0
def about(self):
QMessageBox.about(self, "Image View in the Main Window",
"<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 updateActions(self):
self.window.zoomInAct.setEnabled(not self.window.fitToWindowAct.isChecked())
self.window.zoomOutAct.setEnabled(not self.window.fitToWindowAct.isChecked())
self.window.normalSizeAct.setEnabled(not self.window.fitToWindowAct.isChecked())
def scaleImage(self, factor):
self.scaleFactor *= factor
self.imageLabelLeft.resize(self.scaleFactor * self.imageLabelLeft.pixmap().size())
self.imageLabelRight.resize(self.scaleFactor * self.imageLabelRight.pixmap().size())
self.adjustScrollBar(self.scrollAreaLeft.horizontalScrollBar(), factor)
self.adjustScrollBar(self.scrollAreaLeft.verticalScrollBar(), factor)
self.adjustScrollBar(self.scrollAreaRight.horizontalScrollBar(), factor)
self.adjustScrollBar(self.scrollAreaRight.verticalScrollBar(), factor)
self.window.zoomInAct.setEnabled(self.scaleFactor < 3.0)
self.window.zoomOutAct.setEnabled(self.scaleFactor > 0.333)
def adjustScrollBar(self, scrollBar, factor):
scrollBar.setValue(int(factor * scrollBar.value()
+ ((factor - 1) * scrollBar.pageStep() / 2)))
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.imageViewSync = QImageViewSync(window=self)
self.setCentralWidget(self.imageViewSync.centralWidget)
self.createActions(self.imageViewSync)
self.createMenus()
self.setWindowTitle("Image View Sync in the Main Window")
self.resize(1200, 600)
def fitToWindow(self):
fitToWindow = self.fitToWindowAct.isChecked()
self.imageViewSync.scrollAreaLeft.setWidgetResizable(fitToWindow)
self.imageViewSync.scrollAreaRight.setWidgetResizable(fitToWindow)
if not fitToWindow:
self.imageViewSync.normalSize()
self.imageViewSync.updateActions()
def createActions(self, view):
self.openLeftAct = QAction("&Open Left...", self, shortcut="Ctrl+O", triggered=view.openLeft)
self.openRightAct = QAction("&Open Right...", self, shortcut="Shift+Ctrl+O", triggered=view.openRight)
self.printLeftAct = QAction("&Print Left...", self, shortcut="Ctrl+P", enabled=False, triggered=view.printLeft)
self.printRightAct = QAction("&Print Right...", self,
shortcut="Shift+Ctrl+P", enabled=False, triggered=view.printRight)
# self.exitAct = QAction("E&xit", self, shortcut="Ctrl+Q", triggered=image.close)
self.zoomInAct = QAction("Zoom &In (25%)", self, shortcut="Ctrl++", enabled=False, triggered=view.zoomIn)
self.zoomOutAct = QAction("Zoom &Out (25%)", self, shortcut="Ctrl+-", enabled=False, triggered=view.zoomOut)
self.normalSizeAct = QAction("&Normal Size", self, shortcut="Ctrl+S", enabled=False, triggered=view.normalSize)
self.fitToWindowAct = QAction("&Fit to Window", self,
enabled=False, checkable=True, shortcut="Ctrl+F", triggered=self.fitToWindow)
self.aboutAct = QAction("&About", self, triggered=view.about)
self.aboutQtAct = QAction("About &Qt", self, triggered=qApp.aboutQt)
def createMenus(self):
self.fileMenu = QMenu("&File", self)
self.fileMenu.addAction(self.openLeftAct)
self.fileMenu.addAction(self.openRightAct)
self.fileMenu.addSeparator()
self.fileMenu.addAction(self.printLeftAct)
self.fileMenu.addAction(self.printRightAct)
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)
if __name__ == '__main__':
import sys
from PyQt5.QtWidgets import QApplication
app = QApplication(sys.argv)
win = MainWindow()
win.show()
sys.exit(app.exec_())
@acbetter

Copy link
Copy Markdown
Author

screenshot (os x)

image

@ce-dric

ce-dric commented Oct 7, 2020

Copy link
Copy Markdown

No suitable image viewer for accepting test in Ubuntu. (as my company desired, at least)
Therefore, i make the project named "pruebas-de-validacion" and i will write down your link in this project readme file
Thanks for sharing source.

leave project link, https://github.com/Odin-son/pruebas-de-validacion

@vovavili

vovavili commented Dec 8, 2022

Copy link
Copy Markdown

This is brilliant!

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