我在 Canvas 中用 idlelib.TreeWidget 创建了 2 棵树,左边和右边。
如果双击,我也可以打印出树节点的名称,但我需要的是双击一个树节点将使某个树节点可见并被选中。
我这里有一个简单的例子。如果双击左侧的“level1”,右侧的“ccc”应该可见并自动选中。你是怎么做到的?
请运行以下代码:
from Tkinter import Tk, Frame, BOTH, Canvas
from xml.dom.minidom import parseString
from idlelib.TreeWidget import TreeItem, TreeNode
class DomTreeItem(TreeItem):
def __init__(self, node):
self.node = node
def GetText(self):
node = self.node
if node.nodeType == node.ELEMENT_NODE:
return node.nodeName
elif node.nodeType == node.TEXT_NODE:
return node.nodeValue
def IsExpandable(self):
node = self.node
return node.hasChildNodes()
def GetSubList(self):
parent = self.node
children = parent.childNodes
prelist = [DomTreeItem(node) for node in children]
itemlist = [item for item in prelist if item.GetText().strip()]
return itemlist
def OnDoubleClick(self):
print self.node.nodeName
left = '''
<level0>
<level1/>
</level0>
'''
right = '''
<aaa>
<bbb> <ccc/> </bbb>
</aaa>
'''
class Application(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.parent.geometry('%dx%d+%d+%d' % (800, 300, 0, 0))
self.parent.resizable(0, 0)
dom = parseString(left)
item = DomTreeItem(dom.documentElement)
self.canvas = Canvas(self, bg = "cyan")
self.canvas.grid(column = 0, row = 0, sticky = 'NSWE')
node = TreeNode(self.canvas, None, item)
node.update()
dom2 = parseString(right)
item2 = DomTreeItem(dom2.documentElement)
self.canvas2 = Canvas(self, bg = "yellow")
self.canvas2.grid(column = 1, row = 0, sticky = 'NSWE')
node2 = TreeNode(self.canvas2, None, item2)
node2.update()
self.pack(fill = BOTH, expand = True)
def main():
root = Tk()
Application(root)
root.mainloop()
if __name__ == '__main__':
main()
最佳答案
首先,您的双击回调必须知道您的 TreeNode node2
(我可以想到全局变量、DomTreeItem 中的属性或跳转到另一个组件)。
然后就可以依赖TreeNode的expand()
方法,读取children
属性,依次展开,直到需要的元素。请注意,children
属性仅在节点展开后才会填充。
1。快速回答
针对您提供的示例的快速而肮脏的解决方案
class DomTreeItem(TreeItem):
def OnDoubleClick(self):
if self.GetText() == "level1":
node2.expand()
node2.children[0].expand()
node2.children[0].children[0].select()
[...]
class Application(Frame):
def __init__(self, parent):
global node2
2。通用解决方案
这里有一个更通用的方法来显示树中的任意项。
def reach(node_tree, path):
tokens = path.split("/")
current_node = node_tree
for name in tokens:
if len(current_node.children) == 0 and current_node.state != "expanded":
current_node.expand()
candidates = [child for child in current_node.children if child.item.GetText() == name]
if len(candidates) == 0:
print("did not find '{}'".format(name))
return
current_node = candidates[0]
current_node.select()
你可能会这样使用它
if self.GetText() == "level1":
reach(node2, "bbb/ccc")
3。架构方案
除了扩展项目的选择外,我还通过 DIY 观察器向您推荐一个更简洁的架构。
DIY观察者
(模仿 Tkinter bind
调用,但自 generating event with user data is not properly handled 起不依赖 tkinter 机器)
class DomTreeItem(TreeItem):
def __init__(self, node, dbl_click_bindings = None):
self.node = node
self.dbl_click_bindings = [] if (dbl_click_bindings == None) else dbl_click_bindings
[...]
def OnDoubleClick(self):
self.fireDblClick()
def bind(self, event, callback):
'''mimic tkinter bind
'''
if (event != "<<TreeDoubleClick>>"):
print("err...")
self.dbl_click_bindings.append(callback)
def fireDblClick(self):
for callback in self.dbl_click_bindings:
callback.double_click(self.GetText())
专用组件
依靠上面介绍的 reach 方法。
class TreeExpander:
def __init__(self, node_tree, matching_items):
self.node_tree = node_tree
self.matching_items = matching_items
def double_click(self, item_name):
print("double_click ({0})".format(item_name))
if (item_name in self.matching_items):
reach(self.node_tree, self.matching_items[item_name])
订阅
class Application(Frame):
def __init__(self, parent):
[...]
expander = TreeExpander(node2, {
"level1": "bbb/ccc"
})
item.bind("<<TreeDoubleClick>>", expander)
我没有找到 idlelib 的文档,在这种情况下,您可以尝试查看代码。以下代码片段可让您找到托管此模块的文件。
import idlelib.TreeWidget
print(idlelib.TreeWidget.__file__)
关于Python Tkinter : Tree selection,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23022005/