【问题标题】:Drag and drop in QTreeView fails with items that hold a QImage拖放 QTreeView 失败,项目包含 QImage
【发布时间】:2018-04-16 12:14:17
【问题描述】:

我在QTreeView 中有一个项目列表。每个项目都包含一个QImage 对象。如果我尝试拖放项目,程序会冻结。但是当我注释掉objMod._Image = QImage(flags = Qt.AutoColor)这一行时,程序运行良好。

如何使用QImage 对象拖放项目? QImage 包含渲染的图像。渲染过程需要一段时间,所以最好保留 QImage 对象。

import sys
import os

from PySide.QtCore    import *
from PySide.QtGui     import *
from PySide.QtUiTools import *

from PIL import Image, ImageCms, ImageQt

class ObjModel:
    def __init__(self):
        self._Image = None

class DragMoveTest(QMainWindow):
    def __init__(self):
        super(DragMoveTest,self).__init__()
        self.initGUI()
        self.show()

    def initGUI(self):
        self.treeView = QTreeView()
        modelTreeView = QStandardItemModel()
        self.treeView.setModel(modelTreeView)
        for i in range(0, 4):
            objMod = ObjModel()
            objMod._Image = None
            objMod._Image = QImage(flags = Qt.AutoColor)

            item = QStandardItem('Test: %s' % str(i))
            item.setData(objMod, Qt.UserRole + 1)
            modelTreeView.invisibleRootItem().appendRow(item)

        self.treeView.setDragDropMode(QAbstractItemView.InternalMove)
        self.setCentralWidget(self.treeView)

def main(args):
    app = QApplication(sys.argv)
    qt_main_wnd = DragMoveTest()
    ret = app.exec_()
    sys.exit(ret)

if __name__ == "__main__":
    main(sys.argv)

【问题讨论】:

    标签: python-3.x drag-and-drop pyside qtreeview qimage


    【解决方案1】:

    这是由 PySide 中的错误引起的。在进行拖放操作时,被拖动项中的数据必须为serialized。对于大多数数据类型,这将由 Qt 处理,但对于特定于 Python 的类型,则需要特殊处理。这种特殊处理似乎在 PySide 中被打破了。如果您的示例转换为 PyQt,则在尝试拖动项目时会引发 TypeError,但程序不会冻结。

    问题的根源在于您使用自定义 Python 类存储数据。 PyQt 使用pickle 来序列化自定义数据类型,但不可能同时pickle 存储在其__dict__ 中的QImage,因此操作失败。我认为 PySide 必须尝试类似的事情,但由于某种原因,它在失败时不会引发错误。 Qt在拖动的时候会抓住鼠标,所以如果操作异常失败,就不会再释放,程序会出现卡住的现象。

    解决此问题的最简单方法是避免使用自定义类来保存QImage,而是将图像直接存储在项目中:

        image = QImage()
        item = QStandardItem('Test: %s' % i)
        item.setData(image, Qt.UserRole + 1)
    

    要存储更多数据项,您可以为每个数据项使用不同的数据角色,或者使用 dict 来保存所有数据项:

        data = {'image': QImage(), 'title': 'foo', 'timestamp': 1756790}
        item.setData(data, Qt.UserRole + 1)
    

    但是,如果你这样做,你必须总是在dict中使用字符串键,否则你会遇到和以前一样的问题. (使用字符串键意味着dict 可以转换为QMap,Qt 知道如何序列化)。

    (注意:如果您想知道 Qt 类是否可以序列化,请查看文档以查看它是否定义了 datastream operators)。

    【讨论】:

      【解决方案2】:

      我想出了一个不同的解决方案。拥有一个包含 io.BytesIO 的对象更容易。您将 ImageData 存储到 bytesIO 变量中。在您的图像库上,您可以从 bytesIO 变量打开图像。 在演示中,ObjModel 类可以处理来自 Pillow/PIL 的 QImage 和 Image。如果使用 set 方法,图像对象将被转换为 bytesIO。 简而言之,这里是一个工作示例:

      #!/usr/bin/env python3
      # -*- coding: utf-8 -*-
      
      import sys
      import os
      import io
      
      from PySide.QtCore                          import *
      from PySide.QtGui                           import *
      from PySide.QtUiTools                       import *
      
      from PIL                import Image, ImageCms, ImageQt
      
      ########################################################################
      class ObjModel:
          """"""
          #----------------------------------------------------------------------
          def __init__(self):
              """Constructor"""
              self._ImageByteIO = None
      
      
          #----------------------------------------------------------------------
          def getObjByte(self):
              """"""
              return self._ImageByteIO
      
      
          #----------------------------------------------------------------------
          def getQImage(self):
              """"""
              try:
                  self._ImageByteIO.seek(0)
                  qImg = QImage.fromData(self._ImageByteIO.getvalue())
                  return qImg
              except:
                  return None
      
      
          #----------------------------------------------------------------------
          def getPILImage(self):
              """"""
              try:
                  self._ImageByteIO.seek(0)
                  img = Image.open(tBytesIO)
                  return img
              except:
                  return None
      
      
          #----------------------------------------------------------------------
          def setObjByte(self, fileName):
              """"""
              try:
                  tBytesIO = io.BytesIO()
                  f = open (fileName, 'rb')
                  tBytesIO.write(f.read())
                  f.close()
                  self._ImageByteIO = tBytesIO
              except:
                  self._ImageByteIO = None
      
      
          #----------------------------------------------------------------------
          def setQImage(self, qImg):
              """"""
              try:
                  tBytesIO = io.BytesIO()
                  qByteArray = QByteArray()
                  qBuf = QBuffer(qByteArray)
                  qBuf.open(QIODevice.ReadWrite)
                  qImg.save(qBuf, 'PNG')
                  tBytesIO = io.BytesIO()
                  tBytesIO.write(qByteArray.data())
                  self._ImageByteIO = tBytesIO
              except:
                  self._ImageByteIO = None
      
      
          #----------------------------------------------------------------------
          def setPILImage(self, pImg):
              """"""
              tBytesIO = io.BytesIO()
              pImg.save(tBytesIO, 'png')
              self._ImageByteIO = tBytesIO
      
      
      
      #----------------------------------------------------------------------
      class DragMoveTest(QMainWindow):
          def __init__(self):
              """"""
              super(DragMoveTest,self).__init__()
              self.initGUI()
              self.show()
      
      
          #----------------------------------------------------------------------
          def initGUI(self):
              """"""
              self.treeView = QTreeView()
              modelTreeView = QStandardItemModel()
              self.treeView.setModel(modelTreeView)
              for i in range(0, 4):
                  objMod = ObjModel()
                  objMod.setQImage(QImage(flags = Qt.AutoColor))
                  item = QStandardItem('Test: %s' % str(i))
                  item.setData(objMod, Qt.UserRole + 1)
                  modelTreeView.invisibleRootItem().appendRow(item)
      
              self.treeView.setDragDropMode(QAbstractItemView.InternalMove)
              self.setCentralWidget(self.treeView)
      
      
      #----------------------------------------------------------------------
      def main(args):
          app = QApplication(sys.argv)
          qt_main_wnd = DragMoveTest()
          ret = app.exec_()
          sys.exit(ret)
      
      
      #----------------------------------------------------------------------
      if __name__ == "__main__":
          main(sys.argv)
      

      【讨论】:

        猜你喜欢
        • 2014-01-03
        • 2018-10-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-14
        • 2012-01-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多