2010-10-06 17 views
9

मैं पायथन और Tkinter का उपयोग कर रहा हूं, और मैं अन्य टूलकिट/भाषाओं से onchange ईवेंट के बराबर चाहता हूं। जब भी उपयोगकर्ता कुछ विगेट्स की स्थिति अपडेट करता है तो मैं कोड चलाने के लिए चाहता हूं।जब भी कोई Tkinter विजेट मान बदलता है तो कोड कैसे चलाएं?

मेरे मामले में, मैं कई Entry, Checkbutton, Spinbox और Radiobutton विगेट्स है। जब भी इनमें से कोई भी परिवर्तन, मैं अपना कोड चलाने के लिए चाहता हूं (इस मामले में, दूसरे पैनल पर एक टेक्स्ट बॉक्स अपडेट करें)।

उत्तर

0

अब तक, मैं Tkinter में onChange से किसी बात बराबर का सामना नहीं किया है (सिर्फ इतना है कि उपयोगकर्ता या तो माउस या कीबोर्ड का उपयोग करने वालों विजेट, और यहां तक ​​कि पाठ चिपकाने के लिए Ctrl + V उपयोग करने के साथ बातचीत कर सकते हैं याद)। विजेट विभिन्न घटनाओं से बंधे जा सकते हैं और मैंने इसे स्पष्ट रूप से किया है।

+2

यह दृष्टिकोण विस्तार करने के लिए सावधान ध्यान देने की आवश्यकता है, या आप बाइंडिंग कि ऐसा * पहले * विजेट वास्तव में नहीं बल्कि तुरंत बाद से बदल जाता है बना सकते हैं। –

7

मैं कैसे हल होगा Tcl में यह सुनिश्चित करें कि checkbutton, spinbox और radiobutton विगेट्स सब एक सरणी चर के साथ जुड़े रहे बनाने के लिए किया जाएगा। मैं फिर सरणी पर एक निशान डालता हूं जो हर बार एक चर को बुलाया जाता है जिसे चर लिखा जाता है। टीसीएल यह मामूली बनाता है।

दुर्भाग्यवश Tkinter Tcl arrays के साथ काम करने का समर्थन नहीं करता है। सौभाग्य से, यह हैक करना काफी आसान है। यदि आप साहसी हैं, तो निम्न कोड आज़माएं।

पूर्ण प्रकटीकरण विभाग से: मैंने आज सुबह लगभग आधे घंटे में इसे फेंक दिया। मैंने वास्तव में इस तकनीक का उपयोग किसी भी वास्तविक कोड में नहीं किया है। हालांकि, चुनौती का विरोध नहीं कर सका, यह पता लगाने के लिए कि टिंकर के साथ सरणी का उपयोग कैसे करें।

import Tkinter as tk 

class MyApp(tk.Tk): 
    '''Example app that uses Tcl arrays''' 

    def __init__(self): 

     tk.Tk.__init__(self) 

     self.arrayvar = ArrayVar() 
     self.labelvar = tk.StringVar() 

     rb1 = tk.Radiobutton(text="one", variable=self.arrayvar("radiobutton"), value=1) 
     rb2 = tk.Radiobutton(text="two", variable=self.arrayvar("radiobutton"), value=2) 
     cb = tk.Checkbutton(text="checked?", variable=self.arrayvar("checkbutton"), 
          onvalue="on", offvalue="off") 
     entry = tk.Entry(textvariable=self.arrayvar("entry")) 
     label = tk.Label(textvariable=self.labelvar) 
     spinbox = tk.Spinbox(from_=1, to=11, textvariable=self.arrayvar("spinbox")) 
     button = tk.Button(text="click to print contents of array", command=self.OnDump) 

     for widget in (cb, rb1, rb2, spinbox, entry, button, label): 
      widget.pack(anchor="w", padx=10) 

     self.labelvar.set("Click on a widget to see this message change") 
     self.arrayvar["entry"] = "something witty" 
     self.arrayvar["radiobutton"] = 2 
     self.arrayvar["checkbutton"] = "on" 
     self.arrayvar["spinbox"] = 11 

     self.arrayvar.trace(mode="w", callback=self.OnTrace) 

    def OnDump(self): 
     '''Print the contents of the array''' 
     print self.arrayvar.get() 

    def OnTrace(self, varname, elementname, mode): 
     '''Show the new value in a label''' 
     self.labelvar.set("%s changed; new value='%s'" % (elementname, self.arrayvar[elementname])) 

