2014-04-11 54 views
3

我已經創建了2個樹,在畫布上,左側和右側是idlelib.TreeWidget。Python Tkinter:樹的選擇

我也能夠打印出一個樹節點的名稱,如果雙擊,但我需要的是雙擊一個樹節點將使某個樹節點可見和選擇。

我在這裏有一個簡單的例子。如果雙擊左側的「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() 

回答

1

首先,你雙擊回調必須知道你的樹節點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()) 

專用部件

依靠上面給出的範圍的方法。

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__) 
+0

我喜歡你的解決方案,確實非常快速和骯髒。但是,如果有一個以上的孩子呢?如果我們知道節點的名稱,那麼擴展一下:node2.children [0] .expand() –

+0

我剛剛發現了:node2.children [0] .__ dict__給了我們對象屬性,並且我發現它的TreeItem屬性。所以只需檢查node2.children [0] .item.GetText() –

+0

你想回答我的其他問題:http://stackoverflow.com/questions/22996833/python-tkinter-canvas-scrolling-with-mousewheel –