2010-11-10 7 views
49

Каков рекомендуемый метод интерактивной проверки содержимого в виджере tkinter Entry?Интерактивная проверка содержимого виджета входа в tkinter

Я читал сообщения об использовании validate=True и validatecommand=command, и кажется, что эти возможности ограничены тем, что они получают очищенные, если команда validatecommand обновляет значение в Entry виджета.

Учитывая такое поведение, мы должны связать на KeyPress, Cut и Paste событий и монитор/обновить нашу ценность Entry виджета с помощью этих событий? (И другие связанные события, которые я, возможно, пропустил?)

Или мы должны полностью забыть об интерактивной проверке и только подтверждать на FocusOut событиях?

ответ

123

Правильный ответ: используйте атрибут validatecommand виджета. К сожалению, эта функция сильно недооценена в мире Tkinter, хотя она достаточно хорошо документирована в мире Tk. Несмотря на то, что он не задокументирован хорошо, он имеет все необходимое для проверки, не прибегая к привязкам или отслеживанию переменных, или изменяя виджет из процедуры проверки.

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

Примечание: важно, чтобы команда проверки возвращала либо True, либо False. Все остальное приведет к отключению проверки для виджета.

Вот пример, который позволяет только в нижнем регистре (и печатает все эти напуганные значения):

import tkinter as tk # python 3.x 
# import Tkinter as tk # python 2.x 

class Example(tk.Frame): 

    def __init__(self, parent): 
     tk.Frame.__init__(self, parent) 

     # valid percent substitutions (from the Tk entry man page) 
     # note: you only have to register the ones you need; this 
     # example registers them all for illustrative purposes 
     # 
     # %d = Type of action (1=insert, 0=delete, -1 for others) 
     # %i = index of char string to be inserted/deleted, or -1 
     # %P = value of the entry if the edit is allowed 
     # %s = value of entry prior to editing 
     # %S = the text string being inserted or deleted, if any 
     # %v = the type of validation that is currently set 
     # %V = the type of validation that triggered the callback 
     #  (key, focusin, focusout, forced) 
     # %W = the tk name of the widget 

     vcmd = (self.register(self.onValidate), 
       '%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W') 
     self.entry = tk.Entry(self, validate="key", validatecommand=vcmd) 
     self.text = tk.Text(self, height=10, width=40) 
     self.entry.pack(side="top", fill="x") 
     self.text.pack(side="bottom", fill="both", expand=True) 

    def onValidate(self, d, i, P, s, S, v, V, W): 
     self.text.delete("1.0", "end") 
     self.text.insert("end","OnValidate:\n") 
     self.text.insert("end","d='%s'\n" % d) 
     self.text.insert("end","i='%s'\n" % i) 
     self.text.insert("end","P='%s'\n" % P) 
     self.text.insert("end","s='%s'\n" % s) 
     self.text.insert("end","S='%s'\n" % S) 
     self.text.insert("end","v='%s'\n" % v) 
     self.text.insert("end","V='%s'\n" % V) 
     self.text.insert("end","W='%s'\n" % W) 

     # Disallow anything but lowercase letters 
     if S == S.lower(): 
      return True 
     else: 
      self.bell() 
      return False 

if __name__ == "__main__": 
    root = tk.Tk() 
    Example(root).pack(fill="both", expand=True) 
    root.mainloop() 
+8

Это правильный способ сделать это.Он рассматривает проблемы, которые я обнаружил, когда пытался получить ответ jmeyer10. Этот один пример обеспечивает превосходную документацию для проверки по сравнению с тем, что я могу найти в другом месте. Хотел бы я дать эти 5 голосов. –

+1

WOW! Я согласен с Стивеном - это тот тип ответа, который заслуживает более одного голоса. Вы должны написать книгу о Tkinter (и вы уже опубликовали достаточно решений, чтобы сделать многотомную серию). Спасибо!!! – Malcolm

+0

В качестве побочного примечания: Я взорван силой Ткинтера. В течение многих лет он получил плохую упаковку, но с новой поддержкой родных тем (ttk) и объяснением своих скрытых полномочий силами таких экспертов, как Брайан Оукли, эта инфраструктура графического интерфейса может иметь свои собственные против подобных wxPython, pyQT и других , – Malcolm

7

