Rafael_Lago | 2020-11-10 20:14:45 UTC | #1
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?
martin | 2020-11-24 10:28:12 UTC | #2
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.
Rafael_Lago | 2020-11-24 10:28:12 UTC | #3
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!
Rafael_Lago | 2020-11-24 10:28:07 UTC | #4
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)