Написание тестового приложения для эмуляции строк PIO, у меня очень простое приложение Python/Tk GUI. Использование цифровых клавиш с 1 по 8 для имитации выводов PIO с 1 по 8. Нажмите кнопку down = PIO High, отпустите кнопку Key = PIO. Для меня это не проблема. Я как бы спустился по кроличьей дыре, пытаясь использовать factory, чтобы создать ключевые функции переадресации вызовов.
Вот какой-то урезанный код:
#!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()
Наконец-то я получил какой-то метод factory, работающий для обратного вызова, но я не считаю его очень удовлетворительным.
Итак, мой вопрос: можете ли вы иметь метод класса factory, который будет генерировать методы класса так, как я пытался (см. прокомментированный код и класс класса приложения cb_factory())?
ПРИМЕЧАНИЯ: Да, я знаю, что это приложение позволяет вам одновременно удерживать 4 клавиши, но это достаточно хорошо для моих целей.
cb ожидает "я" и "событие". Может быть, это только событие из привязки?
В ответ на ваш последующий вопрос.
Я не уверен, какую часть вы не понимаете, но я предполагаю, что вы не совсем справляетесь с тем, как работают обратные вызовы событий? Если так, то это довольно легко. 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 ...
Поскольку мы теперь используем self.keyaction как обратный вызов, он должен получить себя как свой первый аргумент. Он получает свой ключевой код от объекта события. Теперь ни одна ценность не должна быть "встроена" в функцию при создании функции, поэтому необходимость создания разных обратных вызовов для каждой клавиши будет удалена, а код легче понять.
Вот исправленный код с учетом ответа 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()