Благодаря приведенной выше ссылке на sourceforge, любезно предоставленной fhdrsdg, похоже, что в tkinter единственный способ сделать виджет невосприимчивым к любым действиям пользователя - это удалить список тегов привязки (фактически, кортеж) для этого виджета.
Теги привязки определяют порядок, в котором обрабатываются события для этого виджета (подробнее см. https://www.tcl.tk/man/tcl8.4/TkCmd/bindtags.htm) - по умолчанию, за исключением окон верхнего уровня, виджет имеет четыре тега: имя виджета сам по себе, имя класса виджета, окно верхнего уровня (обозначается как «.» - по-видимому, реликвия от tcl days) и специальное ключевое слово «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).
Подробное описание этой темы см. В статье Стивена Лиди и Нэнси Уолш (2002) «Освоение Perl / Tk: графические пользовательские интерфейсы в Perl», O'Reilly, с.372 и след. Они фактически предлагают удалить привязки виджета, чтобы сделать его «инертным» (как они его называют) на стр.373.
Следующая реализация инкапсулирует эту технику в смешанном классе, называемом DisableMixin, который расширяет метод state () виджетов ttk и предоставляет 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()