簡體   English   中英

Python tkinter.ttk:如何禁用 Treeview

[英]Python tkinter.ttk: how to disable Treeview

禁用小部件,例如 ttk.Button:

button = ttk.Button(frame, text='Quit', command=self.quit)
button.state(('disabled',))

使其對用戶操作(例如單擊)無響應。 但是,禁用 ttk.Treeview:

tree = ttk.Treeview(frame,column=['one','two'])
tree.state(('disabled',))

僅用於更改其樣式 - 它仍然響應用戶操作,例如調整列大小、選擇和滾動。 有沒有辦法讓 ttk.Treeview 停止響應像 ttk.Button 這樣的用戶操作? 谷歌和文檔似乎都沒有答案。

感謝上面由 fhdrsdg 提供的 sourceforge 鏈接,似乎在 tkinter 中呈現小部件對任何用戶操作都沒有響應的唯一方法是刪除該小部件的綁定標簽列表(實際上是元組)。

綁定標簽確定處理該小部件的事件的順序(有關更多詳細信息,請參見https://www.tcl.tk/man/tcl8.4/TkCmd/bindtags.htm ) - 默認情況下,頂級除外windows 一個小部件有四個標簽:小部件本身的名稱、小部件的類名、頂層窗口(由 '.' 表示——顯然是 tcl 時代的遺物)和特殊關鍵字“all”(其意義仍然存在)對我來說是個謎),例如:

('.!frame.!mytreeview', 'Treeview', '.', 'all')

當事件發生時,標簽從左到右搜索匹配事件的序列 - 如果找到,則調用綁定回調,除非它返回“中斷”以中止過程,否則搜索將繼續沿鏈 - 從實例綁定到類綁定,再到頂級窗口綁定,再到“所有”(無論是什么)。

這是默認的、開箱即用的 tkinter 行為。 但是,沒有什么可以阻止我們更改小部件的綁定標簽,從而更改該小部件的事件處理方式。 例如,刪除所有標簽將完全阻止處理事件,從而使小部件完全無響應。

綁定標簽可以使用 bindtags 小部件方法進行操作:

bindtags(self, tagList=None)
    Set or get the list of bindtags for this widget.

    With no argument return the list of all bindtags associated with
    this widget. With a list of strings as argument the bindtags are
    set to this list. The bindtags determine in which order events are
    processed (see bind).

有關該主題的徹底/不遺余力的處理,請參閱 Stephen Lidie 和 Nancy Walsh(2002 年)“掌握 Perl/Tk:Perl 中的圖形用戶界面”O'Reilly,第 372 頁等。 他們實際上建議在第 373 頁刪除小部件的綁定標簽以使其“惰性”(正如他們所說的那樣)。

以下實現將此技術封裝在名為 DisableMixin 的混合類中,該類擴展了 ttk 小部件的 state() 方法並提供了 4 個簡單的實用程序方法來檢查和設置小部件的啟用狀態:

import tkinter as tk
import tkinter.ttk as ttk


class DisableMixin(object):

    def state(self,statespec=None):
        if statespec:
            e = super().state(statespec)
            if 'disabled' in e:
                self.bindtags(self.tags)
            elif '!disabled' in e:
                self.tags = self.bindtags()
                self.bindtags([None])
            return e
        else:
            return super().state()

    def disable(self):
        self.state(('disabled',))

    def enable(self):
        self.state(('!disabled',))

    def is_disabled(self):
        return 'disabled' in self.state()

    def is_enabled(self):
        return not self.is_disabled()

state() 方法檢查小部件是被啟用還是被禁用,並相應地操作綁定標簽 - 如果小部件被禁用,則在刪除它們之前將標簽保存在實例變量中,並在重新啟用時加載它們。 此混合僅適用於 ttk 小部件(tk 小部件沒有 state() 方法),並且必須列為最左側的父類(即 MRO 中的第一個超類),例如:

class myTreeview(DisableMixin, ttk.Treeview): pass

最后是一個小演示程序:

import tkinter as tk
import tkinter.ttk as ttk


class DisableMixin(object):

    def state(self,statespec=None):
        if statespec:
            e = super().state(statespec)
            if 'disabled' in e:
                self.bindtags(self.tags)
            elif '!disabled' in e:
                self.tags = self.bindtags()
                self.bindtags(['xxx'])
            return e
        else:
            return super().state()

    def disable(self):
        self.state(('disabled',))

    def enable(self):
        self.state(('!disabled',))

    def is_disabled(self):
        return 'disabled' in self.state()

    def is_enabled(self):
        return not self.is_disabled()


class myTreeview(DisableMixin, ttk.Treeview): pass


class myApp(tk.Tk):

    def __init__(self, *args,**kwargs):
        super().__init__(*args,**kwargs)
        self.tree = None
        self.setup_widgets()
        self.load_data()

    def setup_widgets(self):
        self.columnconfigure(0,weight=1)
        self.rowconfigure(0,weight=0)
        self.rowconfigure(1,weight=1)

        self.btn = ttk.Button(self,text='CLICK TO DISABLE', command=self.btnclicked)
        self.btn.grid(row=0,column=0,sticky='we')

        frame = ttk.Frame(self)
        frame.grid(row=1,column=0,sticky='nsew')

        self.tree = myTreeview(frame, columns=emp_header, show="headings")
        vsb = ttk.Scrollbar(frame,orient="vertical",command=self.tree.yview)
        self.tree.configure(yscrollcommand=vsb.set)

        self.tree.grid(row=0, column=0, sticky='nsew')
        vsb.grid(row=0, column=1, sticky='ns')

        frame.grid_columnconfigure(0, weight=1)
        frame.grid_columnconfigure(1, weight=0)
        frame.grid_rowconfigure(0, weight=1)


    def btnclicked(self):

        if self.tree.is_enabled():
            self.tree.disable()
            self.btn['text'] = 'CLICK TO ENABLE'
        else:
            self.tree.enable()
            self.btn['text'] = 'CLICK TO DISABLE'


    def load_data(self):
        for col in emp_header:
            self.tree.heading(col, text=col)

        for emp in emp_list:
            self.tree.insert('', tk.END, values=emp)


# --- test data
emp_header = ['Employee', 'Based','Salary']
emp_list = [
('Justin Case', 'London', 80000) ,
('Jerry Khan', 'Aberdeen', 67000) ,
('Jordie Banks', 'Cardiff', 42000) ,
('Angel Falls', 'Manchester', 65000) ,
('Judas Priest', 'Canterbury', 96000) ,
('Pearl Harper', 'Scarborough', 43000) ,
('Julian Date', 'York', 54000) ,
('Perry Winkle', 'Belfast', 78000) ,
('Kate Canaveral', 'Liverpool', 49000) ,
('Bill Lading', 'Bath', 69000) ,
]

app = myApp()
app.mainloop()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM