[python] tkinter에서 대화식으로 항목 위젯 컨텐츠 유효성 검증

tkinter Entry위젯 에서 대화식으로 콘텐츠를 검증하는 데 권장되는 기술은 무엇입니까 ?

validate=True및 사용에 대한 게시물을 읽었으며 validatecommand=command이러한 기능은 validatecommand명령이 Entry위젯의 값을 업데이트하면 지워진다는 사실로 인해 제한되는 것으로 보입니다 .

이 동작을 감안할 때, 우리는에 결합한다 KeyPress, CutPaste이벤트 모니터 / 업데이트 우리의 Entry이러한 이벤트를 통해 위젯의 가치인가? (그리고 내가 놓친 기타 관련 이벤트?)

아니면 대화 형 유효성 검사를 완전히 잊어 버리고 FocusOut이벤트에 대해서만 유효성을 검사해야 합니까?



답변

정답은 validatecommand위젯 의 속성을 사용하는 것입니다. 안타깝게도이 기능은 Tkinter 세계에서는 충분히 문서화되어 있지 않지만 Tk 세계에서는 충분히 문서화되어 있습니다. 잘 문서화되어 있지는 않지만 바인딩이나 추적 변수에 의존하지 않고 유효성 검사 절차 내에서 위젯을 수정하지 않고도 유효성 검사를 수행하는 데 필요한 모든 것이 있습니다.

트릭은 Tkinter가 특정 값을 validate 명령에 전달할 수 있다는 것을 아는 것입니다. 이러한 값은 데이터가 유효한지 여부를 결정하는 데 필요한 모든 정보를 제공합니다. 편집 전 값, 편집이 유효한 경우 편집 후 값 및 기타 여러 정보를 제공합니다. 그러나이를 사용하려면이 정보를 validate 명령에 전달하기 위해 약간의 부두를해야합니다.

참고 : 유효성 검사 명령이 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()

register메서드 를 호출 할 때 내부에서 발생하는 작업에 대한 자세한 내용 은 입력 유효성 검사 tkinter를 참조하세요.


답변

Bryan의 코드를 연구하고 실험 한 후 최소한의 입력 유효성 검사 버전을 생성했습니다. 다음 코드는 입력 상자를 표시하고 숫자 만 허용합니다.

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

아마도 나는 아직 파이썬을 배우고 있으며 모든 의견 / 제안을 기꺼이 받아 들일 것이라고 덧붙여 야 할 것입니다.


답변

Tkinter.StringVar항목 위젯의 값을 추적 하려면을 사용하십시오 . 에 StringVara trace를 설정하여 의 값을 확인할 수 있습니다 .

다음은 Entry 위젯에서 유효한 수레 만 허용하는 짧은 작업 프로그램입니다.

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


답변

Bryan Oakley의 답변을 연구하는 동안 훨씬 더 일반적인 솔루션을 개발할 수 있다는 내용이 나에게 알려졌습니다. 다음 예제에서는 유효성 검사를위한 모드 열거, 유형 사전 및 설정 함수를 소개합니다. 사용 예와 단순성에 대한 데모는 48 행을 참조하십시오.

#! /usr/bin/env python3
# /programming/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()


답변

Bryan의 대답은 맞지만 아무도 tkinter 위젯의 ‘invalidcommand’속성을 언급하지 않았습니다.

좋은 설명은 다음과 같습니다.
http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/entry-validation.html

링크가 끊어진 경우 텍스트 복사 / 붙여 넣기

Entry 위젯은 validatecommand가 False를 반환 할 때마다 호출되는 콜백 함수를 지정하는 invalidcommand 옵션도 지원합니다. 이 명령은 위젯의 관련 텍스트 변수에서 .set () 메서드를 사용하여 위젯의 텍스트를 수정할 수 있습니다. 이 옵션을 설정하는 것은 validate 명령을 설정하는 것과 동일하게 작동합니다. Python 함수를 래핑하려면 .register () 메서드를 사용해야합니다. 이 메서드는 래핑 된 함수의 이름을 문자열로 반환합니다. 그런 다음 해당 문자열 또는 대체 코드를 포함하는 튜플의 첫 번째 요소로 invalidcommand 옵션의 값으로 전달합니다.

참고 : 방법을 알아낼 수없는 한 가지만 있습니다. 항목에 유효성 검사를 추가하고 사용자가 텍스트의 일부를 선택하고 새 값을 입력하면 원래 값을 캡처하고 재설정 할 방법이 없습니다. 항목. 여기에 예가 있습니다.

  1. 항목은 ‘validatecommand’를 구현하여 정수만 허용하도록 설계되었습니다.
  2. 사용자가 1234567을 입력합니다.
  3. 사용자는 ‘345’를 선택하고 ‘j’를 누릅니다. 이것은 ‘345’삭제와 ‘j’삽입의 두 가지 동작으로 등록됩니다. Tkinter는 삭제를 무시하고 ‘j’가 삽입 된 경우에만 작동합니다. ‘validatecommand’는 False를 반환하고 ‘invalidcommand’함수에 전달 된 값은 다음과 같습니다. % d = 1, % i = 2, % P = 12j67, % s = 1267, % S = j
  4. 코드가 ‘invalidcommand’함수를 구현하지 않으면 ‘validatecommand’함수는 ‘j’를 거부하고 결과는 1267이됩니다. 코드가 ‘invalidcommand’함수를 구현하면 원래 1234567을 복구 할 방법이 없습니다. .


답변

다음은 사용자가 숫자 만 입력 할 수있는 입력 값을 확인하는 간단한 방법입니다.

import tkinter  # imports Tkinter module


root = tkinter.Tk()  # creates a root window to place an entry with validation there


def only_numeric_input(P):
    # checks if entry's value is an integer or empty and returns an appropriate boolean
    if P.isdigit() or P == "":  # if a digit was entered or nothing was entered
        return True
    return False


my_entry = tkinter.Entry(root)  # creates an entry
my_entry.grid(row=0, column=0)  # shows it in the root window using grid geometry manager
callback = root.register(only_numeric_input)  # registers a Tcl to Python callback
my_entry.configure(validate="key", validatecommand=(callback, "%P"))  # enables validation
root.mainloop()  # enters to Tkinter main event loop

추신 :이 예제는 calc와 같은 앱을 만드는 데 매우 유용 할 수 있습니다.


답변

import tkinter
tk=tkinter.Tk()
def only_numeric_input(e):
    #this is allowing all numeric input
    if e.isdigit():
        return True
    #this will allow backspace to work
    elif e=="":
        return True
    else:
        return False
#this will make the entry widget on root window
e1=tkinter.Entry(tk)
#arranging entry widget on screen
e1.grid(row=0,column=0)
c=tk.register(only_numeric_input)
e1.configure(validate="key",validatecommand=(c,'%P'))
tk.mainloop()
#very usefull for making app like calci