ProgramingTip

Tkinter에서 대화식으로 항목 위젯 컨텐츠 유효성

bestdevel 2020. 10. 25. 12:44
반응형

Tkinter에서 대화식으로 항목 위젯 컨텐츠 유효성


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

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

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

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


정답은 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()

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

Bryan의 대답은하지만 아무도 tkinter 위젯의 'invalidcommand'속 언급하지 않습니다.

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

링크가 더러워진 경우 텍스트 복사 / 가져 오기

항목 위젯은 validatecommand가 False를 반환 할 때마다 호출되는 호출되는 함수를 지정하는 invalidcommand 옵션도 지원합니다. 이 명령은 위젯의 관련 텍스트 변수에서 .set () 메소드를 사용하여 위젯의 텍스트를 사용할 수 있습니다. 이 옵션을 설정하는 것은 명령을 설정하는 것과 동일하게 작동합니다. Python 함수를 래핑합니다. 이 메서드는 래핑 된 함수의 이름을 반환합니다. 그런 다음 포함하는 SSL 또는 대체 코드를 포함하는 튜플의 첫 번째 요소로 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
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

별도의 삭제 또는 삽입 대신 선택을 통해 텍스트를 대체 할 때 간단한 유효성 검사를 처리 하는 orionrobert의 문제대한 응답 :

선택한 텍스트의 대체는 삭제 후 삽입으로 처리됩니다. 예를 들어 삭제하면 커서가 왼쪽으로 이동하고 대체는 커서를 오른쪽으로 이동해야하는 경우 문제가 발생할 수 있습니다. 다행히도이 두 프로세스는 즉시 실행 됩니다. 따라서 후자는 삭제와 삽입 사이에 유휴 시간이 없기 때문에 자체 삭제와 대체로 인해 삽입이 직접 뒤 따르는 삭제를 구별 할 수 있습니다.

이것은 replacementFlag 및 Widget.after_idle(). after_idle()이벤트 큐 끝에서 람다 함수를 실행합니다.

class ValidatedEntry(Entry):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.tclValidate = (self.register(self.validate), '%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W')
        # attach the registered validation function to this spinbox
        self.config(validate = "all", validatecommand = self.tclValidate)

    def validate(self, type, index, result, prior, indelText, currentValidationMode, reason, widgetName):

        if typeOfAction == "0":
            # set a flag that can be checked by the insertion validation for being part of the substitution
            self.substitutionFlag = True
            # store desired data
            self.priorBeforeDeletion = prior
            self.indexBeforeDeletion = index
            # reset the flag after idle
            self.after_idle(lambda: setattr(self, "substitutionFlag", False))

            # normal deletion validation
            pass

        elif typeOfAction == "1":

            # if this is a substitution, everything is shifted left by a deletion, so undo this by using the previous prior
            if self.substitutionFlag:
                # restore desired data to what it was during validation of the deletion
                prior = self.priorBeforeDeletion
                index = self.indexBeforeDeletion

                # optional (often not required) additional behavior upon substitution
                pass

            else:
                # normal insertion validation
                pass

        return True

물론 대체 후 삭제 부분을 확인하는 동안 삽입이 뒤따를 지 여부를 알 수 없습니다. 다행히 그러나,로 : .set(), .icursor(), .index(SEL_FIRST), .index(SEL_LAST), .index(INSERT), 우리가 대상으로 가장 원하는 동작을 얻을 수 있습니다 (삽입과 우리의 새로운 substitutionFlag의 조합은 새로운 독특하고 최종 이벤트이기 때문이다.


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

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와 같은 앱을 만드는 데 매우 유용 할 수 있습니다.

참고 URL : https://stackoverflow.com/questions/4140437/interactively-validating-entry-widget-content-in-tkinter

반응형