File Image Browser App with thumbnails

Hey there,

I’m trying to develop some image viewer in the same vein as KDE’s Gwenview, but with some very specific features for my actual application. I’ll be using python 3.8 and PyQt5.

The question is: is there already any widget for browsing files in icon-mode, showing thumbnails of images, or would I have to implement that whole jazz from the scratch?

Hi @Rafael_Lago welcome to the forum, sorry for the delay in replying

Unfortunately, there isn’t a ready-to-go widget for this, but it’s fairly straightforward to implement. See a quick example below, which is used the model view framework to render thumbnails for image files in a folder. Here we’re using a table view to layout the items, and a custom delegate to draw the thumbnails (you can add anything else you like).

import glob
import math
import sys
from collections import namedtuple

from PyQt5.QtCore import QAbstractTableModel, Qt, QSize
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtWidgets import QApplication, QMainWindow, QTableView, QStyledItemDelegate

# Create a custom namedtuple class to hold our data.
preview = namedtuple("preview", "id title image")

NUMBER_OF_COLUMNS = 4
CELL_PADDING = 20 # all sides

class PreviewDelegate(QStyledItemDelegate):
    
    def paint(self, painter, option, index):
        # data is our preview object
        data = index.model().data(index, Qt.DisplayRole)
        if data is None:
            return
            
        width = option.rect.width() - CELL_PADDING * 2
        height = option.rect.height() - CELL_PADDING * 2

        # option.rect holds the area we are painting on the widget (our table cell)
        # scale our pixmap to fit
        scaled = data.image.scaled(
            width,
            height,
            aspectRatioMode=Qt.KeepAspectRatio,
        )
        # Position in the middle of the area.
        x = CELL_PADDING + (width - scaled.width()) / 2
        y = CELL_PADDING + (height - scaled.height()) / 2
        
        painter.drawImage(option.rect.x() + x, option.rect.y() + y, scaled)
        
    def sizeHint(self, option, index):
        # All items the same size.
        return QSize(300, 200)


class PreviewModel(QAbstractTableModel):
    def __init__(self, todos=None):
        super().__init__()
        # .data holds our data for display, as a list of Preview objects.
        self.previews = []

    def data(self, index, role):
        try:
            data = self.previews[index.row() * 4 + index.column() ]
        except IndexError:
            # Incomplete last row.
            return
            
        if role == Qt.DisplayRole:
            return data   # Pass the data to our delegate to draw.
           
        if role == Qt.ToolTipRole:
            return data.title

    def columnCount(self, index):
        return NUMBER_OF_COLUMNS

    def rowCount(self, index):
        n_items = len(self.previews)
        return math.ceil(n_items / NUMBER_OF_COLUMNS)


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.view = QTableView()
        self.view.horizontalHeader().hide()
        self.view.verticalHeader().hide()
        self.view.setGridStyle(Qt.NoPen)
        
        delegate = PreviewDelegate()
        self.view.setItemDelegate(delegate)
        self.model = PreviewModel()
        self.view.setModel(self.model)
        
        self.setCentralWidget(self.view)

        # Add a bunch of images.
        for n, fn in enumerate(glob.glob("*.jpg")):
            image = QImage(fn)
            item = preview(n, fn, image)
            self.model.previews.append(item)
        self.model.layoutChanged.emit()
        
        self.view.resizeRowsToContents()
        self.view.resizeColumnsToContents()


app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()

If you drop this file in any folder containing jpeg files (extension “*.jpg”) they’ll be shown in a list.

Thank you very much! I was actually trying to develop it using QTableView, but I am not very familiar with this part of Qt5 (yet). Your code is very helpful!

So, I extended the concept, but using QFilesystemModel and QListView in IconMode with a given gridSize:

modUI.py: https://pastebin.com/q5jzz7S8
main.py: https://pastebin.com/Mes6cVZt

One of the remaining problems is that the position of the icons is not updated when I resize the window, which gives some funny results (see attached image).

I assume I have to connect some signal to some slot (i.e. resize of window to QListView.update?) but I’m not very familiar with Qt at this point to know which signal to what. Can anyone shed some light?

EDIT#1: nevermind, I just found about setResizeMode(QtWidgets.QListView.Adjust)

1 Like