В интерактивном режиме проверки записи содержимого виджета в tkinter


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

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

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

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

4 56

4 ответа:

правильный ответ, используйте

использовать 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()

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

from tkinter import *

root = Tk()

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

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

root.mainloop()

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

во время учебы ответ Брайана Оукли, что-то подсказало мне, что можно разработать гораздо более общее решение. В следующем примере представлены перечисление режимов, словарь типов и функция настройки для целей проверки. См. строку 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()