2009-05-26 19 views
1

編寫一個測試應用程序來模擬PIO線,我有一個非常簡單的Python/Tk GUI應用程序。使用數字鍵1到8來模擬PIO引腳1到8.按下鍵= PIO高,鬆開鍵= 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() 

我終於得到了某種方法工廠的回調工作,但我不覺得很滿意。所以我的問題是,你可以有一個類方法工廠,這將產生我嘗試的方式類方法(請參閱註釋代碼和應用程​​序類的方法cb_factory())?

注意:是的,我知道這個應用程序只允許您一次按住4個鍵,但這對我的目的來說已經足夠了。

回答

1

cb期望'自我'和'事件'。也許它只是從綁定中獲得事件?

+0

是的,就是這樣! 「自我」在'cb_factory'返回的'cb'方法中是多餘的。我將發佈修改後的代碼以顯示工作示例。 現在,如果有人願意向我解釋這一點,我的大腦正在受傷! – 2009-05-26 12:44:21

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運行循環尋找事件(按鍵,鼠標點擊等)。當你將一個回調/函數綁定到一個事件時,你只需要讓它調用你的函數並傳遞一個事件對象作爲它的參數。然後您可以查詢事件對象以獲取實際發生的更多細節。您目前正在構建單獨的回調函數,並將每個函數綁定到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

這是 key = event.char 但是我更感興趣的是爲什麼工廠生產的'cb'方法不需要'self',因爲它是第一個參數。 – 2009-05-27 07:26:28