На мой взгляд, это ошибка tkinter - при нажатии кнопка проверки получает фокус,
Нет, это не ошибка tkinter.Tkinter работает как задумано.Стандартная кнопка не получает фокус при нажатии.Однако кнопка ttk делает это.
Мне кажется, что tkinter кратко утверждает, что фокус принадлежит двум виджетам, сообщая об этом в неправильном порядке.
Это неверная оценка.Невозможно, чтобы фокус был одновременно на нескольких виджетах.
Проблема заключается в том, что вы явно устанавливаете фокус на кнопку-флажок, но не даете циклу событий tkinter шанс обработать<FocusOut>
событие, прежде чем продолжить.Поэтому, даже если вы думаете, что основное внимание уделяется кнопке проверки, это не так.Фокус не может измениться, пока tkinter не сможет обработать запрос на изменение фокуса.
Быстрое, хакерское решение - вызвать update
после изменения фокуса, что дает tkinter возможность обрабатыватьвсе ожидающие события.Однако вызов update
может быть сложным, если есть ожидающие события, которые могут вызвать повторный вызов обработчика.Мое личное правило - никогда не вызывать update
, если в этом нет особой необходимости.
Если ваша основная цель состоит в том, чтобы переключить фокус на кнопку-флажок (и, таким образом, принудительно обработать привязку виджета ввода <FocusOut>
дощелкнув по кнопке), вы можете создать привязку к классу виджета (Checkbutton
), который обрабатывается перед обработчиком кнопки.Это, или используйте кнопку ttk, которая автоматически меняет фокус.
Правильный минимальный воспроизводимый пример
Я не собираюсь показывать вам, как изменить ваш код, потому что он слишком сложен для обслуживаниякак хороший минимальный пример.Вместо этого вот программа, которая иллюстрирует проблему.
Начните с этого базового кода, который служит минимальным воспроизводимым примером для проблемы, которую вы наблюдаете.Обратите внимание, что когда вы нажимаете на запись и затем нажимаете на кнопку-флажок, порядок, который отображается в текстовом виджете, - это сначала щелчок кнопки-флажка, а затем изменение фокуса.
import tkinter as tk
from tkinter import ttk
class Example():
def __init__(self):
self.root = tk.Tk()
self.entry = tk.Entry(self.root)
self.cb = tk.Checkbutton(self.root, command=self.handle_checkbutton, text="Click me")
self.text = tk.Text(self.root, height=8, width=80, background="bisque")
self.vsb = tk.Scrollbar(self.root, command=self.text.yview)
self.text.configure(yscrollcommand=self.vsb.set)
self.entry.pack(side="top", fill="x")
self.cb.pack(side="top", anchor="w")
self.vsb.pack(side="right", fill="y")
self.text.pack(side="bottom", fill="both", expand=True)
self.entry.insert(0, "click here, then click on the checkbutton")
self.entry.bind("<FocusOut>", self.handle_focus_out)
self.entry.focus_set()
def handle_focus_out(self, event):
self.text.insert("end", "received entry <FocusOut>\n")
self.text.see("end")
def handle_checkbutton(self):
self.cb.focus_set()
self.text.insert("end", "received checkbutton command\n")
self.text.see("end")
e = Example()
tk.mainloop()
Решение 1: вызовите обновление дозаставить tkinter обработать изменение фокуса
В этом решении добавьте вызов к self.root.update()
сразу после изменения фокуса.Это даст tkinter возможность обработать изменение фокуса, прежде чем продолжить работу с остальной частью кода.
def handle_checkbutton(self):
self.cb.focus_set()
self.root.update()
self.text.insert("end", "received checkbutton command\n")
self.text.see("end")
Решение 2: используйте кнопку ttk checkbox
вместо вызова update
,Вы можете использовать кнопку проверки ТТК.Он имеет встроенное поведение установки фокуса на кнопку при нажатии.
Сначала измените обработчик, чтобы удалить код, управляющий фокусом:
def handle_checkbutton(self):
self.text.insert("end", "received checkbutton command\n")
self.text.see("end")
Затем используйте ttk.Checkbutton
вместо tk.Checkbutton
:
self.cb = ttk.Checkbutton(self.root, command=self.handle_checkbutton, text="Click me")
Преимущество здесь в том, что вам не нужно писать код для управления фокусом.
Решение 3: добавить обработку фокуса в класс tk Checkbutton
Третье решение - добавить привязку к классу checkbutton, чтобы имитировать поведение кражи фокуса кнопки ttk.Преимущество здесь в том, что вам нужно установить привязку только один раз, и она будет применяться ко всем кнопкам в вашем пользовательском интерфейсе.
Сначала добавьте следующий обработчик:
def set_cb_focus(self, event):
self.text.insert("end", "setting focus via class binding\n")
self.text.see("end")
event.widget.focus_set()
Затем добавьтекласс привязки в __init__
.Обратите внимание, что встроенное поведение кнопки проверки также выполняется с помощью привязок классов.Чтобы не удалять это поведение, нам нужно включить add=True
в команду bind
, которая добавляет новое поведение, а не заменяет встроенное поведение.
self.root.bind_class("Checkbutton", "<ButtonPress-1>", self.set_cb_focus)
Наконец, переключите код обратно на tk.Checkbutton
вместо ttk.Checkbutton
:
self.cb = tk.Checkbutton(self.root, command=self.handle_checkbutton, text="Click me")