PyQT playground: image gallery with zoom

Posted by Marco Dinacci on 0 comments

In this article I'm going to create a small PyQT based application that displays images in a grid and that zoom the image hovered by the mouse.

This is how the final application looks like: PyQT Image Gallery Screenshot

The first class we need is an ImageGallery, which is a mere QDialog using a QGridLayout.

class ImageGallery(QDialog):

    def __init__(self, parent=None):
        super(QDialog, self).__init__(parent)
        self.setWindowTitle("Image Gallery")
        self.setLayout(QGridLayout(self))

    def populate(self, pics, size, imagesPerRow=4,
                 flags=Qt.KeepAspectRatioByExpanding):
        row = col = 0
        for pic in pics:
            label = ImageLabel("")
            pixmap = QPixmap(pic)
            pixmap = pixmap.scaled(size, flags)
            label.setPixmap(pixmap)
            self.layout().addWidget(label, row, col)
            col +=1
            if col % imagesPerRow == 0:
                row += 1
                col = 0

The code is very simple, the class has only one public method, populate, which iterates over the images passed in the argument pics, create a QPixmap for each of them, and add them to the layout.

The interesting bit is the line where I create the label:

label = ImageLabel("")

This line creates a new instance of the ImageLabel, which is listed here below:

class ImageLabel(QLabel):
    """ This widget displays an ImagePopup when the mouse enters its region """
    def enterEvent(self, event):
        self.p = ImagePopup(self)
        self.p.show()
        event.accept()

The ImageLabel is a normal QLabel except that I redefined the enterEvent in order to show an ImagePopup when the mouse enters the label.

Finally, the ImagePopup class, responsible for the creation of the zoomed popup image.

class ImagePopup(QLabel):
    """
    The ImagePopup class is a QLabel that displays a popup, zoomed image
    on top of another label.
    """
    def __init__(self, parent):
        super(QLabel, self).__init__(parent)

        # set pixmap and size, which is the double of the original pixmap
        thumb = parent.pixmap()
        imageSize = thumb.size()
        imageSize.setWidth(imageSize.width()*2)
        imageSize.setHeight(imageSize.height()*2)
        self.setPixmap(thumb.scaled(imageSize,Qt.KeepAspectRatioByExpanding))

        # center the zoomed image on the thumb
        position = self.cursor().pos()
        position.setX(position.x() - thumb.size().width())
        position.setY(position.y() - thumb.size().height())
        self.move(position)

        # FramelessWindowHint may not work on some window managers on Linux
        # so I force also the flag X11BypassWindowManagerHint
        self.setWindowFlags(Qt.Popup | Qt.WindowStaysOnTopHint
                            | Qt.FramelessWindowHint
                            | Qt.X11BypassWindowManagerHint)

    def leaveEvent(self, event):
        """ When the mouse leave this widget, destroy it. """
        self.destroy()

That's it, it's very simple as you can see. You can download the python code or a zip file containing the code and the images.

Thanks for reading.