我的代码包含两个输入小部件,它们应该保存图像的高度和宽度。 当用户通过在两个小部件之一中输入数字来调整高度或宽度时,应该根据输入的值和固定的纵横比来调整另一个的值。
我的第一种方法是将更新功能绑定到按键事件,但是我遇到了导致在更改textvariable之前执行绑定功能的错误。问题在这里描述: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中的*替换为/
答案 0 :(得分:2)
可以说,最简单的解决方案是绑定<KeyRelease>
事件,因为修改发生在按键 press 上。这样,您就不需要使用textvariable
属性了。
self.entry_x.bind("<Any-KeyRelease>", self.update_y)
self.entry_y.bind("<Any-KeyRelease>", self.update_x)
答案 1 :(得分:0)
我个人将输入字段名称,然后只检查编辑时焦点所在的条目。由于两条迹线都被激活,因此我们可以使用焦点来告诉我们要编辑哪个字段。
我必须构建一个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()
答案 2 :(得分:-1)
所以,我要解决的方法是拥有一个值存储,该值存储上次更新了Entry
个小部件,然后有一个“检查器”功能,它以很小的间隔检查是否有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中更新小部件并设置变量{{1} }还原为默认值:
self.lastupdated
对于其他self.heightvalue.set(str(int(int(self.widthvalue.get())/self.aspect)))
self.height.update()
self.lastupdated = None
,我们大致相同。