Применить IntVar() к сфокусированному Entry() tkinter

У меня есть приложение 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_entry или self.pin_number_entry в зависимости от того, какой из них имеет фокус.

Во-первых, вы первый (грубый) человек, который сказал, что блок коротких кодов слишком длинный/много, поэтому, пожалуйста, успокойтесь в своем тоне! Я не ценю этого, ты не высокий и могучий, потому что у тебя много репутации! Такие люди, как вы, известны в этом сообществе своей недружелюбностью. Теперь я понимаю, что мой код использует self.user_number_var = IntVar() для инициализации целочисленной переменной и использует упомянутый IntVar(), назначенный имени self.user_number_var, который устанавливает текст в self.user_number_entry.

Jamie Lindsey 30.01.2019 16:34

Мне жаль, если вы обиделись. Я попытался спросить как можно вежливее ("пожалуйста, уменьшите..."). Спасибо за разъяснения. Если я правильно понимаю ваш комментарий, когда вы написали «введите IntVar», вы на самом деле имеете в виду «назначить IntVar записи». Это правильно? Я до сих пор не понимаю, какое это имеет отношение к фокусу. Почему вам нужна только переменная, связанная с выделенным виджетом, а не по одной для каждого виджета?

Bryan Oakley 30.01.2019 17:17

Может быть, я также принял ваш комментарий близко к сердцу (мне просто надоело, что люди здесь грубят, это заставляет людей не хотеть быть здесь), в любом случае, давайте двигаться вперед. На мой взгляд, мы входить в Entry() не назначаем ни одному. В любом случае, я хорошо разбираюсь в графическом интерфейсе на основе классов Gtk и пытаюсь изучить более сложные графические интерфейсы tkinter, и поэтому я нахожусь на том этапе, когда все еще не совсем понятно. По сути, мне не нужно делать это так, как я установил, я просто хочу иметь возможность использовать цифровые кнопки для ввода цифр в Entry(), который имеет фокус в любой момент, ..... [продолжение]

Jamie Lindsey 30.01.2019 17:27

Поэтому, если я нажму на self.user_number_entry, то нажатие на кнопки введет эту цифру в self.user_number_entry, а затем, когда я введу номер пользователя и нажму на self.pin_number_entry, нажатие на кнопки введет цифру в self.pin_number_entry. Именно в тот момент, когда я перепробовал так много вариантов, что запутался и не могу уследить за тем, что я уже пробовал, я попробовал ответ Чака Джи, но, похоже, он не работает.

Jamie Lindsey 30.01.2019 17:30

Да, пользователь вводит данные в поле ввода. Однако вы не «входите» в IntVar, что и вызвало у меня замешательство.

Bryan Oakley 30.01.2019 17:31

Давайте продолжить обсуждение в чате.

Jamie Lindsey 30.01.2019 17:31
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
391
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете добавить привязку к событию "<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, если пользователь изменит фокус записи, и я думаю, что это нежелательное поведение.

Я играл с функцией привязки, но не смог заставить ее работать должным образом, поэтому подумал, что, возможно, это неправильный подход. Я проверю ваш пример, как только буду за компьютером. Очень признателен.

Jamie Lindsey 30.01.2019 11:22

@BryanOakley, вместо того, чтобы просто говорить, что люди не правы, почему бы не помочь? Вы потратили время на комментарии о том, что я не прав, и о том, как неправ Чак Джи, но на самом деле вы не говорите ничего полезного! Как можно «спросить виджет, который в данный момент находится в фокусе»? Я пробовал что-то подобное среди нескольких вариантов, которые я пробовал в процессе, и get_focus() и set_focus() не работали, я видел, как вы отвечали на многие вопросы о tkinter, поэтому вы, безусловно, могли бы помочь, но вместо этого вы не предлагаете ничего полезного, я предлагаю вы присоединяетесь к interpersonal.meta.stackexchange.com

Jamie Lindsey 30.01.2019 17:02

Мои извинения. Я не знал, что вы используете кнопки ttk, которые крадут фокус. Это очень раздражающее дизайнерское решение со стороны разработчиков ttk. Я удалил свой предыдущий комментарий.

Bryan Oakley 30.01.2019 17:27

@Chuck G спасибо за усилия, но ваше решение просто не сработало.

Jamie Lindsey 30.01.2019 18:05
Ответ принят как подходящий

Если я правильно понимаю вопрос, все, что вы действительно спрашиваете, это то, как кнопка должна знать, куда вставить текст. Я не думаю, что использование IntVar имеет какое-то отношение к этому, это ху проблема. Вы можете использовать IntVar, если хотите, но они не имеют отношения к этой проблеме.

Tkinter предоставляет команду для получения текущего виджета — focus_get. Когда вы нажимаете кнопку, вы можете вставить текст в конец этого виджета. Вам не нужно использовать IntVar.

тем не мение, вы используете кнопки 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] (с фокусом на минимум), так как нам трудно сосредоточиться только на заданном вопросе. Другой код затуманивает проблему.

Короткое исправление, чтобы вы знали, что это решение работает, — временно отключить проверку ввода.

Кроме того, это решение предполагает, что вы щелкнули одну из записей до того, как нажали кнопку. Вы можете обойти это, вызвав self.user_number_entry.focus_set() в своем __init__, что заставит фокус ввода начать с этого виджета ввода.

Идеально! Спасибо, я не слышал о проблеме xy до сегодняшнего дня, и это не может быть более точным. Поскольку я новичок в tkinter, пришедшем из Gtk, многие концепции tkinter кажутся странными, я все еще нахожусь на ранней стадии открытия . Ваш ответ облегчил мой текущий вопрос, и за это я благодарен, что странно, так это то, что проверка ввода работала нормально, когда я только делал self.user_number_var.set(expression), но это еще одна проблема для меня, чтобы попытаться решить, приветствия и извинения за любые недоразумения ранее.

Jamie Lindsey 30.01.2019 18:04

Другие вопросы по теме