Python:将 API 事件处理程序与 OOP 结合使用
Python: Using API Event Handlers with OOP
我正在尝试为基于 Eclipse 的工具构建一些 UI 面板。该工具的 API 具有基于装饰器的事件处理机制,例如,以下将 callbackOpen
与 a_panel_object
的开头联系起来:
@panelOpenHandler(a_panel_object)
def callbackOpen(event):
print "opening HERE!!"
这很好用,但我想将面板的所有事件处理程序和实际数据处理包装在 class 后面。理想情况下,我想做类似的事情:
class Test(object):
def __init__(self):
# initialise some data here
@panelOpenHandler(a_panel_object)
def callbackOpen(self, event):
print "opening HERE!!"
但这行不通,我想可能是因为我正在给它一个同时接受 self
和 event
的回调,当装饰器只提供 event
时在内部调用该函数(注意:我无法访问 panelOpenHandler
上的源代码,而且它的文档也不是很好......此外,任何错误消息都被 Eclipse / jython 某处吞没了)。
有什么方法可以使用一种库装饰器,它为接受多个参数的函数提供一个参数来装饰函数?我可以使用 lambdas
以某种方式绑定 self
参数并使其隐含吗?
我尝试合并一些方法的变体 here and here,但我认为这不是完全相同的问题。
你的装饰器显然注册了一个稍后调用的函数。因此,它完全不适合用于 class 方法,因为它不知道要在 class 的哪个实例上调用该方法。
您能够做到这一点的唯一方法是从特定的 class 实例手动注册一个 绑定方法 - 这不能使用装饰器语法。例如,将其放在 class:
定义之后的某处
panelOpenHandler(context.controls.PerformanceTuneDemoPanel)(Test().callbackOpen)
我找到了解决此问题的方法。我不确定是否有更优雅的解决方案,但基本上问题归结为必须将回调函数公开给 global()
范围,然后使用 API 装饰器使用 [=13 装饰它=]语法。
因此,我写了一个基础 class (CallbackRegisterer
),它为任何派生的 classes 提供 bindHandler()
方法——这个方法包装了一个函数并给出它是每个 CallbackRegisterer
实例的唯一 ID(我同时打开多个 UI 面板):
class CallbackRegisterer(object):
__count = 0
@classmethod
def _instanceCounter(cls):
CallbackRegisterer.__count += 1
return CallbackRegisterer.__count
def __init__(self):
"""
Constructor
@param eq_instance 0=playback 1=record 2=sidetone.
"""
self._id = self._instanceCounter()
print "instantiating #%d instance of %s" % (self._id, self._getClassName())
def bindHandler(self, ui_element, callback, callback_args = [], handler_type = None,
initialize = False, forward_event_args = False, handler_id = None):
proxy = lambda *args: self._handlerProxy(callback, args, callback_args, forward_event_args)
handler_name = callback.__name__ + "_" + str(self._id)
if handler_id is not None:
handler_name += "_" + str(handler_id)
globals()[handler_name] = proxy
# print "handler_name: %s" % handler_name
handler_type(ui_element)(proxy)
if initialize:
proxy()
def _handlerProxy(self, callback, event_args, callback_args, forward_event_args):
try:
if forward_event_args:
new_args = [x for x in event_args]
new_args.extend(callback_args)
callback(*new_args)
else:
callback(*callback_args)
except:
print "exception in callback???"
self.log.exception('In event callback')
raise
def _getClassName(self):
return self.__class__.__name__
然后我可以从中派生一个 class 并传入我的回调,它将使用 API 装饰器正确装饰:
class Panel(CallbackRegisterer):
def __init__(self):
super(Panel, self).__init__()
# can bind from sub classes of Panel as well - different class name in handle_name
self.bindHandler(self.controls.test_button, self._testButtonCB, handler_type = valueChangeHandler)
# can bind multiple versions of same function for repeated ui elements, etc.
for idx in range(0, 10):
self.bindHandler(self.controls["check_box_"+str(idx)], self._testCheckBoxCB,
callback_args = [idx], handler_type = valueChangeHandler, handler_id = idx)
def _testCheckBoxCB(self, *args):
check_box_id = args[0]
print "in _testCheckBoxCB #%d" % check_box_id
def _testButtonCB(self):
"""
Handler for test button
"""
print "in _testButtonCB"
panel = Panel()
请注意,我还可以从 Panel
派生出更多的子 class,并且绑定到那里的任何回调都将基于 class 获得自己独特的 handler_name
] 名称字符串。
我正在尝试为基于 Eclipse 的工具构建一些 UI 面板。该工具的 API 具有基于装饰器的事件处理机制,例如,以下将 callbackOpen
与 a_panel_object
的开头联系起来:
@panelOpenHandler(a_panel_object)
def callbackOpen(event):
print "opening HERE!!"
这很好用,但我想将面板的所有事件处理程序和实际数据处理包装在 class 后面。理想情况下,我想做类似的事情:
class Test(object):
def __init__(self):
# initialise some data here
@panelOpenHandler(a_panel_object)
def callbackOpen(self, event):
print "opening HERE!!"
但这行不通,我想可能是因为我正在给它一个同时接受 self
和 event
的回调,当装饰器只提供 event
时在内部调用该函数(注意:我无法访问 panelOpenHandler
上的源代码,而且它的文档也不是很好......此外,任何错误消息都被 Eclipse / jython 某处吞没了)。
有什么方法可以使用一种库装饰器,它为接受多个参数的函数提供一个参数来装饰函数?我可以使用 lambdas
以某种方式绑定 self
参数并使其隐含吗?
我尝试合并一些方法的变体 here and here,但我认为这不是完全相同的问题。
你的装饰器显然注册了一个稍后调用的函数。因此,它完全不适合用于 class 方法,因为它不知道要在 class 的哪个实例上调用该方法。
您能够做到这一点的唯一方法是从特定的 class 实例手动注册一个 绑定方法 - 这不能使用装饰器语法。例如,将其放在 class:
定义之后的某处panelOpenHandler(context.controls.PerformanceTuneDemoPanel)(Test().callbackOpen)
我找到了解决此问题的方法。我不确定是否有更优雅的解决方案,但基本上问题归结为必须将回调函数公开给 global()
范围,然后使用 API 装饰器使用 [=13 装饰它=]语法。
因此,我写了一个基础 class (CallbackRegisterer
),它为任何派生的 classes 提供 bindHandler()
方法——这个方法包装了一个函数并给出它是每个 CallbackRegisterer
实例的唯一 ID(我同时打开多个 UI 面板):
class CallbackRegisterer(object):
__count = 0
@classmethod
def _instanceCounter(cls):
CallbackRegisterer.__count += 1
return CallbackRegisterer.__count
def __init__(self):
"""
Constructor
@param eq_instance 0=playback 1=record 2=sidetone.
"""
self._id = self._instanceCounter()
print "instantiating #%d instance of %s" % (self._id, self._getClassName())
def bindHandler(self, ui_element, callback, callback_args = [], handler_type = None,
initialize = False, forward_event_args = False, handler_id = None):
proxy = lambda *args: self._handlerProxy(callback, args, callback_args, forward_event_args)
handler_name = callback.__name__ + "_" + str(self._id)
if handler_id is not None:
handler_name += "_" + str(handler_id)
globals()[handler_name] = proxy
# print "handler_name: %s" % handler_name
handler_type(ui_element)(proxy)
if initialize:
proxy()
def _handlerProxy(self, callback, event_args, callback_args, forward_event_args):
try:
if forward_event_args:
new_args = [x for x in event_args]
new_args.extend(callback_args)
callback(*new_args)
else:
callback(*callback_args)
except:
print "exception in callback???"
self.log.exception('In event callback')
raise
def _getClassName(self):
return self.__class__.__name__
然后我可以从中派生一个 class 并传入我的回调,它将使用 API 装饰器正确装饰:
class Panel(CallbackRegisterer):
def __init__(self):
super(Panel, self).__init__()
# can bind from sub classes of Panel as well - different class name in handle_name
self.bindHandler(self.controls.test_button, self._testButtonCB, handler_type = valueChangeHandler)
# can bind multiple versions of same function for repeated ui elements, etc.
for idx in range(0, 10):
self.bindHandler(self.controls["check_box_"+str(idx)], self._testCheckBoxCB,
callback_args = [idx], handler_type = valueChangeHandler, handler_id = idx)
def _testCheckBoxCB(self, *args):
check_box_id = args[0]
print "in _testCheckBoxCB #%d" % check_box_id
def _testButtonCB(self):
"""
Handler for test button
"""
print "in _testButtonCB"
panel = Panel()
请注意,我还可以从 Panel
派生出更多的子 class,并且绑定到那里的任何回调都将基于 class 获得自己独特的 handler_name
] 名称字符串。