在 Tkinter 的樹視圖中,當節點展開和折疊時,節點會像任何子條目一樣被選中/取消選中。我的應用程式無法使用它,因此我創建了一個選擇處理程式并將其系結到 ButtonRelease 事件。問題是現在,shift-click 批量選擇不起作用 - 僅僅是因為我沒有在我的處理程式中撰寫它。但是用樹視圖撰寫很痛苦,我的 IID 在我的實作中不容易迭代。
如果節點被選中(我已經這樣做了),我想截取訊息,并為任何其他選擇呼叫默認處理程式,這樣我就可以在不重新發明輪子的情況下獲得 shift-click 批量選擇功能。我怎樣才能做到這一點?
我嘗試查看帶有和不帶有新回呼引數的 bind() 的結果,希望存盤默認的回呼/處理程式,但它不起作用,所以據我所知,這是一個死胡同。
import tkinter as tk
from tkinter.ttk import *
class TV: #Find a better name
def __init__(self, window):
self.tree = Treeview(window, selectmode="none", show="tree")
self.tree.pack(expan=1,fill='both')
self.tree.bind("<ButtonRelease-1>", self.treeSelect) #HERE: how to get the default handler?
#Generate dummy data
parents = ['1', '2', '3']
children = ['a', 'b', 'c']
for parent in parents:
self.tree.insert('', tk.END, iid=parent, text=parent)
for child in children:
#Insert the item
self.tree.insert(parent, tk.END, iid=parent ':' child, text=child)
#Private/protected methods
def treeSelect(self, event):
curItem = self.tree.focus() #Get selected item by getting the item in focus
#If the item has no children (i.e. it's not a node)
if(not len(self.tree.get_children(curItem))):
#Execute the selection
self.tree.selection_toggle(curItem) #HERE: how to call the default handler?
#Create the window
window = tk.Tk()
window.title("test")
tv = TV(window)
window.mainloop()
我也試過這個,但出于某種原因,Tkinter 認為當所選專案在其節點中折疊時需要清除選擇...
import tkinter as tk
from tkinter.ttk import *
class TV: #Find a better name
def __init__(self, window):
self.tree = Treeview(window, show="tree")
self.tree.pack(expan=1,fill='both')
self.tree.bind("<<TreeviewOpen>>", self.treeOpenCloseUnselect, ' ')
self.tree.bind("<<TreeviewClose>>", self.treeOpenCloseUnselect, ' ')
#Generate dummy data
parents = ['1', '2', '3']
children = ['a', 'b', 'c']
for parent in parents:
self.tree.insert('', tk.END, iid=parent, text=parent)
for child in children:
#Insert the item
self.tree.insert(parent, tk.END, iid=parent ':' child, text=child)
#Private/protected methods
def treeOpenCloseUnselect(self, event=None):
curItem = self.tree.focus() #Get selected item by getting the item in focus
#If the item has no children (i.e. it's not a node)
self.tree.selection_remove(curItem)
#Create the window
window = tk.Tk()
window.title("test")
tv = TV(window)
window.mainloop()
uj5u.com熱心網友回復:
在我看來,實作您想要的最短方法是使用 self.tree = Treeview(window)樹視圖本身的選擇,然后檢查有效專案。一個例子:
def treeSelect(self, event):
selection = self.tree.selection() #Get selected item by getting the item in focus
valid = []
for item in selection:
if len(item) != 1:
valid.append(item)
self.tree.selection_set(valid)
但請注意,這更像是一個 xyproblem,并且有不同的方法可用。我會使用標簽并添加一個自定義的選擇顏色。但是您可能希望查看帶有復選框的 Treeview 作為選項。
uj5u.com熱心網友回復:
我不確定我是否理解您要執行的操作,但您似乎只是想阻止選擇帶有子節點的節點。如果是這種情況,我認為您將解決方案倒退。
我將介紹兩種解決方案。第一個解決方案防止點擊的專案被選中,同時允許所有其他互動正常作業。第二個將檢測何時選擇發生任何變化,并取消選擇所有父節點。
防止默認行為
在以下示例中,單擊父級將打開或關閉它,但不允許選擇它。關鍵是回傳 string "break",這將阻止默認系結的任何額外處理。
class TV: #Find a better name
def __init__(self, window):
...
self.tree = Treeview(window, selectmode="extended", show="tree")
self.tree.pack(expan=1,fill='both')
self.tree.bind("<ButtonPress-1>", self.treeSelect)
...
def treeSelect(self, event):
"""Disallow parent items from being selected when clicked"""
curItem = self.tree.identify('item', event.x, event.y)
if len(self.tree.get_children(curItem)):
opened = self.tree.item(curItem)["open"]
self.tree.item(curItem, open=not opened)
return "break"
請注意,selectmode它已設定為“擴展”,因此將啟用默認系結。此外,系結已設定為按鈕按下而不是釋放。
這允許用戶單擊父級來切換子級的可見性,但不會選擇父級。它允許所有其他系結按應有的方式作業。
但是,這也允許用戶單擊一個子節點,然后按住 shift 單擊另一個父節點下的子節點,并且將選擇其間的任何父節點。用戶還可以使用鍵盤選擇父節點。
以編程方式調整選擇
如果上述解決方案不是您想要的,那么我建議的解決方案是<<TreeviewSelect>>通過迭代選擇來系結和取消選擇您不想被選擇的專案。只要選擇更改,無論是通過您的代碼、滑鼠單擊還是通過鍵盤,都會觸發此事件。
該解決方案看起來像這樣:
...
self.tree.bind("<<TreeviewSelect>>", self.treeSelect)
...
def treeSelect(self, event):
"""Deselect all parent nodes"""
items = self.tree.selection()
for item in items:
if len(self.tree.get_children(item)):
self.tree.selection_remove(item)
這可能是更好的解決方案,因為它允許用戶在使用滑鼠的同時使用鍵盤,并且總是會阻止所有父母被選中。
有關回傳"break"作業方式為何如此的更多資訊,請參閱問題Basic query about bindtags in tkinter 的此答案。該問題涉及按鍵,但滑鼠點擊的處理方式完全相同。
uj5u.com熱心網友回復:
我發現的唯一解決方法是記住 Button-1 (clicked) 事件的選擇,并在釋放按鈕事件中,如果最后點擊了一個節點,則洗掉其選定狀態并恢復之前的選擇:
import tkinter as tk
from tkinter.ttk import *
from tkinter import messagebox
class TV: #Find a better name
def __init__(self, window):
self.tree = Treeview(window, show="tree")
self.tree.pack(expan=1,fill='both')
self.tree.bind("<Button-1>", self.treeSelMem, ' ')
self.tree.bind("<ButtonRelease-1>", self.treeSelRestore, ' ')
#Generate dummy data
parents = ['1', '2', '3']
children = ['a', 'b', 'c']
for parent in parents:
self.tree.insert('', tk.END, iid=parent, text=parent)
for child in children:
#Insert the item
self.tree.insert(parent, tk.END, iid=parent ':' child, text=child)
#Private/protected methods
def treeSelMem(self, event):
#Memorise the selection before it changes
self.memSel = self.tree.selection()
def treeSelRestore(self, event):
#The selection has been made. Remove nodes from the selection
for item in self.tree.selection():
if len(item) == 1: #If the item is a parent/node
self.tree.selection_remove(item)
#If a parent has been clicked
curItem = self.tree.focus()
if(len(self.tree.get_children(curItem))):
#Move focus to the first child to avoid not being able to select anything else
self.tree.focus(self.tree.get_children(curItem)[0])
#Clear its selected state
self.tree.selection_remove(curItem)
#And restore from memory the previously selected items
self.tree.selection_set(self.memSel)
#Create the window
window = tk.Tk()
window.title("test")
tv = TV(window)
window.mainloop()
然而,這只解決了這個特定的問題,所以如果有人知道如何回答這個比這更籠統的問題,請繼續,我會很高興地接受它。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/511682.html
