web-dev-qa-db-fra.com

Comment obtenir un rappel d'événement lorsqu'un widget Tkinter Entry est modifié?

Exactement comme le dit la question. Les widgets Text ont l'événement <<Modified>>, mais les widgets Entry n'apparaissent pas.

37
bfops

Ajoutez un Tkinter StringVar à votre widget Entry. Liez votre rappel au StringVar en utilisant la méthode de 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()  
58
Steven Rumbalski

Au moment de la rédaction de ce document (2017, Python 3.6, tkinter version 8.6.6), la documentation suggère que .trace est obsolète. La forme suggérée semble maintenant être:

sv.trace_add("write", callback)

Cela fonctionne très bien si vous souhaitez recevoir une notification chaque fois que la variable est modifiée. Toutefois, mon application souhaite uniquement être notifiée lorsque l'utilisateur termine l'édition du texte. J'ai trouvé que le système de "validation" fonctionnait bien ici:

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()

Ceci invoquera callback chaque fois que le widget d'entrée perd son focus (j'ai ajouté un second widget d'entrée afin que le premier puisse réellement perdre le focus!)

14
Matthew Daws

Merci Steven! Tkinter Folklore, de Russell Owen, explique comment obtenir la valeur StringVar directement à partir de l'argument name (PY_VAR #) à l'aide de globalgetvar (), mais pas comment mapper le nom sur un widget. Votre méthode lambda pour changer les arguments de rappel est comme par magie (pour nous novices en Python, au moins).

Lorsqu'il y a plus d'une entrée, il est souvent nécessaire de connaître non seulement la valeur, mais l'entrée qui a été modifiée. En développant légèrement l’exemple de Steven, ce qui suit (Python3) transmet un index qui peut être utilisé pour garder trace de plusieurs entrées.

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() 
9
Dave

Pourquoi avez-vous besoin de savoir quand l'entrée est modifiée?

Vous avez probablement besoin d'un autre comportement:

  1. l'utilisateur entre le texte dans l'entrée
  2. l'utilisateur appuie sur la touche Entrée pour appliquer les modifications

Dans ce cas, vous pouvez simplement gérer la touche Entrée

self.entry.bind('<Return>', self.on_enter)

def on_enter(self, key_info):
    text = self.entry.get()
0
dimon4eg

Vous pouvez également utiliser l'événement KeyRelease qui sera déclenché chaque fois que l'utilisateur clique sur le widget. 
Que vous pouvez que filtrer pour les changements.

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()

J'espère que ça aide.

0
Avi ba

J'utilise Python 3.6 et je ne pouvais pas faire fonctionner .trace. Le code suivant permet d'accepter ou de modifier une valeur par défaut de StringVar. on_changed est appelé lorsque la touche de retour est enfoncée. 

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 __== '__main__':
    main()
0
Oppy

Je connais une autre variante.

avant de saisir le code, cela pourrait mieux expliquer le chemin du codage: lire ici

et il y a mon code:

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()

il y a 2 variantes: en appuyant sur le bouton et en entrant à l'entrée . maintenant vous pouvez sélectionner n'importe quelle variante

0
Konstantin Kozlenko