如何为密钥绑定python tkinter设置多个类实例的焦点



我在python焦点绑定中做错了什么?

我正在尝试将按钮小部件绑定到键盘输入,但没有发生任何事情,或者焦点结束于要加载的类的最后一个实例。

我在按钮后面的基本代码中添加了self.bind('<Return>', lambda e: self.convert)。没有。尝试了container.bind('<Return>', lambda e: self.convert),但一无所获。我不知道为什么代码没有绑定除了焦点之外的关键事件最终出现在错误的位置(焦点总是出现在类的最后一个实例上(。这是我使用的代码https://github.com/photodude/Tkconverter

在我将代码修改为更OOP 之前,在自述文件中链接到源非OOP教程和源教程的所有文件

如何将焦点集中在";活动的";类的用户可见实例?

最小、可复制示例上面提供了该代码的链接。

  1. 从App.py文件运行python代码
  2. 在GUI中选择";F到C">
  3. 在输入框中输入一个数值(如98或100(
  4. 按回车键

当您打开";F到C";并且你按下回车键,你会得到一个弹出错误";无法将字符串转换为浮点";但当你在";C到F";发生正确的操作(温度转换(。";错误";弹出在";F到C";与单击";转换";按钮,并且输入字段为空或具有数值以外的内容。这个弹出错误发生在";C到F";然后按回车键输入空白或非数值。如果你在";F到C";或";C到F";,在输入框中输入一个数值(如98或100(;转换";按钮,一切如预期。

期望的绑定行为是用户可见的活动实例具有密钥绑定的活动焦点;转换";按钮有。

根据社区请求发布的代码。有关当前代码和尝试修复的历史记录,请参阅Github。应用

# source https://www.pythontutorial.net/tkinter/tkraise/
import tkinter as tk
from tkinter import ttk
from tkinter.messagebox import showerror
from ControlFrame import ControlFrame
class App(tk.Tk):
def __init__(self):
super().__init__()
self.title('Temperature Converter')
self.geometry('300x120')
self.resizable(False, False)

if __name__ == "__main__":
app = App()
ControlFrame(app)
app.mainloop()

ControFrame.py

# source https://www.pythontutorial.net/tkinter/tkraise/
import tkinter as tk
from tkinter import ttk
from ConverterFrame import ConverterFrame
from TemperatureConverter import TemperatureConverter
class ControlFrame(ttk.LabelFrame):
def __init__(self, container):
super().__init__(container)
self['text'] = 'Options'
# radio buttons
self.selected_value = tk.IntVar()
ttk.Radiobutton(
self,
text='F to C',
value=0,
variable=self.selected_value,
command=self.change_frame).grid(column=0, row=0, padx=5, pady=5)
ttk.Radiobutton(
self,
text='C to F',
value=1,
variable=self.selected_value,
command=self.change_frame).grid(column=1, row=0, padx=5, pady=5)
self.grid(column=0, row=1, padx=5, pady=5, sticky='ew')
# initialize frames
self.frames = {}
self.frames[0] = ConverterFrame(
container,
'Fahrenheit',
TemperatureConverter.fahrenheit_to_celsius
)
self.frames[1] = ConverterFrame(
container,
'Celsius',
TemperatureConverter.celsius_to_fahrenheit
)
self.change_frame()
def change_frame(self):
for frame in self.frames.values():
frame.reset()
frame.grid_remove()
frame = self.frames[self.selected_value.get()]
frame.reset()
frame.tkraise()
frame.grid()
frame.focus_set()

ConverterFrame.py

# source https://www.pythontutorial.net/tkinter/tkraise/
import tkinter as tk
from tkinter import ttk
from tkinter.messagebox import showerror
class ConverterFrame(ttk.Frame):
def __init__(self, container, unit_from, converter):
super().__init__(container)
self.unit_from = unit_from
self.converter = converter
self.master.bind('<Return>', lambda event=None: self.enter_key())
# field options
options = {'padx': 5, 'pady': 0}
# temperature label
self.temperature_label = ttk.Label(self, text=self.unit_from)
self.temperature_label.grid(column=0, row=0, sticky='w', **options)
# temperature entry
self.temperature = tk.StringVar()
self.temperature_entry = ttk.Entry(self, textvariable=self.temperature)
self.temperature_entry.grid(column=1, row=0, sticky='w', **options)
self.temperature_entry.focus()
# button
self.convert_button = ttk.Button(self, text='Convert')
self.convert_button.grid(column=2, row=0, sticky='w', **options)
self.convert_button.configure(command=self.convert)
# self.convert_button.bind('<Return>', lambda event=None: self.convert)
# result label
self.result_label = ttk.Label(self)
self.result_label.grid(row=1, columnspan=3, **options)
# add padding to the frame and show it
self.grid(column=0, row=0, padx=5, pady=5, sticky="nsew")
def convert(self, event=None):
"""  Handle button click event
"""
try:
input_value = float(self.temperature.get())
result = self.converter(input_value)
self.result_label.config(text=result)
except ValueError as error:
showerror(title='Error', message=error)
def reset(self):
self.temperature_entry.delete(0, "end")
self.result_label.text = ''
def enter_key(self):
self.convert()

温度转换器.py

# source https://www.pythontutorial.net/tkinter/tkraise/
class TemperatureConverter:
@staticmethod
def fahrenheit_to_celsius(f, format=True):
result = (f - 32) * 5/9
if format:
return f'{f} Fahrenheit = {result:.2f} Celsius'
return result
@staticmethod
def celsius_to_fahrenheit(c, format=True):
result = c * 9/5 + 32
if format:
return f'{c} Celsius = {result:.2f} Fahrenheit'
return result

尝试了Python Tkinter的解决方案:将按键事件绑定到ttk中的活动选项卡。笔记本如以下简化示例,但不成功

class ConverterFrame(ttk.Frame):
def __init__(...):
...
tag = str(self)
self._add_bindtag(self, tag)
self.bind_class(tag, '<Return>', lambda event=None: self.convert())
def _add_bindtag(self, widget, tag):
bindtags = widget.bindtags()
if tag not in bindtags:
widget.bindtags((tag,) + bindtags)
for child in widget.winfo_children():
self._add_bindtag(child, tag)

这里的解决方案是更正ControlFrame.py以初始化change_frame()上的每个帧实例。源教程中的原始错误实例化了ConverterFrame()类,最后一个错误将取代与焦点和绑定相关的所有先前错误。

通过移动self.frames[0] = ConverterFrame(...)。。。并在change_frame()上调用这些函数,纠正了帧替换问题。

GitHub上有完整的修改代码https://github.com/photodude/Tkconverter

最新更新