QMimeData 中的 Python 对象

发布于 2024-10-03 03:55:17 字数 460 浏览 3 评论 0原文

我正在基于我的自定义模型实现拖放 QTreeView。一切正常,我的树显示数据,启用了拖放,现在最后一步就在我面前 - 放置和传输拖动的数据。为此,我需要在模型中实现 mimeTypes、mimeData 和 dropMimeData 方法。现在我的问题是:是否有任何简单的标准方法如何通过 QMimeData 传递任意 Python 对象?我只是在 QTreeView 中进行内部移动,它显示我的 Python 类 Person 的层次结构。我想重新排序它们。不能在应用程序之外进行拖放,甚至不能超出控制范围。我只找到了一个教程:链接文本。但这是唯一的方法吗?如果不将 Python 对象编码为 ByteArray,就无法完成此操作。我需要为我唯一的一类人提供非常简单的解决方案。谢谢。

I'm implementing drag and drop QTreeView based on my custom model. All works fine, my tree displays data, drag and drop is enabled and now the last step lies ahead of me - to drop and trasfer dragged data. To do this I need to implement mimeTypes, mimeData and dropMimeData methods in my model. And now my question: Is there any easy standard way how to pass an arbitrary Python object through QMimeData? I'm doing just an internal move within QTreeView which displays hierarchy of my Python classes Person. And I want to reorder them. No drag and drop outside the application, not even outside of control. I have found only single one tutorial: link text. But is it the only way? Cannot it be done without encoding the Python object into ByteArray. I need really simple solution for my only one class Person. Thank you.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

王权女流氓 2024-10-10 03:55:17

不要尝试通过重新设置底层 python 对象的父级来实现拖放。如果阻力来自流程外部,则此方法将不起作用;它也不适用于复制操作(您的节点对象可能不能存在于树中的多个位置)。

将拖放“移动”视为三个操作:

  1. 将数据序列化为某个字节字符串
  2. 反序列化为新索引(或多个新索引)
  3. (可选:如果“移动”而不是“复制”)删除旧索引

mineData() 和 dropMimeData() 是您提供的序列化和反序列化操作。 Python 提供了一些简单的方法来实现它们——查看 pickle 模块的文档。如果你幸运的话,pickle.dumps() 和 pickle.loads() 将为你开箱即用。

编辑:我不知道如何在评论中粘贴代码,所以这是我的评论所指的解决方案。这是安全的,因为如果您碰巧违反了规则,它将通过抛出 KeyError 来失败,而不是导致崩溃。

# drag: store off the data in a safe place, and serialize a cooky
# that the drop target can use to retrieve the data.
self.__tmp_storage_dct = { self.__tmp_storage_cooky: stuff }
m.setData(self.rowlistptr_mime_type, QByteArray(pickle.dumps(self.__tmp_storage_cooky)))
self.__tmp_storage_cooky += 1

# drop:
if mime.hasFormat(self.rowlistptr_mime_type):
  print "got tmpstorage"
  cooky = pickle.loads(mime.data(self.rowlistptr_mime_type).data())
  nodes = self.__tmp_storage_dct.pop(cooky)

Do not try to implement drag and drop by reparenting the underlying python object. This won't work if the drag comes from outside your process; nor will it work for a copy operation (your node objects probably cannot exist in multiple places in the tree).

Think of a drag and drop "move" as three operations:

  1. serialize the data to some byte string
  2. deserialize into a new index (or new indexes)
  3. (optional: if "move" rather than "copy") remove the old index(es)

mineData() and dropMimeData() are the serialize and deserialize operations that you provide. Python provides some easy ways to implement them -- check the documentation for the pickle module. If you're lucky, pickle.dumps() and pickle.loads() will work out-of-the-box for you.

Edit: I couldn't figure out how to paste code in comments, so here's the solution my comment refers to. This is safe, in the sense that it will fail by throwing a KeyError instead of causing crashes if you happen to break your rules.

# drag: store off the data in a safe place, and serialize a cooky
# that the drop target can use to retrieve the data.
self.__tmp_storage_dct = { self.__tmp_storage_cooky: stuff }
m.setData(self.rowlistptr_mime_type, QByteArray(pickle.dumps(self.__tmp_storage_cooky)))
self.__tmp_storage_cooky += 1

# drop:
if mime.hasFormat(self.rowlistptr_mime_type):
  print "got tmpstorage"
  cooky = pickle.loads(mime.data(self.rowlistptr_mime_type).data())
  nodes = self.__tmp_storage_dct.pop(cooky)
怀里藏娇 2024-10-10 03:55:17

好吧,我想我有一个可能的解决方案给你。

请记住,我在这个领域是一个完全的新手,所以不能保证他的a)有效b)是一个不错的解决方案c)不会让“真正的”程序员扔掉他们的午餐。

我所做的是将特定项目的整个祖先树转换为行列对的文本列表。 (即列出拖动项目的行和列,其父级的行和列,其父级的父级的行和列等......直到我们到达无效索引 - 即根)

这看起来像这样(此示例显示拖动的项目有四层深):

2;0,1;0,5;0,1,0
^   ^   ^   ^
|   |   |   |
|   |   |   great grandparent (and child of the root item)
|   |   |
|   |   grandparent
|   |
|   parent
|
item being dragged

稍后,在 dropMimeData 函数中,我反转列表(以便它从根读回被拖动的项目)并一次构建一个索引,直到我回到最初拖动的项目。

以下是使这一切正常工作的代码片段。再次强调,我不能保证这是一个好主意,只是它看起来可行并且不需要您将 python 对象序列化为 ByteArray。

希望这有帮助。

#---------------------------------------------------------------------------
def mimeTypes(self):
    """
    Only accept the internal custom drop type which is plain text
    """
    types = QtCore.QStringList() 
    types.append('text/plain') 
    return types 


#---------------------------------------------------------------------------
def mimeData(self, index): 
    """
    Wrap the index up as a list of rows and columns of each 
    parent/grandparent/etc
    """
    rc = ""
    theIndex = index[0] #<- for testing purposes we only deal with 1st item
    while theIndex.isValid():
        rc = rc + str(theIndex.row()) + ";" + str(theIndex.column())
        theIndex = self.parent(theIndex)
        if theIndex.isValid():
            rc = rc + ","
    mimeData = QtCore.QMimeData()
    mimeData.setText(rc)
    return mimeData


#---------------------------------------------------------------------------
def dropMimeData(self, data, action, row, column, parentIndex):
    """
    Extract the whole ancestor list of rows and columns and rebuild the 
    index item that was originally dragged
    """
    if action == QtCore.Qt.IgnoreAction: 
        return True 

    if data.hasText():
        ancestorL = str(data.text()).split(",")
        ancestorL.reverse() #<- stored from the child up, we read from ancestor down
        pIndex = QtCore.QModelIndex()
        for ancestor in ancestorL:
            srcRow = int(ancestor.split(";")[0])
            srcCol = int(ancestor.split(";")[1])
            itemIndex = self.index(srcRow, srcCol, pIndex)
            pIndex = itemIndex

    print itemIndex.internalPointer().get_name()
    return True

Ok, I think I have a possible solution for you.

Keep in mind that I am a complete neophyte in this area so no warranties that his a) works b) is a decent solution c) won't make a "real" programmer toss their lunch.

What I did was convert the entire ancestor tree of a particular item into a text list of row column pairs. (i.e. list the row and column of the dragged item, the row and column of its parent, the row and column of its parent's parent, etc... till we get to an invalid index - i.e. the root)

This looks something like this (this example shows that the dragged item is four levels deep):

2;0,1;0,5;0,1,0
^   ^   ^   ^
|   |   |   |
|   |   |   great grandparent (and child of the root item)
|   |   |
|   |   grandparent
|   |
|   parent
|
item being dragged

Later, in the dropMimeData function, I reverse the list (so that it reads from the root back down to the item being dragged) and build the indexes one at a time till I get back to the originally dragged item.

Here are the snippets of code that make that all work. Again, I can't warrantee that this is a good idea, just that it appears to work and does not require that you serialize your python objects into a ByteArray.

Hope this helps.

#---------------------------------------------------------------------------
def mimeTypes(self):
    """
    Only accept the internal custom drop type which is plain text
    """
    types = QtCore.QStringList() 
    types.append('text/plain') 
    return types 


#---------------------------------------------------------------------------
def mimeData(self, index): 
    """
    Wrap the index up as a list of rows and columns of each 
    parent/grandparent/etc
    """
    rc = ""
    theIndex = index[0] #<- for testing purposes we only deal with 1st item
    while theIndex.isValid():
        rc = rc + str(theIndex.row()) + ";" + str(theIndex.column())
        theIndex = self.parent(theIndex)
        if theIndex.isValid():
            rc = rc + ","
    mimeData = QtCore.QMimeData()
    mimeData.setText(rc)
    return mimeData


#---------------------------------------------------------------------------
def dropMimeData(self, data, action, row, column, parentIndex):
    """
    Extract the whole ancestor list of rows and columns and rebuild the 
    index item that was originally dragged
    """
    if action == QtCore.Qt.IgnoreAction: 
        return True 

    if data.hasText():
        ancestorL = str(data.text()).split(",")
        ancestorL.reverse() #<- stored from the child up, we read from ancestor down
        pIndex = QtCore.QModelIndex()
        for ancestor in ancestorL:
            srcRow = int(ancestor.split(";")[0])
            srcCol = int(ancestor.split(";")[1])
            itemIndex = self.index(srcRow, srcCol, pIndex)
            pIndex = itemIndex

    print itemIndex.internalPointer().get_name()
    return True
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文