2009-05-26 10 views
1

PIOラインをエミュレートするテストアプリケーションを書くと、私は非常に単純なPython/Tk GUIアプリケーションを持っています。数字キー1〜8を使用してPIOピン1〜8をシミュレートします。キーダウン= PIO Highを押して、Key = PIOをロウから離します。私が必要とするのは問題ではありません。私は、キープレスのコールバック関数を作成するために工場を使用しようとしているウサギの穴のようなものになった。ここでコールバックメソッドのための工場 - Python TKinter

は、いくつかのダウン剥奪コードです:

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

私は最終的にコールバックのために働く方法工場のいくつかの並べ替えを得たが、私はそれは非常に満足していません。

私の質問は、私が試した方法でクラスメソッドを生成するクラスメソッドファクトリを持つことができます(コードクラスとAppクラスメソッドcb_factory()を参照)?

メモ:はい、このアプリでは4つのキーを一度に押すことができますが、それは私の目的には十分です。

答えて

1

cbは 'self'と 'event'を想定しています。たぶん、バインドからのイベントしか得られないのでしょうか?

+0

うん、そうだった! '自己'は 'cb_factory'によって返された 'cb'メソッドでは余計でした。私は実際の例を示すために修正されたコードを掲載します。 誰かが私にこのことを親切に説明すれば、私の脳は痛いです! –

0

ここでは、SpliFFの答えを考慮して、修正されたコードを示します。私はこれをはるかに美的に喜ばせるが、それは私がそれがどのように動作するのか理解していないバグです。だから、余分なクレジットのために、誰もこれがどのように機能するのか説明できますか?

#!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

あなたのフォローアップの質問に答えてください。

あなたが理解できない部分はわかりませんが、イベントコールバックがどのように機能するかについてはあまり気にしていないと思いますか?もしそうなら、それはかなり簡単です。 Tkはイベント(keypresses、mouseclicksなど)を探してループを走ります。コールバック/関数をイベントにバインドするときは、関数を呼び出すように指示し、イベントオブジェクトを引数として渡します。実際に何が起きたかの詳細については、イベントオブジェクトを照会することができます。あなたは現在、別々のコールバック関数を構築しており、それぞれを18のキーイベントにバインドしています(キー1-9のダウンとリリース)。イベントオブジェクトにはほぼ確実にキーコードも含まれているので、これをクラスのメソッドとして単純にcbにすることができます。

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 ... 

私たちは今、コールバックとして自己 .keyactionを使っているので、それは、最初の引数としての自己を取得する必要があります。イベントオブジェクトからキーコードを取得します。現在、関数を作成するときに値を関数に組み込む必要はありませんので、実際に各キーの異なるコールバックを作成する必要がなくなり、コードを理解しやすくなります。

+0

It's key = event.char しかし、なぜ私は工場で生産された 'cb'メソッドが最初のパラメータとして 'self'を必要としないのか興味がありました。 –

+1

は、自己の意味をファクトリ関数から継承しているため、ほぼグローバル変数と同じです。 selfはcbでは定義されていませんが、上記のスコープで定義されています(オブジェクトメソッド) – SpliFF