class ArrayVar(tk.Variable): 
    '''A variable that works as a Tcl array variable''' 

    _default = {} 
    _elementvars = {} 

    def __del__(self): 
     self._tk.globalunsetvar(self._name) 
     for elementvar in self._elementvars: 
      del elementvar 


    def __setitem__(self, elementname, value): 
     if elementname not in self._elementvars: 
      v = ArrayElementVar(varname=self._name, elementname=elementname, master=self._master) 
      self._elementvars[elementname] = v 
     self._elementvars[elementname].set(value) 

    def __getitem__(self, name): 
     if name in self._elementvars: 
      return self._elementvars[name].get() 
     return None 

    def __call__(self, elementname): 
     '''Create a new StringVar as an element in the array''' 
     if elementname not in self._elementvars: 
      v = ArrayElementVar(varname=self._name, elementname=elementname, master=self._master) 
      self._elementvars[elementname] = v 
     return self._elementvars[elementname] 

    def set(self, dictvalue): 
     # this establishes the variable as an array 
     # as far as the Tcl interpreter is concerned 
     self._master.eval("array set {%s} {}" % self._name) 

     for (k, v) in dictvalue.iteritems(): 
      self._tk.call("array","set",self._name, k, v) 

    def get(self): 
     '''Return a dictionary that represents the Tcl array''' 
     value = {} 
     for (elementname, elementvar) in self._elementvars.iteritems(): 
      value[elementname] = elementvar.get() 
     return value 


class ArrayElementVar(tk.StringVar): 
    '''A StringVar that represents an element of an array''' 
    _default = "" 

    def __init__(self, varname, elementname, master): 
     self._master = master 
     self._tk = master.tk 
     self._name = "%s(%s)" % (varname, elementname) 
     self.set(self._default) 

    def __del__(self): 
     """Unset the variable in Tcl.""" 
     self._tk.globalunsetvar(self._name) 


if __name__ == "__main__": 
    app=MyApp() 
    app.wm_geometry("400x200") 
    app.mainloop() 
1

यह देर से गिट है, लेकिन फिर भी, किसी को यह उपयोगी लगेगा।

पूरे विचार से @bryan Oakley's post

आता है अगर मैं अच्छी तरह से समझ, मुख्य समस्या एंट्री विजेट के detech है। spinbox, Checkbutton और Radiobutton में इसका पता लगाने के लिए आप विजेट बनाते समय command विकल्प का उपयोग कर सकते हैं।

<onChange>Entry विजेट में पकड़ने के लिए आप टीसीएल का उपयोग करके ब्रायन के दृष्टिकोण का उपयोग कर सकते हैं, जो इस घटना को उत्पन्न करता है। जैसा कि मैंने कहा, यह मेरा समाधान नहीं है, मैंने इस मामले के लिए इसे थोड़ा बदल दिया है।

तो अंत में आप इसे इस तरह इस्तेमाल कर सकते हैं:

import tkinter as tk 
from tkinter import ttk 

def generateOnChange(obj): 
     obj.tk.eval(''' 
      proc widget_proxy {widget widget_command args} { 

       # call the real tk widget command with the real args 
       set result [uplevel [linsert $args 0 $widget_command]] 

       # generate the event for certain types of commands 
       if {([lindex $args 0] in {insert replace delete}) || 
        ([lrange $args 0 2] == {mark set insert}) || 
        ([lrange $args 0 1] == {xview moveto}) || 
        ([lrange $args 0 1] == {xview scroll}) || 
        ([lrange $args 0 1] == {yview moveto}) || 
        ([lrange $args 0 1] == {yview scroll})} { 

        event generate $widget <<Change>> -when tail 
       } 

       # return the result from the real widget command 
       return $result 
      } 
      ''') 
     obj.tk.eval(''' 
      rename {widget} _{widget} 
      interp alias {{}} ::{widget} {{}} widget_proxy {widget} _{widget} 
     '''.format(widget=str(obj))) 

