2009-05-26 2 views
1

Schreiben einer Test-App, um PIO-Zeilen zu emulieren, habe ich eine sehr einfache Python/Tk GUI-App. Verwenden Sie die numerischen Tasten 1 bis 8, um die PIO-Pins 1 bis 8 zu simulieren. Drücken Sie die Taste nach unten = PIO High, lassen Sie die Taste los = PIO wird niedrig. Wofür ich es brauche, ist nicht das Problem. Ich bin irgendwie in ein Kaninchenloch gegangen und habe versucht, eine Fabrik zu benutzen, um die Tastenrückruffunktionen zu erzeugen.Factory für Callback-Methoden - Python TKinter

hier etwas abgespeckte Code:

#!usr/bin/env python 
""" 
Python + Tk GUI interface to simulate a 8 Pio lines. 
""" 

from Tkinter import * 

def cb_factory(numberic_key): 
    """ 
    Return a call back function for a specific keyboard numeric key (0-9) 
    """ 
    def cb(self, event, key=numberic_key): 
     bit_val = 1<<numberic_key-1 
     if int(event.type) == 2 and not (bit_val & self.bitfield): 
      self.bitfield |= bit_val 
      self.message("Key %d Down" % key) 
     elif int(event.type) == 3 and (bit_val & self.bitfield): 
      self.bitfield &= (~bit_val & 0xFF) 
      self.message("Key %d Up" % key) 
     else: 
      # Key repeat 
      return 
     print hex(self.bitfield) 
     self.display_bitfield() 
    return cb 

class App(Frame): 
    """ 
    Main TK App class 
    """ 

    cb1 = cb_factory(1) 
    cb2 = cb_factory(2) 
    cb3 = cb_factory(3) 
    cb4 = cb_factory(4) 
    cb5 = cb_factory(5) 
    cb6 = cb_factory(6) 
    cb7 = cb_factory(7) 
    cb8 = cb_factory(8) 

    def __init__(self, parent): 
     "Init" 
     self.parent = parent 
     self.bitfield = 0x00 
     Frame.__init__(self, parent) 

     self.messages = StringVar() 
     self.messages.set("Initialised") 

     Label(parent, bd=1, 
       relief=SUNKEN, 
       anchor=W, 
       textvariable=self.messages, 
       text="Testing").pack(fill=X) 

     self.bf_label = StringVar() 
     self.bf_label.set("0 0 0 0 0 0 0 0") 

     Label(parent, bd=1, 
       relief=SUNKEN, 
       anchor=W, 
       textvariable=self.bf_label, 
       text="Testing").pack(fill=X) 

# This Doesn't work! Get a traceback saying 'cb' expected 2 arguements 
# but only got 1? 
# 
#  for x in xrange(1,9): 
#   cb = self.cb_factory(x) 
#   self.parent.bind("<KeyPress-%d>" % x, cb) 
#   self.parent.bind("<KeyRelease-%d>" % x, cb) 

     self.parent.bind("<KeyPress-1>", self.cb1) 
     self.parent.bind("<KeyRelease-1>", self.cb1) 

     self.parent.bind("<KeyPress-2>", self.cb2) 
     self.parent.bind("<KeyRelease-2>", self.cb2) 

     self.parent.bind("<KeyPress-3>", self.cb3) 
     self.parent.bind("<KeyRelease-3>", self.cb3) 

     self.parent.bind("<KeyPress-4>", self.cb4) 
     self.parent.bind("<KeyRelease-4>", self.cb4) 

     self.parent.bind("<KeyPress-5>", self.cb5) 
     self.parent.bind("<KeyRelease-5>", self.cb5) 

     self.parent.bind("<KeyPress-6>", self.cb6) 
     self.parent.bind("<KeyRelease-6>", self.cb6) 

     self.parent.bind("<KeyPress-7>", self.cb7) 
     self.parent.bind("<KeyRelease-7>", self.cb7) 

     self.parent.bind("<KeyPress-8>", self.cb8) 
     self.parent.bind("<KeyRelease-8>", self.cb8) 


    def display_bitfield(self): 
     """ 
     Display the PIO lines (1 for on, 0 for off) 
     """ 
     bin_lst = [] 
     for x in xrange(8): 
      bit = 1 << x 
      if bit & self.bitfield: 
       bin_lst.append("1") 
      else: 
       bin_lst.append("0") 
     bin_lst.reverse() 
     bin_str = " ".join(bin_lst) 
     self.bf_label.set(bin_str) 

    def message(self, msg_txt): 
     "set" 
     self.messages.set(msg_txt) 

    def cb_factory(self, numberic_key): 
     """ 
     Return a call back function for a specific keyboard numeric key (0-9) 
     """ 
     def cb(self, event, key=numberic_key): 
      bit_val = 1<<numberic_key-1 
      if int(event.type) == 2: 
       self.bitfield |= bit_val 
       self.message("Key %d Down" % key) 
      else: 
       self.bitfield &= (~bit_val & 0xFF) 
       self.message("Key %d Up" % key) 
      print hex(self.bitfield) 
      self.display_bitfield() 
     return cb 

########################################################################## 

if __name__ == "__main__": 

    root = Tk() 
    root.title("PIO Test") 
    theApp = App(root) 

    root.mainloop() 

ich eine Art Methode Fabrik für den Rückruf arbeiten schließlich bekam aber ich es nicht sehr zufrieden.

Also meine Frage ist, können Sie eine Klassenmethode Factory, die Klassenmethoden die Art und Weise, die ich versucht habe, produzieren (siehe auskommentierter Code und App-Klasse-Methode cb_factory())?

