How to resize images so they always fit to the scene ?
-
Hello, so I am very new to coding but wanna learn as a hobby. What I'm trying to do as a first project is an image viewer in python using pyside6.
After hours of banging my head against walls for the last few days I managed to figure out some stuff but I can't figure out how to correctly do one of the most important thing for an image viewer, make it so the images always fit the entirety of the window.
Here's my code so far :
import sys import os from PySide6.QtCore import Qt, QSize from PySide6.QtGui import QPixmap, QAction, QColor, QShortcut, QKeySequence, QImage from PySide6.QtWidgets import QApplication, QGraphicsScene, QGraphicsView, QMainWindow, QVBoxLayout, QWidget, QFileDialog, QGraphicsTextItem, QGraphicsPixmapItem class ImageViewer(QMainWindow): def __init__(self): super().__init__() self.initUI() def initUI(self): self.setWindowTitle("Image Viewer") self.setGeometry(100, 100, 800, 600) background_color = QColor(3, 5, 15) # Create a central widget and set it as the main window's central widget central_widget = QWidget(self) self.setCentralWidget(central_widget) # Create a QGraphicsView and set it as the central widget layout = QVBoxLayout(central_widget) layout.setContentsMargins(0, 0, 0, 0) #layout.setSpacing(0) self.view = QGraphicsView(self) self.view.setBackgroundBrush(background_color) layout.addWidget(self.view) # Create a QGraphicsScene self.scene = QGraphicsScene(self) self.view.setScene(self.scene) # Create a QAction to open an image open_action = QAction("Open Image", self) open_action.triggered.connect(self.openImage) self.menuBar().addAction(open_action) self.grouped_images = {} # Dictionary to store images in groups self.group_names = [] # List to store group names self.current_group_index = 0 # Index of the currently selected group self.current_image_index = 0 # Index of the currently displayed image within the group text_item = QGraphicsTextItem("Alice") font = text_item.font() font.setPointSize(150) # Adjust the font size as needed text_item.setFont(font) text_item.setDefaultTextColor(Qt.red) text_item.setPos(100, 100) self.scene.addItem(text_item) self.setShortcuts() def setShortcuts(self): QShortcut(QKeySequence(Qt.Key_F11), self, self.toggleFullScreen) QShortcut(QKeySequence(Qt.Key_Right), self, self.nextImage) QShortcut(QKeySequence(Qt.Key_Left), self, self.previousImage) QShortcut(QKeySequence(Qt.Key_Up), self, self.switchGroupUp) QShortcut(QKeySequence(Qt.Key_Down), self, self.switchGroupDown) def openImage(self): folder_dialog = QFileDialog.getExistingDirectory(self, "Open Directory") if folder_dialog: self.grouped_images, self.group_names = self.collectGroupedImages(folder_dialog) self.current_group_index = 0 # Reset the current group index self.current_image_index = 0 # Reset the current image index within the group if self.group_names: self.displayImage() def collectGroupedImages(self, directory): grouped_images = {} group_names = [] for root, _, files in os.walk(directory): # Determine the group name (folder name) group_name = os.path.basename(root) if group_name not in grouped_images: grouped_images[group_name] = [] group_names.append(group_name) for filename in files: if filename.lower().endswith(('.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.tif')): image_path = os.path.join(root, filename) grouped_images[group_name].append(image_path) return grouped_images, group_names def switchGroupUp(self): if self.group_names: self.current_group_index = (self.current_group_index - 1) % len(self.group_names) self.current_image_index = 0 # Reset the current image index within the new group self.displayImage() def switchGroupDown(self): if self.group_names: self.current_group_index = (self.current_group_index + 1) % len(self.group_names) self.current_image_index = 0 # Reset the current image index within the new group self.displayImage() def nextImage(self): if self.group_names: current_group_name = self.group_names[self.current_group_index] current_group = self.grouped_images.get(current_group_name, []) if current_group: self.current_image_index = (self.current_image_index + 1) % len(current_group) self.displayImage() def previousImage(self): if self.group_names: current_group_name = self.group_names[self.current_group_index] current_group = self.grouped_images.get(current_group_name, []) if current_group: self.current_image_index = (self.current_image_index - 1) % len(current_group) self.displayImage() def displayImage(self): #self.timer.start(self.new_delay * 1000) if self.group_names: current_group_name = self.group_names[self.current_group_index] current_group = self.grouped_images.get(current_group_name, []) if current_group: image_path = current_group[self.current_image_index] image = QImage(image_path) pixmap = QPixmap(image) pixmap_item = QGraphicsPixmapItem(pixmap) self.scene.clear() self.scene.addItem(pixmap_item) self.scene.setSceneRect(0, 0, pixmap.width(), pixmap.height()) self.view.setScene(self.scene) self.view.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatio) def resizeEvent(self, event): self.view.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatio) def toggleFullScreen(self): if self.isFullScreen(): self.showNormal() self.menuBar().show() else: self.showFullScreen() self.menuBar().hide() def main(): app = QApplication(sys.argv) viewer = ImageViewer() viewer.show() sys.exit(app.exec()) if __name__ == '__main__': main()
I want to apologize already, this code is an amalgamation of what I could get from Chat GPT, old forum posts and my limited understanding of the Qt documentation. So it's defintely terrible in a lot of places, but at least it kinda works.
For this specific problem the important part is the DisplayImage function.
Right now the Scene gets scaled to the size of the current pixmap being displayed, then the View is fitted to the Scene. It gives the intended effect of having the image always filling the entire window while keeping it's AspectRatio, but, since it's the Scene itself being scaled, if I try to add something else to that scene, for example a text, it will also appear scaled and streched.
So I would need the image itself to fit the Scene's size, since the Scene's size is by default the size of the largest item inside of it, smaller images won't fill the entire window. I tried to scale the QPixmap, the QImage or the QGraphicsPixmapItem but couldn't make it work. The images are displayed pixelated and uncentered.
So that's it, I tried to figure it out but I'm giving up... Hope somebody can give me a hand. Thank you
-
@Mattik
You do not want to scale the scene, since as you say that will affect other graphics items. If you are trying to use aQGraphicsPixmapItem
you want only its image scaled. So see e.g. Resize QGraphicsPixmapItem, where you first scale aQPixmap
and then use that for the item. Also maybe see QPixmap: How to increase the size of the picture in addPixmap?.There may be some pixelation, I don't know, that is in the nature of scaling.