Как получить обратный вызов события при изменении виджета Tkinter Entry? - PullRequest
41 голосов
/ 01 июля 2011

Точно так, как говорит вопрос. Text виджеты имеют событие <<Modified>>, но Entry виджеты не отображаются.

Ответы [ 6 ]

64 голосов
/ 01 июля 2011

Добавьте Tkinter StringVar в ваш виджет Entry. Свяжите свой обратный вызов со StringVar, используя метод trace.

from Tkinter import *

def callback(sv):
    print sv.get()

root = Tk()
sv = StringVar()
sv.trace("w", lambda name, index, mode, sv=sv: callback(sv))
e = Entry(root, textvariable=sv)
e.pack()
root.mainloop()  
18 голосов
/ 05 июня 2017

На момент написания (2017, Python 3.6, tkinter версия 8.6.6) документы предполагают, что .trace устарела. Предлагаемая форма теперь выглядит так:

sv.trace_add("write", callback)

Это работает очень хорошо, если вы хотите получать уведомления при каждом изменении переменной. Тем не менее, мое приложение просто хочет уведомления, когда он заканчивает редактирование текста. Я обнаружил, что механизм «проверки» хорошо работает здесь:

from tkinter import *

root = Tk()
sv = StringVar()

def callback():
    print(sv.get())
    return True

e = Entry(root, textvariable=sv, validate="focusout", validatecommand=callback)
e.grid()
e = Entry(root)
e.grid()
root.mainloop()

Это вызовет callback всякий раз, когда виджет ввода теряет фокус (я добавил второй виджет записи, чтобы первый мог фактически потерять фокус!)

9 голосов
/ 10 октября 2011

Спасибо, Стивен!Tkinter Folklore Рассела Оуэна объясняет, как получить значение StringVar непосредственно из аргумента имени (PY_VAR #) с помощью globalgetvar (), но не как сопоставить имя с виджетом.Ваш лямбда-метод изменения аргументов обратного вызова подобен магии (по крайней мере для нас, новичков в Python).

Когда существует более одной записи, часто необходимо знать не только значение, но и какую записьизменилось.Немного расширив пример Стивена, следующий (Python3) передает индекс, который можно использовать для отслеживания нескольких записей.

from tkinter import Tk, Frame, Label, Entry, StringVar

class Fruitlist:
    def entryupdate(self, sv, i):
        print(sv, i, self.fruit[i], sv.get())

    def __init__(self, root):
        cf = Frame(root)
        cf.pack()
        self.string_vars = []
        self.fruit = ("Apple", "Banana", "Cherry", "Date")
        for f in self.fruit:
            i = len(self.string_vars)
            self.string_vars.append(StringVar())
            self.string_vars[i].trace("w", lambda name, index, mode, var=self.string_vars[i], i=i:
                              self.entryupdate(var, i))
            Label(cf, text=f).grid(column=2, row=i)
            Entry(cf, width=6, textvariable=self.string_vars[i]).grid(column=4, row=i)

root = Tk()
root.title("EntryUpdate")
app = Fruitlist(root)
root.mainloop() 
0 голосов
/ 18 июля 2018

Вы также можете использовать событие KeyRelease, которое будет запускаться каждый раз, когда пользователь нажимает на виджет.
Чем вы можете фильтровать изменения.

from tkinter import * 
from tkinter import ttk

class GUI():                              
    def __init__(self):  
        self.root = Tk()
        self.sv = StringVar() 
        self.prevlaue=''
        #entry
        self.entry = ttk.Entry(self.root, width=30, textvariable =self.sv)
        self.entry.grid(pady=20,padx=20) 
        self.entry.bind("<KeyRelease>", self.OnEntryClick) #keyup                  
        self.root.mainloop()       

    def OnEntryClick(self, event):
        value=self.sv.get().strip()
        changed = True if self.prevlaue != value else False
        print(value, 'Text has changed ? {}'.format(changed))
        self.prevlaue = value

#create the gui
GUI()

Надеюсь, это поможет.

0 голосов
/ 04 апреля 2018

Я использую Python 3.6 и не смог получить .trace для работы. Следующий код позволяет принимать или редактировать значение по умолчанию StringVar. on_changed вызывается при нажатии клавиши возврата.

from tkinter import Tk, LEFT, BOTH, StringVar
from tkinter.ttk import Entry, Frame


class Example(Frame):
    def __init__(self, parent):
        Frame.__init__(self, parent)
        self.parent = parent
        self.initUI()

    def initUI(self):
        self.parent.title("Entry")
        self.pack(fill=BOTH, expand=1)
        self.contents = StringVar()
        # give the StringVar a default value
        self.contents.set('test')
        self.entry = Entry(self)
        self.entry.pack(side=LEFT, padx=15)
        self.entry["textvariable"] = self.contents
        self.entry.bind('<Key-Return>', self.on_changed)

    def on_changed(self, event):
        print('contents: {}'.format(self.contents.get()))
        return True


def main():
    root = Tk()
    ex = Example(root)
    root.geometry("250x100+300+300")
    root.mainloop()


if __name__ == '__main__':
    main()
0 голосов
/ 08 февраля 2018

Я знаю другой вариант.

прежде чем я введу код, лучше объяснить путь кодирования: прочитайте здесь

и есть мой код:

from Tkinter import *

class ttt:
   def __init__(self):
      self.str1 = StringVar()
      self.e1 = Entry(root, textvariable=self.str1)
      self.str1.trace('w', self.callback_1)
      self.e1.pack()

      self.str2 = StringVar()
      self.e2 = Entry(root, textvariable=self.str2, state='readonly')
      self.e2.pack()

      self.str3 = StringVar()
      self.e3 = Entry(root, textvariable=self.str3, state='readonly')
      self.e3.pack()

      bt = Button(root, text = 'ещё', command = self.callback_2)
      bt.pack()

   def callback_1(self, name='', index='', mode=''):
      tmp = self.str1.get()
      if tmp:
         self.str2.set(int(tmp) * 6)
         print self.str2.get()

   def callback_2(self, name='', index='', mode=''):
      tmp = self.str1.get()
      if tmp:
         self.str3.set(int(tmp) * 6)
         print self.str3.get()   

root = Tk()
t = ttt()
root.mainloop()

есть 2 варианта: нажмите кнопку и войдите в запись. теперь вы можете выбрать любой вариант

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...