2009-05-26 4 views
1

Написание тестового приложения для эмуляции строк PIO, у меня очень простое приложение Python/Tk GUI. Использование цифровых клавиш с 1 по 8 для имитации выводов PIO с 1 по 8. Нажмите кнопку down = 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() 

я, наконец, получил какой-то метод фабрики, работая на обратный вызов, но я не нахожу это очень удовлетворительным.

Итак, мой вопрос: можете ли вы иметь фабрику методов класса, которая будет создавать методы класса так, как я пытался (см. Код прокомментированного кода и класс приложения cb_factory())?

ПРИМЕЧАНИЯ: Да, я знаю, что это приложение позволяет вам одновременно удерживать 4 клавиши, но это достаточно хорошо для моих целей.

ответ

1

cb ожидает «я» и «событие». Может быть, это только событие из привязки?

+0

Да, это было! «Я» был лишним в методе «cb», возвращаемом «cb_factory». Я опубликую измененный код, чтобы показать рабочий пример. Теперь, если кто-то любезно объяснит это мне, мой мозг болит! –

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 запускается в цикле поиска событий (нажатия клавиш, 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

Это key = event.char Но меня больше интересовало, почему метод «cb», производимый на заводе, не нуждается в «я», поскольку это первый параметр. –

+1

, поскольку он унаследовал значение self от заводской функции, почти как глобальную переменную. self не определен в cb, но он определен в области выше (объектный метод) – SpliFF

Смежные вопросы