import io import sys from pathlib import Path from PIL import Image from PyQt6.QtCore import Qt from PyQt6.QtGui import QImage, QPixmap from PyQt6.QtWidgets import ( QApplication, QGraphicsPixmapItem, QGraphicsScene, QGraphicsView, QMainWindow, QPushButton, QVBoxLayout, QWidget, ) # For PyQt5 users, change the imports above to this: # from PyQt5.QtWidgets import QApplication, QMainWindow, QGraphicsView, QGraphicsScene, QGraphicsPixmapItem, QVBoxLayout, QWidget, QPushButton, QFileDialog # from PyQt5.QtGui import QPixmap # from PyQt5.QtCore import Qt class ZoomableView(QGraphicsView): """ A custom QGraphicsView that provides zoom functionality with the mouse wheel. """ def __init__(self, scene): super().__init__(scene) # Set anchor points for zooming and resizing self.setTransformationAnchor(QGraphicsView.ViewportAnchor.AnchorUnderMouse) self.setResizeAnchor(QGraphicsView.ViewportAnchor.AnchorViewCenter) def wheelEvent(self, event): """ Handles mouse wheel events to zoom in or out. """ # Get the amount of scroll angle = event.angleDelta().y() if angle > 0: # Zoom in factor = 1.25 else: # Zoom out factor = 0.8 self.scale(factor, factor) class ImageViewer(QMainWindow): """ Main application window for viewing an image. Includes loading, zooming, and panning functionality. """ def __init__(self): super().__init__() self.setWindowTitle("PyQt Image Viewer (Load, Zoom, Pan)") self.setGeometry(100, 100, 800, 700) # Main widget and layout central_widget = QWidget() self.setCentralWidget(central_widget) layout = QVBoxLayout(central_widget) # 1. Create the QGraphicsScene # The scene is the container for all 2D graphical items self.scene = QGraphicsScene() # 2. Create the custom QGraphicsView # This is our custom view with zoom capabilities self.view = ZoomableView(self.scene) # 3. Enable panning (drag the scene with the mouse) # The hand cursor will appear when you click and drag. self.view.setDragMode(QGraphicsView.DragMode.ScrollHandDrag) # Add a button to load an image self.load_button = QPushButton("Load Image") self.load_button.clicked.connect(self.load_image) # Add widgets to the layout layout.addWidget(self.load_button) layout.addWidget(self.view) # A variable to hold the currently displayed image item self.pixmap_item = None def load_image(self): file_path = Path( "/home/lambda/Downloads/f546ed6d35ee05338b8403d57dda10103ac3b1b8.jpg@672w_378h_1c_!web-home-common-cover.avif" ) image_data = file_path.read_bytes() im = Image.open(io.BytesIO(image_data)) pixmap = QPixmap.fromImage( QImage(im.tobytes(), im.size[0], im.size[1], QImage.Format.Format_RGB888) ) if pixmap.isNull(): print(f"Error: Failed to load image from {file_path}") return # If an image is already loaded, remove the old one first if self.pixmap_item: self.scene.removeItem(self.pixmap_item) # 5. Create a QGraphicsPixmapItem to hold the image self.pixmap_item = QGraphicsPixmapItem(pixmap) # 6. Add the item to the scene self.scene.addItem(self.pixmap_item) # --- Optional: Improve the viewing experience --- # Reset any previous transformations (like zoom/pan) self.view.resetTransform() # Fit the entire image within the view, maintaining aspect ratio self.view.fitInView(self.pixmap_item, Qt.AspectRatioMode.KeepAspectRatio) if __name__ == "__main__": # Create the application instance app = QApplication(sys.argv) # Create and show the main window viewer = ImageViewer() viewer.show() # Start the application's event loop sys.exit(app.exec())