2017-03-08 43 views
0

我试图找出如何在PyQt Qlistview中正确完成异步图像加载如何正确加载PyQt5中的图像?

我的主要部件由QlistviewQLineEdit文本框组成。 我有一个演员的数据库,我使用QAbstractListModel的子类进行查询。当在文本框中输入文本时,将查询数据库并使用结果填充模型。结果显示在Qlistview中。 (A对于每个演员结果包含演员名称和图像的路径。)

像这样: enter image description here

当结果集太大(大于50),加载图像的问题开始从磁盘采取收费和悬挂用户界面。我希望实现的行为是最初为所有结果加载一个占位符图像,然后在另一个线程中加载磁盘中的特定图像,并在加载时使用新加载的图像更新Qlistview项目。

为此,我创建了一个自定义QItemDelegate类,它具有需要加载的所有图像的缓存。如果图像不在缓存中,则它会绘制占位符图像并向另一个线程发送信号,该线程将该图像加载并放入缓存中。

我的委托类:

class MyDelegate(QStyledItemDelegate): 
    t1 = pyqtSignal(str, str, dict) 

    def __init__(self, image_cache, loader_thread, parent=None): 
     super(MyDelegate, self).__init__(parent) 
     self.placeholder_image = QPixmap(PLACEHOLDER_IMAGE_PATH).scaled(200, 300) 
     self.image_cache = image_cache 
     self.loader_thread = loader_thread 
     self.t1.connect(self.loader_thread.insert_into_queue) 


    def paint(self, QPainter, QStyleOptionViewItem, QModelIndex): 
     rect = QStyleOptionViewItem.rect 
     actor_name = QModelIndex.data(Qt.DisplayRole) 
     actor_thumb = QModelIndex.data(Qt.UserRole) 
     pic_rect = QRect(rect.left(), rect.top(), 200, 300) 
     text_rect = QRect(rect.left(), rect.top() + 300, 200, 20) 
     try: 
      cached_thumb = self.image_cache[actor_name] 
      print("Got image: {} from cache".format(actor_name) 
     except KeyError as e: 
      self.t1.emit(actor_name, actor_thumb, self.image_cache) 
      cached_thumb = self.placeholder_image 
      print("Drawing placeholder image for {}".format(actor_name) 

     QPainter.drawPixmap(pic_rect, cached_thumb) 
     QPainter.drawText(text_rect, Qt.AlignCenter, actor_name) 

     if QStyleOptionViewItem.state & QStyle.State_Selected: 
      highlight_color = QStyleOptionViewItem.palette.highlight().color() 
      highlight_color.setAlpha(50) 
      highlight_brush = QBrush(highlight_color) 
      QPainter.fillRect(rect, highlight_brush) 

    def sizeHint(self, QStyleOptionViewItem, QModelIndex): 
     return QSize(200, 320) 

LoaderThread:

class LoaderThread(QObject): 

    def __init__(self): 
     super(LoaderThread, self).__init__() 

    @pyqtSlot(str, str, dict) 
    def insert_into_queue(self, name, thumb_path, image_cache): 
     print("Got signal, loading image for {} from disk".format(name)) 
     pixmap = QPixmap(thumb_path).scaled(200, 300) 
     image_cache[name] = pixmap 
     print("Image for {} inserted to cache".format(name)) 

主窗口__init__方法的主要组成部分:

image_cache = {} 
lt = loader_tread.LoaderThread() 
self.thread = QThread() 
lt.moveToThread(self.thread) 
self.thread.start() 
self.delegate = MyDelegate(image_cache, lt) 

当THI s的方法似乎工作,因为图像正确加载,当MyDelegate中的多个调用self.t1.emit(actor_name, actor_thumb, self.image_cache)进行时,用户界面挂起。

事实上,延迟是几乎相同的图像时,加载在同一个线程像这样到:

try: 
      cached_thumb = self.image_cache[actor_name] 
      print("Got image: {} from cache".format(QModelIndex.data(Qt.DisplayRole))) 
except KeyError as e: 
      # self.t1.emit(actor_name, actor_thumb, self.image_cache) 
      pixmap = QPixmap(actor_thumb).scaled(200,300) 
      self.image_cache[actor_name] = pixmap 
      cached_thumb = self.image_cache[actor_name] 

如果有人有什么我做错了,或约期望的行为怎能任何指针达到他们将受到好评。

P.S 我知道我可以限制数据库查询中的结果集,但这不是我想要做的。

+0

你做了任何分析?渲染图像需要的时间肯定比加载图像要长得多。 – ekhumoro

+0

@ekhumoro与从磁盘加载图像的时间相比,位于内存中的图像的渲染时间可以忽略不计。当所有图像都在缓存中并且滚动变得平滑时,这是显而易见的。当仅渲染占位符图像(取消从磁盘完全加载图像)并且滚动平滑时,这也是明显的。 – Curtwagner1984

回答

1

我今天刚刚遇到同样的问题:尝试在单独的Q线程上加载缩略图QPixmaps,并挂起UI。

我发现,出于某种原因...使用的QPixmap从磁盘加载将始终“冻结”主界面线程图像:

pixmap = QPixmap(thumb_path).scaled(200, 300) # Will hang UI 

一种解决方案;加载使用QImage的对象的图像,然后从生成的图像的QPixmap,下面的代码做的工作对我来说:

image = QImage(thumb_path) 
pixmap = QPixmap.fromImage(image).scaled(200, 300) 

滚动是光滑的,具有500个+缩略图测试。

相关问题