更新另一个输入小部件的基于输入小部件的内容

时间:2019-10-14 15:45:45

标签: python tkinter tkinter-entry

我的代码包含两个输入小部件,它们应该保存图像的高度和宽度。 当用户通过在两个小部件之一中输入数字来调整高度或宽度时,应该根据输入的值和固定的纵横比来调整另一个的值。

我的第一种方法是将更新功能绑定到按键事件,但是我遇到了导致在更改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中的*替换为/

3 个答案:

答案 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来存储heightEntry

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 ,我们大致相同。