HINWEISE: Ja, ich weiß, dass Sie mit dieser App nur 4 Tasten gleichzeitig gedrückt halten können, aber das ist gut genug für meine Zwecke.

Antwort

1

cb erwartet 'Selbst' und 'Ereignis'. Vielleicht bekommt es nur Event aus der Bindung?

+0

Ja, das war es! Das "Selbst" war in der "cb" -Methode, die von "cb_factory" zurückgegeben wurde, überflüssig. Ich werde den geänderten Code veröffentlichen, um das Arbeitsbeispiel zu zeigen. Nun, wenn jemand mir das freundlicherweise erklären würde, ist mein Gehirn verletzt! –

0

Hier ist der geänderte Code, der die Antwort von SpliFF berücksichtigt. Ich finde das viel ästhetischer, aber es nervt mich, dass ich nicht verstehe, wie es funktioniert. Also, für zusätzlichen Kredit, kann jemand erklären, wie das funktioniert?

#!usr/bin/env python 
""" 
Python + Tk GUI interface to simulate a 8 Pio lines. 
""" 

from Tkinter import * 
from pio_handler import * 

class App(Frame): 
    """ 
    Main TK App class 
    """ 

    def __init__(self, parent): 
     "Init" 
     self.parent = parent 
     self.bitfield = 0x00 
     Frame.__init__(self, parent) 

     self.messages = StringVar() 
     self.messages.set("Initialised") 

     Label(parent, bd=1, 
       relief=SUNKEN, 
       anchor=W, 
       textvariable=self.messages, 
       text="Testing").pack(fill=X) 

     self.bf_label = StringVar() 
     self.bf_label.set("0 0 0 0 0 0 0 0") 

     Label(parent, bd=1, 
       relief=SUNKEN, 
       anchor=W, 
       textvariable=self.bf_label, 
       text="Testing").pack(fill=X) 

     # This is the clever bit! 
     # Use a factory to assign a callback function for keys 1 to 8 
     for x in xrange(1,9): 
      cb = self.cb_factory(x) 
      self.parent.bind("<KeyPress-%d>" % x, cb) 
      self.parent.bind("<KeyRelease-%d>" % x, cb) 

    def display_bitfield(self): 
     """ 
     Display the PIO lines (1 for on, 0 for off) 
     """ 
     bin_lst = [] 
     for x in xrange(8): 
      bit = 1 << x 
      if bit & self.bitfield: 
       bin_lst.append("1") 
      else: 
       bin_lst.append("0") 
     bin_lst.reverse() 
     bin_str = " ".join(bin_lst) 
     self.bf_label.set(bin_str) 

    def message(self, msg_txt): 
     "set" 
     self.messages.set(msg_txt) 

    def cb_factory(self, numeric_key): 
     """ 
     Return a call back function for a specific keyboard numeric key (0-9) 
     """ 
     def cb(event, key=numeric_key): 
      bit_val = 1<<numeric_key-1 
      if int(event.type) == 2: 
       self.bitfield |= bit_val 
       self.message("Key %d Down" % key) 
      else: 
       self.bitfield &= (~bit_val & 0xFF) 
       self.message("Key %d Up" % key) 
      print hex(self.bitfield) 
      self.display_bitfield() 
     return cb 

########################################################################## 

if __name__ == "__main__": 

    root = Tk() 
    root.title("PIO Test") 
    theApp = App(root) 

    root.mainloop() 
1

Als Antwort auf Ihre Follow-up-Frage.

Ich bin mir nicht sicher, welchen Teil Sie nicht verstehen, aber ich vermute, Sie haben nicht ganz einen Griff darüber, wie Ereignisrückrufe arbeiten? Wenn es so ist, ist es ziemlich einfach. Tk läuft in einer Schleife auf der Suche nach Ereignissen (Tasten drücken, Mausklicks, etc ..). Wenn Sie einen Callback/eine Funktion an ein Ereignis binden, geben Sie diesem lediglich vor, die Funktion aufzurufen und ein Ereignisobjekt als Argument zu übergeben. Sie können dann das Ereignisobjekt abfragen, um genauere Informationen zu den tatsächlich aufgetretenen Ereignissen zu erhalten. Sie bauen derzeit separate Callback-Funktionen auf und binden jeweils 18 Schlüsselereignisse (ab und geben sie auf den Tasten 1-9 frei). Ich denke, dass Sie dies umschreiben können, um einfach cb als eine Methode Ihrer Klasse zu haben, weil das Ereignisobjekt fast sicher auch den Schlüsselcode enthalten wird.

class: 
    def __init__(self): 
    for x in xrange(8): 
     self.parent.bind("<KeyPress-%d>" % x, self.keyaction) 
     self.parent.bind("<KeyRelease-%d>" % x, self.keyaction) 

    def keyaction(self, event): 
    key = event.keycode # attribute may have another name, I haven't checked tk docs 
    ... do stuff ... 

Da wir jetzt Selbst .keyaction als Callback verwenden, sollten sie sich selbst als erstes Argument bekommen. Es erhält seinen Schlüsselcode vom Ereignisobjekt. Nun muss kein Wert in die Funktion "eingebaut" werden, wenn die Funktion erstellt wird, so dass die Notwendigkeit, tatsächlich verschiedene Rückrufe für jeden Schlüssel zu erzeugen, entfernt wird und der Code leichter zu verstehen ist.

+0

Es ist key = event.char Aber ich war mehr daran interessiert, warum die "cb" -Methode von der Fabrik nicht "Selbst" als erste Parameter benötigt. –

+1

weil er die Bedeutung von self von der Factory-Funktion geerbt hat, fast wie eine globale Variable. self ist nicht in cb definiert, aber es ist im obigen Bereich definiert (die Objektmethode) – SpliFF