अंतःक्रियात्मक रूप से एंट्री विजेट सामग्री को टिक्कर में मान्य करना


85

एक tkinter Entryविजेट में अंतःक्रियात्मक रूप से मान्य सामग्री के लिए अनुशंसित तकनीक क्या है ?

मैं का उपयोग कर के बारे में पोस्ट पढ़ा है validate=Trueऔर validatecommand=commandहै, और ऐसा लगता है कि इन सुविधाओं के तथ्य यह है कि अगर उन्हें साफ़ करने के द्वारा सीमित हैं validatecommandआदेश को अद्यतन करता Entryविजेट का मूल्य।

इस व्यवहार को देखते हुए, हम पर बाध्य होना चाहिए KeyPress, Cutऔर Pasteघटनाओं और मॉनिटर / अद्यतन हमारे Entryविजेट का मूल्य इन घटनाओं के माध्यम? (और अन्य संबंधित घटनाएं जो मुझे याद हो सकती हैं?)

या क्या हमें इंटरैक्टिव सत्यापन को पूरी तरह से भूल जाना चाहिए और केवल FocusOutघटनाओं पर मान्य होना चाहिए ?

जवाबों:


217

सही उत्तर है, validatecommandविजेट की विशेषता का उपयोग करें । दुर्भाग्य से यह सुविधा टिक्कर दुनिया में गंभीर रूप से रेखांकित की जाती है, हालाँकि यह Tk दुनिया में पर्याप्त रूप से प्रलेखित है। भले ही यह अच्छी तरह से प्रलेखित नहीं है, लेकिन इसमें वह सब कुछ है जो आपको बाइंडिंग या ट्रेसिंग चर का सहारा लिए बिना या सत्यापन प्रक्रिया के भीतर विजेट को संशोधित किए बिना सत्यापन करने की आवश्यकता है।

चाल यह जानने के लिए है कि आप अपने मान्य कमांड के लिए विशेष मूल्यों में टिंकर पास कर सकते हैं। ये मान आपको वे सभी जानकारी देते हैं जो आपको यह जानने के लिए आवश्यक है कि डेटा मान्य है या नहीं: संपादित करने से पहले का मूल्य, संपादन के बाद का मूल्य यदि संपादन मान्य है, और जानकारी के कई अन्य बिट्स। इनका उपयोग करने के लिए, हालांकि, आपको इस जानकारी को अपने मान्य कमांड को पास करने के लिए थोड़ा वूडू करने की आवश्यकता है।

नोट: यह महत्वपूर्ण है कि सत्यापन आदेश 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 देखें


15
यह इसे करने का सही तरीका है। यह उन समस्याओं को संबोधित करता है जब मैंने jmeyer10 के उत्तर को काम करने की कोशिश की। यह एक उदाहरण मैं कहीं और क्या मिल सकता है की तुलना में मान्य करने के लिए बेहतर प्रलेखन प्रदान करता है। काश मैं यह 5 वोट दे पाता।
स्टीवन रूंबल्स्की

3
वाह! मैं स्टीवन से सहमत हूं - यह एक प्रकार का उत्तर है जो एक से अधिक वोटों का हकदार है। आपको टिंकेटर पर एक किताब लिखनी चाहिए (और आपने पहले से ही पर्याप्त समाधान पोस्ट कर दिया है ताकि बहु-मात्रा श्रृंखला हो)। धन्यवाद!!!
मैल्कम

2
उदाहरण के लिए धन्यवाद। यह ध्यान देने योग्य है कि सत्यापनकर्ता को एक बूलियन (केवल सही और गलत) वापस करना होगा। ऐसा नहीं करने पर मान्यता हटा दी जाएगी।
डेव बाकर

3
मुझे लगता है कि इस पृष्ठ को सामने लाया जाना चाहिए।
राइट लेग

4
"टिंकेर वर्ल्ड में गंभीर रूप से अंडर-डॉक्यूमेंटेड"। LOL - बाकी सभी Tkiinter दुनिया की तरह।
मार्टिनगु

21

ब्रायन के कोड के साथ अध्ययन और प्रयोग करने के बाद, मैंने इनपुट सत्यापन का एक न्यूनतम संस्करण तैयार किया। निम्न कोड एक एंट्री बॉक्स रखेगा और केवल संख्यात्मक अंक स्वीकार करेगा।

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

शायद मुझे यह जोड़ना चाहिए कि मैं अभी भी पायथन सीख रहा हूं और मैं किसी भी और सभी टिप्पणियों / सुझावों को सहर्ष स्वीकार करूंगा।


1
आमतौर पर लोग इसके बजाय entry.configure(validatecommand=...)लिखते और लिखते test_valहैं testVal, लेकिन यह एक अच्छा, सरल उदाहरण है।
14 अक्टूबर को wizzwizz4

10

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

1
आपकी पोस्ट के लिए धन्यवाद। मुझे Tkinter StringVar .trace () विधि का उपयोग करने में मज़ा आया।
माल्कम

4

ब्रायन ओकले के उत्तर का अध्ययन करते समय , कुछ ने मुझे बताया कि एक अधिक सामान्य समाधान विकसित किया जा सकता है। निम्न उदाहरण एक मोड गणन, एक प्रकार का शब्दकोष, और सत्यापन के प्रयोजनों के लिए एक सेटअप फ़ंक्शन का परिचय देता है। उदाहरण उपयोग के लिए लाइन 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()

4

ब्रायन का उत्तर सही है, हालांकि किसी ने भी टेंकर विजेट के 'अमान्य' गुण का उल्लेख नहीं किया है।