Используйте Tkinter.StringVar для отслеживания значения виджета ввода. Вы можете проверить значение StringVar, установив на нем trace.

Вот короткая рабочая программа, которая принимает только действительные поплавки в виджетах ввода.

from Tkinter import * 
root = Tk() 
sv = StringVar() 

def validate_float(var): 
    new_value = var.get() 
    try: 
     new_value == '' or float(new_value) 
     validate.old_value = new_value 
    except: 
     var.set(validate.old_value)  
validate.old_value = '' 

# trace wants a callback with nearly useless parameters, fixing with lambda. 
sv.trace('w', lambda nm, idx, mode, var=sv: validate_float(var)) 
ent = Entry(root, textvariable=sv) 
ent.pack() 

root.mainloop() 
+0

Спасибо за сообщение. Мне понравилось видеть используемый Tkinter StringVar .trace(). – Malcolm

4

После изучения и экспериментирования с кодом Брайана, я произвел минимальную версию проверки входных данных. Следующий код помещает поле ввода и принимает только числовые цифры.

from tkinter import * 

root = Tk() 

def testVal(inStr,i,acttyp): 
    ind=int(i) 
    if acttyp == '1': #insert 
     if not inStr[ind].isdigit(): 
      return False 
    return True 

entry = Entry(root, validate="key") 
entry['validatecommand'] = (entry.register(testVal),'%P','%i','%d') 
entry.pack() 

root.mainloop() 

Возможно, я должен добавить, что я все еще изучаю Python, и я с радостью принимаю любые комментарии и предложения.

0

Изучая Bryan Oakley's answer, мне сказали, что может быть разработано гораздо более общее решение. В следующем примере представлены перечисление режимов, словарь типов и функция настройки для целей проверки. См. Строку 48, например, использование и демонстрацию ее простоты.

#! /usr/bin/env python3 
# https://stackoverflow.com/questions/4140437 
import enum 
import inspect 
import tkinter 
from tkinter.constants import * 


Mode = enum.Enum('Mode', 'none key focus focusin focusout all') 
CAST = dict(d=int, i=int, P=str, s=str, S=str, 
      v=Mode.__getitem__, V=Mode.__getitem__, W=str) 


def on_validate(widget, mode, validator): 
    # http://www.tcl.tk/man/tcl/TkCmd/ttk_entry.htm#M39 
    if mode not in Mode: 
     raise ValueError('mode not recognized') 
    parameters = inspect.signature(validator).parameters 
    if not set(parameters).issubset(CAST): 
     raise ValueError('validator arguments not recognized') 
    casts = tuple(map(CAST.__getitem__, parameters)) 
    widget.configure(validate=mode.name, validatecommand=[widget.register(
     lambda *args: bool(validator(*(cast(arg) for cast, arg in zip(
      casts, args)))))]+['%' + parameter for parameter in parameters]) 


class Example(tkinter.Frame): 

    @classmethod 
    def main(cls): 
     tkinter.NoDefaultRoot() 
     root = tkinter.Tk() 
     root.title('Validation Example') 
     cls(root).grid(sticky=NSEW) 
     root.grid_rowconfigure(0, weight=1) 
     root.grid_columnconfigure(0, weight=1) 
     root.mainloop() 

    def __init__(self, master, **kw): 
     super().__init__(master, **kw) 
     self.entry = tkinter.Entry(self) 
     self.text = tkinter.Text(self, height=15, width=50, 
           wrap=WORD, state=DISABLED) 
     self.entry.grid(row=0, column=0, sticky=NSEW) 
     self.text.grid(row=1, column=0, sticky=NSEW) 
     self.grid_rowconfigure(1, weight=1) 
     self.grid_columnconfigure(0, weight=1) 
     on_validate(self.entry, Mode.key, self.validator) 

    def validator(self, d, i, P, s, S, v, V, W): 
     self.text['state'] = NORMAL 
     self.text.delete(1.0, END) 
     self.text.insert(END, 'd = {!r}\ni = {!r}\nP = {!r}\ns = {!r}\n' 
           'S = {!r}\nv = {!r}\nV = {!r}\nW = {!r}' 
         .format(d, i, P, s, S, v, V, W)) 
     self.text['state'] = DISABLED 
     return not S.isupper() 


if __name__ == '__main__': 
    Example.main() 
Смежные вопросы