基于另一个条目小部件的内容更新条目小部件
Update entry widget based contents of another entry widget
我的代码包含两个条目小部件,它们应该保存图像的高度和宽度。
当用户在两个小部件之一中输入数字调整高度或宽度时,另一个的值应该根据输入的值和固定的纵横比进行调整。
我的第一种方法是将更新函数绑定到按键事件,但我遇到了导致在更改文本变量之前执行绑定函数的错误。该问题在此处描述:Tkinter, update Entry widget before calling <Key> binding。建议的解决方案是使用变量跟踪而不是按键事件绑定。但是,由于在我的例子中,一个变量的更改会执行一个更改另一个变量的函数,因此我希望这些函数在无限循环中相互执行。这并没有发生,但肯定没有产生想要的结果。
import tkinter as tk
import tkinter.ttk as ttk
class App:
def __init__(self, master, aspect_ratio):
self.master = master
self.aspect_ratio = aspect_ratio
self.shape_x = tk.DoubleVar()
self.shape_y = tk.DoubleVar()
self.shape_x.trace('w', self.update_y)
self.shape_y.trace('w', self.update_x)
# Entry for x dimension
self.entry_x = ttk.Entry(self.master, textvariable=self.shape_x, width=5)
self.entry_x.pack(side='left', fill='x', expand='yes')
# Label for multiplication sign
self.entry_label = tk.Label(self.master, text='x', fg='gray')
self.entry_label.pack(side='left')
# Entry for y dimension
self.entry_y = ttk.Entry(self.master, textvariable=self.shape_y, width=5)
self.entry_y.pack(side='left', fill='x', expand='yes')
def update_x(self, *args):
self.shape_x.set(self.shape_y.get() * self.aspect_ratio)
def update_y(self, *args):
self.shape_y.set(self.shape_x.get() / self.aspect_ratio)
if __name__ == '__main__':
root = tk.Tk()
app = App(master=root, aspect_ratio=2)
root.mainloop()
实现我所寻找的目标的正确方法是什么?
编辑:将 update_y 中的 * 替换为 /
所以,我解决这个问题的方法是有一个值存储,其中 Entry
小部件是最后更新的,然后有一个 "checker" 函数,它以微小的间隔检查是否 Entry
已更新再更新其他:
from tkinter import *
class App:
def __init__(self, root):
self.root = root
self.aspect = 2
self.widthvalue = StringVar(name="width")
self.widthvalue.set(1)
self.heightvalue = StringVar(name="height")
self.heightvalue.set(str(int(self.widthvalue.get())*self.aspect))
self.widthvalue.trace("w", lambda name, index, mode, width=self.widthvalue: self.entrychange(width))
self.heightvalue.trace("w", lambda name, index, mode, height=self.heightvalue: self.entrychange(height))
self.width = Entry(self.root, textvariable=self.widthvalue)
self.height = Entry(self.root, textvariable=self.heightvalue)
self.width.pack()
self.height.pack()
self.lastupdated = None
self.root.after(10, self.update)
def entrychange(self, value):
self.lastupdated = str(value)
def update(self):
if self.lastupdated != None:
if self.lastupdated == "width" and self.widthvalue.get() != "" and self.widthvalue.get() != "0":
self.heightvalue.set(str(int(int(self.widthvalue.get())/self.aspect)))
self.height.update()
self.lastupdated = None
elif self.lastupdated == "height" and self.heightvalue.get() != "" and self.heightvalue.get() != "0":
self.widthvalue.set(str(int(self.heightvalue.get())*self.aspect))
self.width.update()
self.lastupdated = None
self.root.after(10, self.update)
root = Tk()
App(root)
root.mainloop()
我们来分解一下。
我们要做的第一件事是设置要保持的纵横比。在这种情况下,我们说高度将始终是宽度的两倍:
self.aspect = 2
接下来,我们创建 StringVar
变量来存储两个 Entry
小部件的值,并将它们的值设置为绝对最小值:
self.widthvalue = StringVar(name="width")
self.widthvalue.set(1)
self.heightvalue = StringVar(name="height")
self.heightvalue.set(str(int(self.widthvalue.get())*self.aspect))
然后我们在这些变量上设置跟踪,以便每次更新它们时我们调用一个函数 entrychange
。这个函数相对简单,它只是更新另一个名为 self.lastupdated
的变量,该变量存储 width
或 height
,具体取决于上次更新的 Entry
:
self.widthvalue.trace("w", lambda name, index, mode, width=self.widthvalue: self.entrychange(width))
self.heightvalue.trace("w", lambda name, index, mode, height=self.heightvalue: self.entrychange(height))
然后我们做一些无聊的事情,绘制小部件,设置它们的 textvariable
,打包它们并设置默认值 self.lastupdated
:
self.width = Entry(self.root, textvariable=self.widthvalue)
self.height = Entry(self.root, textvariable=self.heightvalue)
self.width.pack()
self.height.pack()
self.lastupdated = None
在此之后,我们启动一个 .after()
调用。这允许您在 tkinter
:
中连续循环
self.root.after(10, self.update)
这将启动不断 运行 我们的 update
函数的过程。
更新函数首先检查 Entry
是否已更新:
if self.lastupdated != None:
然后它会检查哪个是最后更新的,并确保该字段不为空或不等于 0。这是因为空白值和 0 都会扰乱我们的数学计算(并且对于图片):
if self.lastupdated == "width" and self.widthvalue.get() != "" and self.widthvalue.get() != "0":
然后我们将相反的 Entry
小部件的值设置为双倍或一半(取决于更改了哪个 Entry
),更新 GUI 中的小部件并设置变量 self.lastupdated
恢复为默认值:
self.heightvalue.set(str(int(int(self.widthvalue.get())/self.aspect)))
self.height.update()
self.lastupdated = None
对于另一个 Entry
我们做的差不多。
就我个人而言,我会命名我的条目字段,并在编辑时检查焦点所在的条目。由于两个跟踪都被激活,我们可以使用焦点来告诉我们要编辑的字段。
我必须构建一个 try/except 语句来处理 get()
出于某种原因不从字段中提取值的奇怪行为。当我用我的打印语句进行测试时,它似乎得到了正确的数字,但可能是由于两次调用导致 get() 失败。
示例代码如下:
import tkinter as tk
import tkinter.ttk as ttk
class App(tk.Tk):
def __init__(self, aspect_ratio):
super().__init__()
self.aspect_ratio = aspect_ratio
self.previous_x = 0
self.previous_y = 0
self.shape_x = tk.DoubleVar(self)
self.shape_y = tk.DoubleVar(self)
self.shape_x.trace('w', self.update_x_y)
self.shape_y.trace('w', self.update_x_y)
ttk.Entry(self, name='x', textvariable=self.shape_x, width=5).pack(side='left', fill='x', expand='yes')
tk.Label(self, text='x', fg='gray').pack(side='left')
ttk.Entry(self, name='y', textvariable=self.shape_y, width=5).pack(side='left', fill='x', expand='yes')
def update_x_y(self, *args):
name = self.focus_get().winfo_name()
try:
x = self.shape_x.get()
y = self.shape_y.get()
if name == 'x':
self.previous_y = x * self.aspect_ratio
self.shape_y.set(self.previous_y)
elif name == 'y':
self.previous_x = y * self.aspect_ratio
self.shape_x.set(self.previous_x)
else:
print('Focus is not one of the 2 entry fields. Do nothing.')
except:
print('Handling odd error from the double call to the function returning empty string from get().')
if __name__ == '__main__':
App(aspect_ratio=2).mainloop()
可以说,最简单的解决方案是绑定 <KeyRelease>
事件,因为修改发生在 press 键上。这样,您就不需要使用 textvariable
属性的开销。
self.entry_x.bind("<Any-KeyRelease>", self.update_y)
self.entry_y.bind("<Any-KeyRelease>", self.update_x)
我的代码包含两个条目小部件,它们应该保存图像的高度和宽度。 当用户在两个小部件之一中输入数字调整高度或宽度时,另一个的值应该根据输入的值和固定的纵横比进行调整。
我的第一种方法是将更新函数绑定到按键事件,但我遇到了导致在更改文本变量之前执行绑定函数的错误。该问题在此处描述:Tkinter, update Entry widget before calling <Key> binding。建议的解决方案是使用变量跟踪而不是按键事件绑定。但是,由于在我的例子中,一个变量的更改会执行一个更改另一个变量的函数,因此我希望这些函数在无限循环中相互执行。这并没有发生,但肯定没有产生想要的结果。
import tkinter as tk
import tkinter.ttk as ttk
class App:
def __init__(self, master, aspect_ratio):
self.master = master
self.aspect_ratio = aspect_ratio
self.shape_x = tk.DoubleVar()
self.shape_y = tk.DoubleVar()
self.shape_x.trace('w', self.update_y)
self.shape_y.trace('w', self.update_x)
# Entry for x dimension
self.entry_x = ttk.Entry(self.master, textvariable=self.shape_x, width=5)
self.entry_x.pack(side='left', fill='x', expand='yes')
# Label for multiplication sign
self.entry_label = tk.Label(self.master, text='x', fg='gray')
self.entry_label.pack(side='left')
# Entry for y dimension
self.entry_y = ttk.Entry(self.master, textvariable=self.shape_y, width=5)
self.entry_y.pack(side='left', fill='x', expand='yes')
def update_x(self, *args):
self.shape_x.set(self.shape_y.get() * self.aspect_ratio)
def update_y(self, *args):
self.shape_y.set(self.shape_x.get() / self.aspect_ratio)
if __name__ == '__main__':
root = tk.Tk()
app = App(master=root, aspect_ratio=2)
root.mainloop()
实现我所寻找的目标的正确方法是什么?
编辑:将 update_y 中的 * 替换为 /
所以,我解决这个问题的方法是有一个值存储,其中 Entry
小部件是最后更新的,然后有一个 "checker" 函数,它以微小的间隔检查是否 Entry
已更新再更新其他:
from tkinter import *
class App:
def __init__(self, root):
self.root = root
self.aspect = 2
self.widthvalue = StringVar(name="width")
self.widthvalue.set(1)
self.heightvalue = StringVar(name="height")
self.heightvalue.set(str(int(self.widthvalue.get())*self.aspect))
self.widthvalue.trace("w", lambda name, index, mode, width=self.widthvalue: self.entrychange(width))
self.heightvalue.trace("w", lambda name, index, mode, height=self.heightvalue: self.entrychange(height))
self.width = Entry(self.root, textvariable=self.widthvalue)
self.height = Entry(self.root, textvariable=self.heightvalue)
self.width.pack()
self.height.pack()
self.lastupdated = None
self.root.after(10, self.update)
def entrychange(self, value):
self.lastupdated = str(value)
def update(self):
if self.lastupdated != None:
if self.lastupdated == "width" and self.widthvalue.get() != "" and self.widthvalue.get() != "0":
self.heightvalue.set(str(int(int(self.widthvalue.get())/self.aspect)))
self.height.update()
self.lastupdated = None
elif self.lastupdated == "height" and self.heightvalue.get() != "" and self.heightvalue.get() != "0":
self.widthvalue.set(str(int(self.heightvalue.get())*self.aspect))
self.width.update()
self.lastupdated = None
self.root.after(10, self.update)
root = Tk()
App(root)
root.mainloop()
我们来分解一下。
我们要做的第一件事是设置要保持的纵横比。在这种情况下,我们说高度将始终是宽度的两倍:
self.aspect = 2
接下来,我们创建 StringVar
变量来存储两个 Entry
小部件的值,并将它们的值设置为绝对最小值:
self.widthvalue = StringVar(name="width")
self.widthvalue.set(1)
self.heightvalue = StringVar(name="height")
self.heightvalue.set(str(int(self.widthvalue.get())*self.aspect))
然后我们在这些变量上设置跟踪,以便每次更新它们时我们调用一个函数 entrychange
。这个函数相对简单,它只是更新另一个名为 self.lastupdated
的变量,该变量存储 width
或 height
,具体取决于上次更新的 Entry
:
self.widthvalue.trace("w", lambda name, index, mode, width=self.widthvalue: self.entrychange(width))
self.heightvalue.trace("w", lambda name, index, mode, height=self.heightvalue: self.entrychange(height))
然后我们做一些无聊的事情,绘制小部件,设置它们的 textvariable
,打包它们并设置默认值 self.lastupdated
:
self.width = Entry(self.root, textvariable=self.widthvalue)
self.height = Entry(self.root, textvariable=self.heightvalue)
self.width.pack()
self.height.pack()
self.lastupdated = None
在此之后,我们启动一个 .after()
调用。这允许您在 tkinter
:
self.root.after(10, self.update)
这将启动不断 运行 我们的 update
函数的过程。
更新函数首先检查 Entry
是否已更新:
if self.lastupdated != None:
然后它会检查哪个是最后更新的,并确保该字段不为空或不等于 0。这是因为空白值和 0 都会扰乱我们的数学计算(并且对于图片):
if self.lastupdated == "width" and self.widthvalue.get() != "" and self.widthvalue.get() != "0":
然后我们将相反的 Entry
小部件的值设置为双倍或一半(取决于更改了哪个 Entry
),更新 GUI 中的小部件并设置变量 self.lastupdated
恢复为默认值:
self.heightvalue.set(str(int(int(self.widthvalue.get())/self.aspect)))
self.height.update()
self.lastupdated = None
对于另一个 Entry
我们做的差不多。
就我个人而言,我会命名我的条目字段,并在编辑时检查焦点所在的条目。由于两个跟踪都被激活,我们可以使用焦点来告诉我们要编辑的字段。
我必须构建一个 try/except 语句来处理 get()
出于某种原因不从字段中提取值的奇怪行为。当我用我的打印语句进行测试时,它似乎得到了正确的数字,但可能是由于两次调用导致 get() 失败。
示例代码如下:
import tkinter as tk
import tkinter.ttk as ttk
class App(tk.Tk):
def __init__(self, aspect_ratio):
super().__init__()
self.aspect_ratio = aspect_ratio
self.previous_x = 0
self.previous_y = 0
self.shape_x = tk.DoubleVar(self)
self.shape_y = tk.DoubleVar(self)
self.shape_x.trace('w', self.update_x_y)
self.shape_y.trace('w', self.update_x_y)
ttk.Entry(self, name='x', textvariable=self.shape_x, width=5).pack(side='left', fill='x', expand='yes')
tk.Label(self, text='x', fg='gray').pack(side='left')
ttk.Entry(self, name='y', textvariable=self.shape_y, width=5).pack(side='left', fill='x', expand='yes')
def update_x_y(self, *args):
name = self.focus_get().winfo_name()
try:
x = self.shape_x.get()
y = self.shape_y.get()
if name == 'x':
self.previous_y = x * self.aspect_ratio
self.shape_y.set(self.previous_y)
elif name == 'y':
self.previous_x = y * self.aspect_ratio
self.shape_x.set(self.previous_x)
else:
print('Focus is not one of the 2 entry fields. Do nothing.')
except:
print('Handling odd error from the double call to the function returning empty string from get().')
if __name__ == '__main__':
App(aspect_ratio=2).mainloop()
可以说,最简单的解决方案是绑定 <KeyRelease>
事件,因为修改发生在 press 键上。这样,您就不需要使用 textvariable
属性的开销。
self.entry_x.bind("<Any-KeyRelease>", self.update_y)
self.entry_y.bind("<Any-KeyRelease>", self.update_x)