【问题标题】:Apply IntVar() to focused Entry() tkinter将 IntVar() 应用于重点 Entry() tkinter
【发布时间】:2019-06-23 11:58:34
【问题描述】:

我有一个 tkinter 应用程序,它有 2 个输入框和数字 0-9 的按钮。我希望能够使用按钮输入IntVar(),但可以输入焦点Entry()。目前我可以按下按钮将IntVar() 输入到第一个Entry() 框中,但我无法弄清楚或在网上找到如何仅将IntVar() 输入到焦点Entry() 框中。

import sys
from tkinter import Tk, Text, BOTH, W, N, E, S, IntVar
from tkinter.ttk import Frame, Button, Label, Style, Entry

expression = ''

class Example(Frame):

    def __init__(self, *args, **kwargs):
        Frame.__init__(self, *args, **kwargs)

        self.name = 'Application'
        self.master.title(self.name)
        self.pack(fill=BOTH, expand=True)

        cancel_btn_style = Style()
        cancel_btn_style.configure('CNL.TButton', background='red')
        cancel_btn_style = Style()
        cancel_btn_style.configure('LGN.TButton', background='green')

        self.columnconfigure(1, weight=1)
        self.columnconfigure(2, weight=1)
        self.columnconfigure(3, weight=1)
        self.rowconfigure(0, weight=1)
        self.rowconfigure(1, weight=1)
        self.rowconfigure(2, weight=1)
        self.rowconfigure(3, weight=1)
        self.rowconfigure(4, weight=1)
        self.rowconfigure(5, weight=1)

        self.user_number_var = IntVar()

        self.main_label = Label(self, text=self.name)
        self.main_label.grid(sticky=W, pady=4, padx=5)

        self.entry_label = Label(self, text='User Number / PIN')
        self.entry_label.grid(
            row=1, column=0, columnspan=1,
            rowspan=1, padx=5, sticky=E+W+S+N
        )

        vcmd = (self.register(self.__vcmd), '%P')

        self.user_number_entry = Entry(
            self, validate='key', validatecommand=vcmd, 
            textvariable=self.user_number_var
        )
        self.user_number_entry.grid(
            row=1, column=1, columnspan=1, 
            rowspan=1, sticky=N+S+E+W, pady=4
        )

        self.pin_number_entry = Entry(self, validate='key', validatecommand=vcmd, show='*')
        self.pin_number_entry.grid(
            row=1, column=2, columnspan=1, 
            rowspan=1, sticky=N+S+E+W, pady=4
        )

        self.btn_1 = Button(self, text='1', command=lambda: self.press(1))
        self.btn_1.grid(row=2, column=0, columnspan=1, rowspan=1, sticky=N+S+E+W)

        self.btn_2 = Button(self, text='2', command=lambda: self.press(2))
        self.btn_2.grid(row=2, column=1, columnspan=1, rowspan=1, sticky=N+S+E+W)

        self.btn_3 = Button(self, text='3', command=lambda: self.press(3))
        self.btn_3.grid(row=2, column=2, columnspan=1, rowspan=1, sticky=N+S+E+W)

        self.btn_4 = Button(self, text='4', command=lambda: self.press(4))
        self.btn_4.grid(row=3, column=0, columnspan=1, rowspan=1, sticky=N+S+E+W)

        self.btn_5 = Button(self, text='5', command=lambda: self.press(5))
        self.btn_5.grid(row=3, column=1, columnspan=1, rowspan=1, sticky=N+S+E+W)

        self.btn_6 = Button(self, text='6', command=lambda: self.press(6))
        self.btn_6.grid(row=3, column=2, columnspan=1, rowspan=1, sticky=N+S+E+W)

        self.btn_7 = Button(self, text='7', command=lambda: self.press(7))
        self.btn_7.grid(row=4, column=0, columnspan=1, rowspan=1, sticky=N+S+E+W)

        self.btn_8 = Button(self, text='8', command=lambda: self.press(8))
        self.btn_8.grid(row=4, column=1, columnspan=1, rowspan=1, sticky=N+S+E+W)

        self.btn_9 = Button(self, text='9', command=lambda: self.press(9))
        self.btn_9.grid(row=4, column=2, columnspan=1, rowspan=1, sticky=N+S+E+W)

        self.cancel_btn = Button(self, text='Cancel / Close', command=sys.exit, style='CNL.TButton')
        self.cancel_btn.grid(row=5, column=0, columnspan=1, rowspan=1, sticky=N+S+E+W)

        self.btn_0 = Button(self, text='0', command=lambda: self.press(0))
        self.btn_0.grid(row=5, column=1, columnspan=1, rowspan=1, sticky=N+S+E+W)

        self.login_btn = Button(self, text='Log In', style='LGN.TButton')
        self.login_btn.grid(row=5, column=2, columnspan=1, rowspan=1, sticky=N+S+E+W)

    def press(self, num):
        # point out the global expression variable
        global expression

        # concatenation of string
        expression = expression + str(num)

        # update the expression by using set method
        self.user_number_var.set(expression)

    def __vcmd(self, P, S):
        print('__vcmd')