एक अच्छी व्याख्या यहाँ है: http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/entry-validation.html

टूटी हुई कड़ी के मामले में पाठ कॉपी / पेस्ट

एंट्री विजेट एक अमान्यकमांड विकल्प का भी समर्थन करता है जो एक कॉलबैक फ़ंक्शन को निर्दिष्ट करता है जिसे कि जब भी वैलेडेकैम्पैंड फ़ाल्स देता है तो उसे कॉल किया जाता है। यह कमांड विजेट के संबंधित टेक्स्टवर्जेबल पर .set () विधि का उपयोग करके विजेट में पाठ को संशोधित कर सकती है। इस विकल्प को सेट करना वैलिडेटकैंडम को सेट करने के समान काम करता है। आपको अपने पायथन फ़ंक्शन को लपेटने के लिए .register () विधि का उपयोग करना होगा; यह विधि लिपटे फ़ंक्शन का नाम स्ट्रिंग के रूप में देता है। फिर आप अमान्य स्ट्रिंग विकल्प के मूल्य के रूप में या तो उस स्ट्रिंग से गुजरेंगे, या प्रतिस्थापन कोड वाले टपल के पहले तत्व के रूप में।

नोट: केवल एक चीज है जो मैं समझ नहीं पा रहा हूं कि आप कैसे कर सकते हैं: यदि आप प्रविष्टि में सत्यापन जोड़ते हैं, और उपयोगकर्ता पाठ के एक हिस्से का चयन करता है और एक नया मान टाइप करता है, तो मूल मूल्य और रीसेट पर कब्जा करने का कोई तरीका नहीं है। प्रवेश। यहाँ एक उदाहरण है

  1. एंट्री को केवल 'वैलिडेटकमैंड' लागू करके पूर्णांक स्वीकार करने के लिए डिज़ाइन किया गया है
  2. उपयोगकर्ता 1234567 में प्रवेश करता है
  3. उपयोगकर्ता '345' का चयन करता है और 'j' दबाता है। यह दो क्रियाओं के रूप में पंजीकृत है: '345' का विलोपन, और 'ज' का सम्मिलन। टिंकर विलोपन को अनदेखा करता है और केवल 'जे' के सम्मिलन पर कार्य करता है। 'मान्यमाँकांड' गलत लौटाता है, और 'अमान्य' कृत्य के लिए दिए गए मान निम्न हैं:% d = 1,% i = 2,% P = 12j67,% s = 1267,% S = j
  4. यदि कोड 'अमान्य' फ़ंक्शन को कार्यान्वित नहीं करता है, तो 'सत्यापनकर्ता' फ़ंक्शन 'j' को अस्वीकार कर देगा और परिणाम 1267 होगा। यदि कोड एक 'अमान्य कंप्यूटर' फ़ंक्शन को लागू करता है, तो मूल 1234567 को पुनर्प्राप्त करने का कोई तरीका नहीं है। ।

3

यहां प्रविष्टि मान को मान्य करने का एक सरल तरीका है, जो उपयोगकर्ता को केवल अंकों में प्रवेश करने की अनुमति देता है:

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

PS: कैल्क जैसे ऐप बनाने के लिए यह उदाहरण बहुत उपयोगी हो सकता है।


2
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

2
नमस्ते, ढेर अतिप्रवाह में आपका स्वागत है। "कोड-ओनली" उत्तर पर ध्यान दिया जाता है, खासकर जब एक प्रश्न का उत्तर दिया जाता है जिसमें पहले से ही कई उत्तर होते हैं। कृपया कुछ अतिरिक्त जानकारी जोड़ना सुनिश्चित करें कि जो प्रतिक्रिया आप प्रदान कर रहे हैं वह किसी भी तरह से मूल है और केवल वही नहीं गूँज रहा है जो पहले से ही मूल पोस्टर द्वारा वेट किया गया है।
चब

1
@ डेमियन वुल्फ मुझे मूल उत्तर का आपका उन्नत संस्करण पसंद आया, लेकिन मुझे इसे वापस लेना था। कृपया, इसे अपने स्वयं के उत्तर के रूप में पोस्ट करने पर विचार करें (आप इसे संशोधन इतिहास में पा सकते हैं )।
मार्क.2377

1

अलग-अलग विलोपन या सम्मिलन के बजाय चयन के माध्यम से पाठ के प्रतिस्थापन पर सरल सत्यापन से निपटने की ओरियनोबर्ट की समस्या का जवाब :

चयनित पाठ के प्रतिस्थापन को एक प्रविष्टि के बाद हटाए जाने के रूप में संसाधित किया जाता है। उदाहरण के लिए, विलोपन के लिए कर्सर को बाईं ओर ले जाना चाहिए, जबकि एक प्रतिस्थापन को कर्सर को दाईं ओर ले जाना चाहिए। सौभाग्य से, इन दोनों प्रक्रियाओं को एक दूसरे के तुरंत बाद निष्पादित किया जाता है । इसलिए, हम एक विलोपन के बीच अंतर कर सकते हैं और एक विलोपन के बाद सीधे सम्मिलन के बाद एक विलोपन कर सकते हैं क्योंकि बाद वाले ने विलोपन और सम्मिलन के बीच निष्क्रिय ध्वज को नहीं बदला है।

यह एक प्रतिस्थापन का उपयोग कर शोषण किया जाता हैफ्लैग और ए 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 के संयोजन एक नया अनूठा और अंतिम घटना है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.