Thumbnails

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Hallo Leute,

ich versuche mich gerade daran zu machen, mir eine Art "Thumbnails" zu basteln. Dazu habe ich ein lauffähiges Programm. Wie ihr gleich im Quelltext feststellen werdet, versuche ich das einmal mit QTreeView und einmal mit QListView. Je nach dem, von welcher Qt-Klasse ihr ableitet, müsst ihr im Quelltext das entsprechende auskommentieren. Ich habe selbst für mich die Stellen markiert, welche für QListView und welche für QTreeView sind. Weiterhin werdet ihr mit einem Blick in die MyListModel()-Klasse, welche von der QAbstractListModel()-Klasse abgeleitet wird, sehen, dass ich in der data()-Funktion versuche, die Größe des Bildes festzulegen. Übertriebener weise habe ich hier mal 2500x2000 genommen. Aber die Bilder in diesem Programm bleiben immer gleich groß - es ändert sich nichts.

Was möchte ich? Ich möchte in der Lage sein, die Bilder entsprechend nach meiner Vorstellung anzupassen. Zum Beispiel in Thumbnail-Größe. Von mir aus 128x128 oder 250x250. Aber die Bilder werden hier viel zu klein dargestellt. Ein weiteres Problem habe ich, dass die Anordnung der Bilder irgendwie nicht richtig positioniert sind. Ich möchte, dass alle Bilder gleichmäßig angeordnet sind, und nicht irgendwie verschoben aussehen. Und ich möchte, dass die Bilder auch nebeneinander, soweit es möglich ist, angeordnet sind, und nicht nur untereinander. Das ist auch der Grund, weshalb ich mich auch an QTreeView probiere, da ich vermute, dass QListView hier nicht ausreichend ist.

Um mein Programm nutzen zu können, solltet ihr einen Ordner anlegen, und Bilder mit .*jpg- und/oder *.png-Endungen füllen. Und in Zeile 33 müsst ihr euren Pfad zu dem Ordner anpassen.

Code: Alles auswählen

import sys
import os
from PyQt4 import QtGui, QtCore
from PyQt4.QtGui import QLabel
from PyQt4.QtCore import Qt

class MyListModel(QtCore.QAbstractListModel): 
    def __init__(self, datain, parent=None, *args): 
        """ datain: a list where each item is a row
        """
        QtCore.QAbstractListModel.__init__(self, parent, *args) 
        self.listdata = datain
    def rowCount(self, parent=QtCore.QModelIndex()): 
        return len(self.listdata) 
    def data(self, index, role):
        if index.isValid() and role == QtCore.Qt.DecorationRole:
            img_size = QtCore.QSize(2500, 2000)
            return QtGui.QIcon(QtGui.QPixmap(self.listdata[index.row()]).scaled(img_size, Qt.KeepAspectRatio))
        if index.isValid() and role == QtCore.Qt.DisplayRole:
            return QtCore.QVariant(os.path.splitext(os.path.split(self.listdata[index.row()])[-1])[0])
        else: 
            return QtCore.QVariant()
        
#class MyListView(QtGui.QTreeView):
class MyListView(QtGui.QListView):
    def __init__(self):
        super(MyListView, self).__init__()

        # show in Icon Mode - only for QListView
        self.setViewMode(QtGui.QListView.IconMode)

        # path to the folder that contains image-files
        crntDir = os.path.abspath(os.path.join('test_pic_nails'))
        
        # create empty list
        list_data = []

        philes = os.listdir(crntDir)
        
        for phile in philes:
            if phile.endswith(".png") or phile.endswith("jpg"):
                list_data.append(os.path.join(crntDir, phile))
                
        lm = MyListModel(list_data, self)
        self.setModel(lm)

        # All settings of QTreeView-class
##        self.setHeaderHidden(True)
##        self.setSelectionMode(QtGui.QTreeView.SingleSelection)
##        self.setFocusPolicy(QtCore.Qt.NoFocus)
##        self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
##        self.setFrameShape(QtGui.QFrame.StyledPanel)
##        self.setFrameShadow(QtGui.QFrame.Plain)
##        self.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
##        self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
##        self.setAlternatingRowColors(True)
##        self.setRootIsDecorated(False)
##        self.setUniformRowHeights(False)
##        self.setItemsExpandable(False)
##        self.setSortingEnabled(True)
##        self.setAnimated(True)
##        self.setAllColumnsShowFocus(True)
##        self.setExpandsOnDoubleClick(False)
##        self.setObjectName("tree_view")
        
        self.show()
        
if __name__ == '__main__':
    app = QtGui.QApplication(sys.argv)
    window =  MyListView()
    #window.show()
    window.raise_()
    sys.exit(app.exec_())
So sieht das Programm bei mir aus:
Bild

