У меня есть приложение 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 в зависимости от того, какой из них имеет фокус.
Мне жаль, если вы обиделись. Я попытался спросить как можно вежливее ("пожалуйста, уменьшите..."). Спасибо за разъяснения. Если я правильно понимаю ваш комментарий, когда вы написали «введите IntVar», вы на самом деле имеете в виду «назначить IntVar записи». Это правильно? Я до сих пор не понимаю, какое это имеет отношение к фокусу. Почему вам нужна только переменная, связанная с выделенным виджетом, а не по одной для каждого виджета?
Может быть, я также принял ваш комментарий близко к сердцу (мне просто надоело, что люди здесь грубят, это заставляет людей не хотеть быть здесь), в любом случае, давайте двигаться вперед. На мой взгляд, мы входить в Entry() не назначаем ни одному. В любом случае, я хорошо разбираюсь в графическом интерфейсе на основе классов Gtk и пытаюсь изучить более сложные графические интерфейсы tkinter, и поэтому я нахожусь на том этапе, когда все еще не совсем понятно. По сути, мне не нужно делать это так, как я установил, я просто хочу иметь возможность использовать цифровые кнопки для ввода цифр в Entry(), который имеет фокус в любой момент, ..... [продолжение]
Поэтому, если я нажму на self.user_number_entry, то нажатие на кнопки введет эту цифру в self.user_number_entry, а затем, когда я введу номер пользователя и нажму на self.pin_number_entry, нажатие на кнопки введет цифру в self.pin_number_entry. Именно в тот момент, когда я перепробовал так много вариантов, что запутался и не могу уследить за тем, что я уже пробовал, я попробовал ответ Чака Джи, но, похоже, он не работает.
Да, пользователь вводит данные в поле ввода. Однако вы не «входите» в IntVar, что и вызвало у меня замешательство.
Давайте продолжить обсуждение в чате.





Вы можете добавить привязку к событию "<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, вместо того, чтобы просто говорить, что люди не правы, почему бы не помочь? Вы потратили время на комментарии о том, что я не прав, и о том, как неправ Чак Джи, но на самом деле вы не говорите ничего полезного! Как можно «спросить виджет, который в данный момент находится в фокусе»? Я пробовал что-то подобное среди нескольких вариантов, которые я пробовал в процессе, и get_focus() и set_focus() не работали, я видел, как вы отвечали на многие вопросы о tkinter, поэтому вы, безусловно, могли бы помочь, но вместо этого вы не предлагаете ничего полезного, я предлагаю вы присоединяетесь к interpersonal.meta.stackexchange.com
Мои извинения. Я не знал, что вы используете кнопки ttk, которые крадут фокус. Это очень раздражающее дизайнерское решение со стороны разработчиков ttk. Я удалил свой предыдущий комментарий.
@Chuck G спасибо за усилия, но ваше решение просто не сработало.
Если я правильно понимаю вопрос, все, что вы действительно спрашиваете, это то, как кнопка должна знать, куда вставить текст. Я не думаю, что использование 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), но это еще одна проблема для меня, чтобы попытаться решить, приветствия и извинения за любые недоразумения ранее.
Во-первых, вы первый (грубый) человек, который сказал, что блок коротких кодов слишком длинный/много, поэтому, пожалуйста, успокойтесь в своем тоне! Я не ценю этого, ты не высокий и могучий, потому что у тебя много репутации! Такие люди, как вы, известны в этом сообществе своей недружелюбностью. Теперь я понимаю, что мой код использует
self.user_number_var = IntVar()для инициализации целочисленной переменной и использует упомянутыйIntVar(), назначенный имениself.user_number_var, который устанавливает текст вself.user_number_entry.