def main():

    root = Tk()
    root.geometry('540x640')
    app = Example(root)
    root.mainloop()


if __name__ == '__main__':
    main()

我需要按钮来输入数字到self.user_number_entryself.pin_number_entry,这取决于哪个有焦点。

【问题讨论】:

  • 首先是你第一个(粗鲁)说短代码块太长/太多的人,所以请冷静一下!我不欣赏它,您的声望并不高,因为您有很多声望!这个社区以不友好而闻名,像你这样的人。现在我的理解是,我的代码使用self.user_number_var = IntVar() 来初始化一个整数变量,并使用IntVar() 分配给self.user_number_var 的名称,从而将文本设置在self.user_number_entry 中。
  • 如果你冒犯了我很抱歉。我尽量礼貌地问(“请减少...”)。感谢您的澄清。如果我理解你的评论是正确的,当你写“输入一个 IntVar”时,你实际上是指“将一个 IntVar 分配给一个条目”。那是对的吗?不过,我仍然不明白这与专注有什么关系。为什么你只想要与焦点小部件关联的变量,而不是每个小部件一个?
  • 也许我也把你的评论放在心上(我只是厌倦了人们在这里粗鲁,这让人不想待在这里)无论如何让我们继续前进。在我看来,我们输入Entry(),而不是分配给一个。无论如何,我精通基于 Gtk 类的 GUI,并试图学习更复杂的 tkinter GUI,所以我正处于那个事情还不太有意义的阶段。基本上我不必按照我已经建立的方式这样做,我只想能够使用编号按钮将数字输入到Entry() 中,在任何给定时刻都有焦点,.....[继续]
  • 所以如果我点击self.user_number_entry然后点击按钮将输入该数字到self.user_number_entry然后当我输入用户号码并点击self.pin_number_entry点击按钮将进入将数字转换为self.pin_number_entry。正是在我尝试了如此多的变化时,我变得困惑并且无法跟踪我已经尝试过的内容,我尝试了 Chuck G 的答案,但它似乎不起作用。
  • 是的,用户在输入字段中输入数据。但是,您不会“输入”我的困惑所在的 IntVar。

标签: tkinter python-2.x


【解决方案1】:

您可以将绑定添加到"<FocusIn>" 事件以检查哪些条目是焦点和insert 您的值:

....

def __init__(self, *args, **kwargs):
    ....
    self.user_number_var = IntVar()
    self.pin_number_var = IntVar()
    self.user_number_entry = Entry(
        self, validate='key', validatecommand=vcmd, 
        textvariable=self.user_number_var
    )
    self.user_number_entry.grid(
        row=1, column=1, columnspan=1, 
        rowspan=1, sticky=N+S+E+W, pady=4
    )
    self.user_number_entry.bind("<FocusIn>", self.remember_focus)

    self.pin_number_entry = Entry(self, textvariable=self.pin_number_var, validate='key', validatecommand=vcmd, show='*')
    self.pin_number_entry.grid(
        row=1, column=2, columnspan=1, 
        rowspan=1, sticky=N+S+E+W, pady=4
    )
    self.pin_number_entry.bind("<FocusIn>", self.remember_focus)
    ....

def remember_focus(self,event):
    global focused_entry
    focused_entry = event.widget    

def press(self, num):
    focused_entry.insert('end',str(num))

如果您坚持使用IntVar(),另一种解决方案是将bind 单独的函数分配给每个entry,然后将global variable 设置为焦点的IntVar(),这样您就可以将表达式输入到焦点条目的IntVar()

...
def __init__(self, *args, **kwargs):
    self.user_number_var = IntVar()
    self.pin_number_var = IntVar()

    self.main_label = Label(self, text=self.name)
    self.main_label.grid(sticky=W, pady=4, padx=5)

    self.entry_label = Label(self, text='User Number / PIN')
    self.entry_label.grid(
        row=1, column=0, columnspan=1,
        rowspan=1, padx=5, sticky=E+W+S+N
    )

    self.user_number_entry = Entry(
        self, validate='key', validatecommand=vcmd, 
        textvariable=self.user_number_var
    )
    self.user_number_entry.grid(
        row=1, column=1, columnspan=1, 
        rowspan=1, sticky=N+S+E+W, pady=4
    )
    self.user_number_entry.bind("<FocusIn>", self.set_user_number_int_var)

    self.pin_number_entry = Entry(self, textvariable=self.pin_number_var, validate='key', validatecommand=vcmd, show='*')
    self.pin_number_entry.grid(
        row=1, column=2, columnspan=1, 
        rowspan=1, sticky=N+S+E+W, pady=4
    )
    self.pin_number_entry.bind("<FocusIn>", self.set_pin_number_int_var)
