Uploaded image for project: 'Qt for Python'
  1. Qt for Python
  2. PYSIDE-2302

QThreadPool locking main thread when creating QImage in PySide2 and PySide6

    XMLWordPrintable

Details

    • Bug
    • Resolution: Fixed
    • P3: Somewhat important
    • 6.5.1, 6.6.0
    • 5.15.10, 6.5.0
    • PySide
    • None
    • Windows
    • 14299e5f5 (dev), 619a44493 (6.5)

    Description

      I have a widget that uses a QThreadPool to load QImage objects, then sends it back to the widget. It works in Python 2, but completely locks up in Python 3 until all images are loaded. I narrowed it down to to creating a QImage inside the QThreadPool. It works in Python 3 when using PyQt6, so I believe the issue is something specific to PySide.

      Here's my tests on different versions:

      • [works] Python 2, PySide 1.2.4
      • [works] Python 2, PySide2 5.12.5
      • [fails] Python 3, PySide2 5.15.0
      • [fails] Python 3, PySide6 6.5.0
      • [works] Python 3, PyQt6

      I have an MRE that demonstrates this (from my 2 year old SO question). If using Python 2 or PyQt6, the scrolling is smooth and threads run in the background while resizing or scrolling. If using PySide in Python 3, the window freezes until all threads are complete.

      PATH_TO_LARGE_FILE = 'C:/large_image.png'  # Pick something >1MB to really show the slowness
      
      import sys
      from Qt import QtCore, QtGui, QtWidgets
      
      class GridView(QtWidgets.QListView):
          def __init__(self):
              super(GridView, self).__init__()
              self.setViewMode(QtWidgets.QListView.IconMode)
              self.setModel(GridModel())
      
      class GridModel(QtGui.QStandardItemModel):
          def __init__(self):
              super(GridModel, self).__init__()
              self.threadPool = QtCore.QThreadPool()
      
          def data(self, index, role=QtCore.Qt.UserRole):
              # Load image
              if role == QtCore.Qt.DecorationRole:
                  item = self.itemFromIndex(index)
                  worker = ImageLoader(item)
                  self.threadPool.start(worker)
                  return None
      
              # Set size of icons
              elif role == QtCore.Qt.SizeHintRole:
                  return QtCore.QSize(64, 89)
              return super(GridModel, self).data(index, role)
      
      class ImageLoader(QtCore.QRunnable):
          def __init__(self, item):
              self.item = item
              super(ImageLoader, self).__init__()
      
          def run(self):
              QtGui.QImage(self.item.path)
      
      if __name__ == '__main__':
          app = QtWidgets.QApplication(sys.argv)
          widget = GridView()
          for i in range(1000):
              item = QtGui.QStandardItem('test {}'.format(i))
              item.path = PATH_TO_LARGE_FILE
              widget.model().appendRow(item)
          widget.show()
          app.setActiveWindow(widget)
          app.exec_() 

      Attachments

        Issue Links

          For Gerrit Dashboard: PYSIDE-2302
          # Subject Branch Project Status CR V

          Activity

            People

              kleint Friedemann Kleint
              xarovin Peter Hunt
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Gerrit Reviews

                  There are no open Gerrit changes