Python tkinter.ttk:如何禁用Treeview

时间:2018-09-05 09:09:29

标签: python tkinter

禁用诸如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之类的用户操作? Google和文档似乎都没有答案。

1 个答案:

答案 0 :(得分:0)

由于上面由fhdrsdg提供的sourceforge链接,看来tkinter中使窗口小部件不响应任何用户操作的唯一方法是删除该窗口小部件的绑定标签列表(实际上是元组)。

绑定标签确定该窗口小部件的事件处理顺序(有关更多详细信息,请参见https://www.tcl.tk/man/tcl8.4/TkCmd/bindtags.htm)-默认情况下,除了顶级窗口外,窗口小部件具有四个标签:窗口小部件本身的名称,小部件的类名,顶级窗口(由'。'表示-显然是tcl天的遗物)和特殊关键字'all'(其重要性对我来说仍然是个谜),例如:

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

当事件发生时,从左到右搜索标签,以找到与事件匹配的序列-如果找到一个,则调用绑定的回调,除非返回“ break”以中止该过程,否则进行搜索从实例绑定到类绑定,再到顶级窗口绑定,再到“全部”(无论是什么)。

这是默认的开箱即用的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页及以下。他们实际上建议删除小部件的绑定标签,以使其在p.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()