如何在移动子树后更新TreeModel
我有一个自定义的通用树模型,它运行得很好。现在我想让用户能够通过拖放的方式来重新排列节点,所以我把一个节点移动到它的新父节点下。
但是,这样一来,树模型就需要通过一些方法来通知,比如 row_has_child_toggled
、row_deleted
和 row_inserted
。
显然没有 row_moved
这个方法,而调用 row_deleted
(针对原来的位置)和 row_inserted
(针对新位置)似乎不够。因此,我想我可能需要递归地发出这些更改。
考虑以下示例:
* A (0,)
* B (1,)
* C (1,0)
* D (1,0,0)
现在,如果我把 C 移动到 A,发生了以下事情:
row_delete( (1,0) ) # C
row_delete( (1,0,0) ) # D
row_inserted( (0,0) ) # C'
row_inserted( (0,0,0) ) # D'
child_toggle( (0,) ) # A
child_toggle( (1,) ) # B
child_toggle( (0,1) ) # C'
然而,gtk 仍然抱怨模型的状态不一致。我想到两件事:
- 也许调用这些函数的顺序很重要(如果是这样,有什么想法吗?)
- 从技术上讲,
child_toggle( (1,0) )
也会发生,但这个行 a) 已经被删除,b)row_has_child_toggled
需要一个tree_iter
的引用,而我无法获取,因为它已经不存在了。
也许我在这里走错了方向,那么处理这个问题的最佳方法是什么呢?
2 个回答
在我看来,这个更新应该是自动进行的,只要你正确地把树形视图(treeview)和它的“drag_data_get”和“drag_data_received”方法连接起来,并且允许它作为拖拽的源和目标,使用“enable_model_drag_dest”和“enable_model_drag_source”这两个方法就可以了。
对于树形视图来说,应该没有其他需要做的事情。
模型在调用“drag_data_received_data”时必须更新,但不需要“删除”任何东西,只需要根据具体情况使用“insert_before”或“insert_after”,就像这里展示的那样:
def drag_data_received_data(self, treeview, context, x, y, selection,
info, etime):
model = treeview.get_model()
data = selection.data
drop_info = treeview.get_dest_row_at_pos(x, y)
if drop_info:
path, position = drop_info
iter = model.get_iter(path)
if (position == gtk.TREE_VIEW_DROP_BEFORE
or position == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE):
model.insert_before(iter, [data])
else:
model.insert_after(iter, [data])
else:
model.append([data])
if context.action == gtk.gdk.ACTION_MOVE:
context.finish(True, True, etime)
return
这段代码来自于:
http://www.pygtk.org/pygtk2tutorial/sec-TreeViewDragAndDrop.html#DragDropReordering
在这个地方,程序“treeviewdnd.py”可以完美地展示你需要的内容。
希望这能解决你的问题!
我搞明白了:要删除一个子树,只需要删除这个子树的根节点(告诉模型这个路径已经不存在了,并根据情况切换父节点的has_child状态)。插入一个新的子树也是一样,所以我不需要递归地告诉模型一些事情。
不过,这两个操作的顺序很重要,所以要记住:
- 先删除,然后通知模型
- 再插入,并再次通知模型。