...

def set_user_number_int_var(self,event):
    global set_int_var
    set_int_var = self.user_number_var

def set_pin_number_int_var(self,event):
    global set_int_var
    set_int_var = self.pin_number_var        


def press(self, num):
    # point out the global expression variable
    global expression,set_int_var

    # concatenation of string
    expression = expression + str(num)
    set_int_var.set(expression)

一个缺点:串联:

 expression = expression + str(num)

如果用户更改条目的焦点,这将继承 expression 中已有的任何内容,我猜这不是理想的行为。

【讨论】:

  • 我正在使用绑定函数,但无法使其按预期工作,所以我认为这可能是错误的方法。我会在我的电脑前测试你的例子。非常感谢。
  • @BryanOakley,与其说人们错了,为什么不实际提供一些帮助呢?你花了很多时间评论我错了以及查克 G 有多么错误,但你实际上并没有说任何有用的东西!如何“向它询问当前具有焦点的小部件”?我在此过程中尝试的几种变体中尝试了类似的方法,get_focus()set_focus() 不起作用,我看到你在 tkinter 上回答了很多问题,所以你当然可以提供帮助,但你什么也没提供有用,建议你加入interpersonal.meta.stackexchange.com
  • 我很抱歉。我没有意识到您正在使用 ttk 按钮来窃取焦点。对于 ttk 开发人员来说,这是一个非常烦人的设计决定。我删除了我之前的评论。
  • @Chuck G 感谢您的努力,但您的解决方案根本不起作用。
【解决方案2】:

如果我对问题的理解正确,那么您真正要问的是按钮应该如何知道在哪里插入文本。我不认为IntVar 的使用与它有任何关系,那是xy problem。如果您愿意,可以使用IntVars,但他们与此问题无关。

Tkinter 提供了一个命令来获取当前聚焦的小部件——focus_get。单击按钮时,可以将文本插入到该小部件的末尾。你不需要使用IntVar

然而,您正在使用 ttk 按钮,这会窃取焦点。 ttk 开发人员的一个糟糕的设计决定,但这不是重点。最简单的解决方案是跟踪哪个条目小部件最后获得焦点。然后,您可以直接在条目小部件中附加数字。

首先在每个条目小部件上添加一个绑定,以便在获得焦点时记住它自己。在创建条目之后的某个时间,以创建条目的相同方法执行此操作:

self.user_number_entry.bind("<FocusIn>", self._remember_focus)
self.pin_number_entry.bind("<FocusIn>", self._remember_focus)

接下来,我们需要定义_remember_focus 方法来记住哪个小部件最后获得了焦点:

def _remember_focus(self, event):
    self._current_entry = event.widget

最后,我们需要修改press 命令,将数字直接附加到条目小部件中。在此示例中,我还将焦点重置为条目小部件。我不确定你是否想要。

def press(self, num):
    self._current_entry.insert("end", str(num))
    self._current_entry.focus_set()

注意:您的代码中还有其他错误会阻止此操作。具体来说,您的条目验证代码中有错误。这就是我们要求 [mcve] 的原因(重点是 minimum),因为这让我们很难只专注于所提出的问题。其他代码掩盖了这个问题。

让您知道此解决方案有效的一个简短解决方法是暂时关闭您的输入验证。

此外,此解决方案假定您在单击按钮之前已单击其中一个条目。您可以通过在 __init__ 中调用 self.user_number_entry.focus_set() 来解决此问题,这将强制输入焦点从该条目小部件开始。

【讨论】:

  • 完美!谢谢,我今天之前没有听说过xy问题,而且不能更准确,因为我是来自Gtk的tkinter新手,很多tkinter概念看起来很奇怪,我还处于早期发现阶段.你的回答解决了我当前的问题,对此我很感激,奇怪的是,当我只做self.user_number_var.set(expression) 时,输入验证工作得很好,但这是我尝试解决的另一个问题,欢呼,并对之前的任何误解表示歉意.
猜你喜欢
  • 2016-08-24
  • 2022-01-14
  • 1970-01-01
  • 2014-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多