def onEntryChanged(event = None): 
    print("Entry changed") 

def onCheckChanged(event = None): 
    print("Check button changed") 

def onSpinboxChanged(event = None): 
    print("Spinbox changed") 

def onRadioChanged(event = None): 
    print("Radio changed") 

if __name__ == '__main__': 
    root = tk.Tk() 

    frame = tk.Frame(root, width=400, height=400) 

    entry = tk.Entry(frame, width=30) 
    entry.grid(row=0, column=0) 
    generateOnChange(entry) 
    entry.bind('<<Change>>', onEntryChanged) 

    checkbutton = tk.Checkbutton(frame, command=onCheckChanged) 
    checkbutton.grid(row=1, column=0) 

    spinbox = tk.Spinbox(frame, width=100, from_=1.0, to=100.0, command=onSpinboxChanged) 
    spinbox.grid(row=2, column=0) 


    phone = tk.StringVar() 
    home = ttk.Radiobutton(frame, text='Home', variable=phone, value='home', command=onRadioChanged) 
    home.grid(row=3, column=0, sticky=tk.W) 
    office = ttk.Radiobutton(frame, text='Office', variable=phone, value='office', command=onRadioChanged) 
    office.grid(row=3, column=0, sticky=tk.E) 

    frame.pack()  
    root.mainloop() 

बेशक उदाहरणों के बहुत सारे के लिए अलग कॉलबैक बनाने के लिए इसे संशोधित (के रूप में आप प्रश्न में आपका उल्लेख) अब आसान है।

मुझे उम्मीद है कि किसी को यह उपयोगी लगेगा।

+0

दिलचस्प। बस एक छोटा ऐड-ऑन, वे विजेट जो 'कमांड' कीवर्ड का समर्थन करते हैं, इसे न केवल सृजन के दौरान सेट किया जा सकता है, बल्कि 'widget.config (command = ...) के साथ भी सेट किया जा सकता है। – norok2

5

मुझे लगता है कि एक विधि को सौंपा गया एक टिंकर चर पर trace का उपयोग करने का सही तरीका है।

उदाहरण के लिए ...

import tkinter 

root = tkinter.Tk() 
myvar = tkinter.StringVar() 
myvar.set('') 
mywidget = tkinter.Entry(root,textvariable=myvar,width=10) 
mywidget.pack() 

def oddblue(a,b,c): 
    if len(myvar.get())%2 == 0: 
     mywidget.config(bg='red') 
    else: 
     mywidget.config(bg-'blue') 
    mywidget.update_idletasks() 

myvar.trace('w',oddblue) 

root.mainloop() 

w का पता लगाने में tkinter बताता है कि जब भी किसी को (नवीनीकरण) चर, जो होगा हर बार जब कोई प्रविष्टि विजेट में कुछ लिखा था लिखते हैं, oddblue है। ट्रेस हमेशा आपके द्वारा सूचीबद्ध किए गए कार्यों के लिए तीन मानों को पास करता है, इसलिए आपको उन्हें अपने कार्य में उम्मीद करनी होगी, इसलिए a,b,c। मैं आमतौर पर उनके साथ कुछ नहीं करता क्योंकि मुझे जो भी चाहिए वह स्थानीय रूप से परिभाषित किया जाता है। मैं क्या कह सकता हूं a परिवर्तनीय वस्तु है, b खाली है (सुनिश्चित नहीं है क्यों), और c ट्रेस मोड (यानी w) है।

For more info on tkinter variables check this out.

संबंधित मुद्दे