繁体   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