如何获得 QListWidgetItem 在 PyQT5 中变得可见的回调

问题描述 投票:0回答:1

我在 PyQT5 中使用

QListWidget
创建了一个简单的 MediaBrowser:

class MediaBrowser(QListWidget):

    def __init__(self, database: Database, viewtab, dir_path):
        QListWidget.__init__(self)
        self.log = logging.getLogger('mediahug')
        self.database = database
        self.viewtab = viewtab

        self.setLayoutMode(QListView.Batched)
        self.setBatchSize(10000)
        self.setUniformItemSizes(True)

        self.current_directory = dir_path
        # self.current_file_widgets = []
        self.thumb_loader_thread = None
        self.itemSelectionChanged.connect(self.selection_change)

        # Should theoretically speed things up but it does not
        # self.setSizeAdjustPolicy(QListWidget.AdjustToContents)

        self.setSelectionMode(QAbstractItemView.SingleSelection)
        self.setViewMode(QListWidget.IconMode)
        self.setResizeMode(QListWidget.Adjust)
        self.setIconSize(QSize(THUMB_WIDTH, THUMB_HEIGHT))
        self.load_files(dir_path)

...
...

    def all_files(self):
        return self.findItems('*', Qt.MatchWildcard)

    def load_files(self, dir_path):
        if self.thumb_loader_thread and self.thumb_loader_thread.isRunning():
            self.log.info('Killing Previous Thumbnail Loading Thread')
            self.thumb_loader_thread.requestInterruption()
            self.thumb_loader_thread.wait(sys.maxsize)
            self.log.info('Previous Thumbnail Thread Done')

        self.clear()
        # Load New File widgets
        onlyfiles = [f for f in listdir(dir_path) if isfile(join(dir_path, f))]
        for f in onlyfiles:
            vid = join(dir_path, f)
            self.log.debug(f"Creating File/Thumb Widget {vid}")
            self.addItem(ThumbWidget(vid, self.database))

        self.thumb_loader_thread = ThumbLoaderThread(self.all_files(), dir_path)
        self.thumb_loader_thread.start()

MediaBrowser
是一个
QListWidget
,在启动时添加了一堆
ThumbWidget
项目(即
QListWidgetItem
对象):

class ThumbWidget(QListWidgetItem):

    def __init__(self, filename: str, database):
        QListWidgetItem.__init__(self)
        self.filename = filename
        self.database = database
        self.setText(basename(self.filename))
        standard_file_icon = QWidget().style().standardIcon(QStyle.SP_FileIcon)
        self.setIcon(standard_file_icon)
        self.setSizeHint(QSize(THUMB_WIDTH, THUMB_HEIGHT + FILENAME_MARGIN))

    def __str__(self):
        return f'Thumbnail for {self.filename}'

    def load_thumb(self):
        metadata = self.database.file_metadata(self.filename)
        img_thumb = metadata['thumb']
        if img_thumb:
            img = QPixmap()
            img.loadFromData(img_thumb, 'JPEG')
            self.setIcon(QIcon(img))

这在启动时需要很多时间。我只想在项目滚动到视图中时加载该项目的缩略图。在我的代码中的其他地方,

MediaBrowser
位于
QScrollArea
内。

        self.media_scroller = QScrollArea()
        self.media_scroller.setWidget(self._media_browser)
        self.media_scroller.setWidgetResizable(True)

有什么方法可以让事件知道特定的

QWidgetItem
当前滚动进入/退出视图吗?这样我就可以加载和卸载缩略图,从而提高启动时间。

可以在这里找到该项目的完整代码:

https://gitlab.com/djsumdog/mediahug/-/tree/master/mediahug/gui/viewtab

python pyqt5 qt5
1个回答
0
投票

感谢您对这个问题的所有评论。有趣的是,链接的文档是 QT 网站的 Python 部分,但其代码是 C++。我用它创建了一个在 PyQT5 中处理文件的模型。我在其他地方实现了诸如

database
和缩略图之类的东西,但对于任何正在寻找处理文件的
QAbstractListModel
的 Python 实现的人来说,它应该很容易适应:

import logging
from functools import lru_cache
from os import listdir
from os.path import isfile, join, basename

from PyQt5 import QtCore
from PyQt5.QtCore import QAbstractListModel, QModelIndex, QVariant, Qt, QSize, pyqtSignal
from PyQt5.QtGui import QIcon, QPixmap
from PyQt5.QtWidgets import QStyle, QWidget



class FileListModel(QAbstractListModel):

    numberPopulated = pyqtSignal(int)

    def __init__(self, database: Database, dir_path: str):
        super().__init__()
        self.log = logging.getLogger('<your app name>')
        self.database = database
        self.files = []
        self.loaded_file_count = 0
        self.set_dir_path(dir_path)

    def rowCount(self, parent: QModelIndex = QtCore.QModelIndex()) -> int:
        return 0 if parent.isValid() else self.loaded_file_count

    def set_dir_path(self, dir_path: str):
        self.beginResetModel()
        self.files = []
        self.loaded_file_count = 0
        only_files = [f for f in listdir(dir_path) if isfile(join(dir_path, f))]
        for f in only_files:
            vid = join(dir_path, f)
            self.files.append(vid)
        self.endResetModel()

    def data(self, index: QModelIndex, role: int = Qt.DisplayRole):
        if not index.isValid():
            return QVariant()

        if index.row() >= len(self.files) or index.row() < 0:
            return QVariant()

        if role == Qt.DisplayRole:
            filename = basename(self.files[index.row()])
            return QVariant(filename)

        if role == Qt.DecorationRole:
            thumb = self.__load_thumb(self.files[index.row()])
            if thumb:
                return thumb
            else:
                return QWidget().style().standardIcon(QStyle.SP_FileIcon)

        if role == Qt.SizeHintRole:
            return QSize(THUMB_WIDTH, THUMB_HEIGHT + FILENAME_MARGIN)

        return QVariant()

    def fetchMore(self, parent: QModelIndex) -> None:
        if parent.isValid():
            return

        remainder = len(self.files) - self.loaded_file_count
        items_to_fetch = min(100, remainder)
        if items_to_fetch <= 0:
            self.log.debug("No More Items to Fetch")
            return

        self.log.debug(f'Loaded Items: {self.loaded_file_count} / Items to Fetch: {items_to_fetch}')

        self.beginInsertRows(QModelIndex(), self.loaded_file_count, self.loaded_file_count + items_to_fetch - 1)
        self.loaded_file_count += items_to_fetch
        self.endInsertRows()
        self.numberPopulated.emit(items_to_fetch)

    def canFetchMore(self, parent: QModelIndex) -> bool:
        if parent.isValid():
            return False
        can_fetch = self.loaded_file_count < len(self.files)
        self.log.debug(f'Can Fetch More? {can_fetch}')
        return can_fetch

    @lru_cache(maxsize=5000)
    def __load_thumb(self, filename):
        self.log.debug(f'Loading Thumbnail For {filename}')
        metadata = self.database.file_metadata(filename)
        img_thumb = metadata['thumb']
        if img_thumb:
            img = QPixmap()
            img.loadFromData(img_thumb, 'JPEG')
            return QIcon(img)

在上面的示例中,我从数据库层加载缩略图,并在其周围添加了

lru_cache
注释。这目前有效,但如果尚未生成/缓存缩略图,则滚动大文件夹时会出现延迟。

未来的改进和加速将涉及更改

if role == Qt.DecorationRole
data()
部分,以便在缩略图无法立即在内存中使用时始终返回加载图标。然后有一个后台任务、线程或委托,然后在后台加载或生成缩略图,并在模型上使用
dataChanged()
调用来指示缩略图现已准备就绪。

我还将

QListWidget
更改为
QListView
并在构造函数中更改以下内容:

        self.setLayoutMode(QListView.Batched)
        self.setBatchSize(10)
        self.setUniformItemSizes(True)

        self.file_list_model = FileListModel(database, dir_path)
        self.setModel(self.file_list_model)
        self.selectionModel().selectionChanged.connect(self.selection_change)
© www.soinside.com 2019 - 2024. All rights reserved.