【问题标题】:Update entry widget based contents of another entry widget更新另一个条目小部件的基于条目小部件的内容
【发布时间】:2020-02-11 06:34:37
【问题描述】:

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

我的第一种方法是将更新函数绑定到按键事件,但我遇到了导致绑定函数在文本变量更改之前执行的错误。此处描述了该问题: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 中的 * 替换为 /

【问题讨论】:

  • 所以这个设置的问题是这两个函数都会被调用。因为当调用一个或另一个时,您正在编辑另一个。这也会触发另一个呼叫。

标签: python tkinter tkinter-entry


【解决方案1】:

可以说,最简单的解决方案是绑定&lt;KeyRelease&gt; 事件,因为修改发生在一个键press上。这样,您就不需要使用 textvariable 属性的开销了。

self.entry_x.bind("<Any-KeyRelease>", self.update_y)
self.entry_y.bind("<Any-KeyRelease>", self.update_x)

【讨论】:

  • 这可能是最好的解决方案。我的回答只是尝试处理 track 问题的错误,而不是像这样简单地解决问题。
【解决方案2】:

就我个人而言,我会命名我的条目字段,并检查在编辑时哪些条目具有焦点。由于两条轨迹都被激活,我们可以使用焦点告诉我们要编辑的字段。

我必须构建一个 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()

【讨论】:

    【解决方案3】:

    所以,我解决这个问题的方法是拥有一个上次更新 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 的变量,它存储widthheight,具体取决于上次更新的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,我们或多或少做同样的事情。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-12-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多