Und so hätte ich es gerne - sozusagen meine Wunschvorstellung:
Bild
Benutzeravatar
Madmartigan
User
Beiträge: 200
Registriert: Donnerstag 18. Juli 2013, 07:59
Wohnort: Berlin

Für eine regelmäßige Anordnung reicht es schon, wenn du setUniformItemSizes(bool enable) aufrufst.

Ich denke aber, dass der Ansatz hier von QAbstractListModel zu erben, nicht der beste Weg ist. Das Model ist nicht für QTreeView verwendbar, da es keine Hierarchie kennt, sondern wirklich nur Listen (also ein-dimensional ist).
Schau am besten mal in die Klassenreferenz und lies dir die Hinweise durch. Zudem gibt es wesentlich einfachere Wege, einen Thumbnail-View zu erstellen.

Mein Ansatz wäre folgender:
Im View würde ich QListView bzw. QTreeView verwenden, das machst du ja bereits.
Für die Daten würde ich dann ein eigenes ItemModel erstellen (erbt von QStandardItemModel)
Zum "Customizen" der Optik benutzt du dann ein QItemDelegate

Mit diesem "Bauplan" habe ich damals die erste Version unseres Asset-Browsers entwickelt (mittlerweile allerdings C#/WPF). Der konnte damals schon Texturen (max. Auflösung 4096x4096) und eine Vorschau für Animationen (max. 256x256 und max. 300 Frames) im Thumbnail darstellen, dank lazy loading ruckelfrei und ressourcen-schonend. Soll nur als Beispiel dienen, dass der Ansatz funktioniert und auch mit großen Datenmengen problemlos verwendbar ist.

Ich hoffe, das hilft dir weiter.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Madmartigan: Vielen Dank für deine Antwort. Ich werde mal schauen, was ich daraus machen kann. Hast du dazu ein kleines Beispiel-Quelltext?

Beim Stöbern fand ich hier diesen nachstehenden Quelltext. Der Autor des Quelltextes bedient sich hier an QListWidget.setItemWidget und QListWidget. Alles ohne Model. Ist diese Lösung weniger Elegant?

Code: Alles auswählen

import sys
from PyQt4 import QtGui

class QCustomQWidget (QtGui.QWidget):
    def __init__ (self, parent = None):
        super(QCustomQWidget, self).__init__(parent)
        self.textQVBoxLayout = QtGui.QVBoxLayout()
        self.textUpQLabel    = QtGui.QLabel()
        self.textDownQLabel  = QtGui.QLabel()
        self.textQVBoxLayout.addWidget(self.textUpQLabel)
        self.textQVBoxLayout.addWidget(self.textDownQLabel)
        self.allQHBoxLayout  = QtGui.QHBoxLayout()
        self.iconQLabel      = QtGui.QLabel()
        self.allQHBoxLayout.addWidget(self.iconQLabel, 0)
        self.allQHBoxLayout.addLayout(self.textQVBoxLayout, 1)
        self.setLayout(self.allQHBoxLayout)
        # setStyleSheet
        self.textUpQLabel.setStyleSheet('''
            color: rgb(0, 0, 255);
        ''')
        self.textDownQLabel.setStyleSheet('''
            color: rgb(255, 0, 0);
        ''')

    def setTextUp (self, text):
        self.textUpQLabel.setText(text)

    def setTextDown (self, text):
        self.textDownQLabel.setText(text)

    def setIcon (self, imagePath):
        self.iconQLabel.setPixmap(QtGui.QPixmap(imagePath))

class exampleQMainWindow (QtGui.QMainWindow):
    def __init__ (self):
        super(exampleQMainWindow, self).__init__()
        # Create QListWidget
        self.myQListWidget = QtGui.QListWidget(self)
        for index, name, icon in [
            ('No.1', 'Meyoko',  'icon.png'),
            ('No.2', 'Nyaruko', 'icon.png'),
            ('No.3', 'Louise',  'icon.png')]:
            # Create QCustomQWidget
            myQCustomQWidget = QCustomQWidget()
            myQCustomQWidget.setTextUp(index)
            myQCustomQWidget.setTextDown(name)
            myQCustomQWidget.setIcon(icon)
            # Create QListWidgetItem
            myQListWidgetItem = QtGui.QListWidgetItem(self.myQListWidget)
            # Set size hint
            myQListWidgetItem.setSizeHint(myQCustomQWidget.sizeHint())
            # Add QListWidgetItem into QListWidget
            self.myQListWidget.addItem(myQListWidgetItem)
            self.myQListWidget.setItemWidget(myQListWidgetItem, myQCustomQWidget)
        self.setCentralWidget(self.myQListWidget)

app = QtGui.QApplication([])
window = exampleQMainWindow()
window.show()
sys.exit(app.exec_())
Antworten