【问题标题】:Python: Using API Event Handlers with OOPPython:将 API 事件处理程序与 OOP 结合使用
【发布时间】:2017-08-03 13:42:27
【问题描述】:

我正在尝试为基于 Eclipse 的工具构建一些 UI 面板。该工具的 API 具有基于装饰器的事件处理机制,例如,以下将 callbackOpena_panel_object 的打开联系起来:

@panelOpenHandler(a_panel_object)
def callbackOpen(event):
    print "opening HERE!!"

这很好用,但我想将面板的所有事件处理程序和实际数据处理封装在一个类后面。理想情况下,我想做这样的事情:

class Test(object):
    def __init__(self):
        # initialise some data here

    @panelOpenHandler(a_panel_object)
    def callbackOpen(self, event):
        print "opening HERE!!"

但这不起作用,我想可能是因为我给它一个回调,它需要selfevent,当装饰器在内部调用函数时只提供event(注意:我无法访问panelOpenHandler 上的源代码,并且它没有很好的文档记录......而且,任何错误消息都会被某个地方的 Eclipse / jython 吞噬)。

有什么方法可以使用库装饰器,它为被装饰的函数提供一个参数,该函数需要多个参数?我可以以某种方式使用lambdas 来绑定self 参数并使其隐含吗?

我尝试将herehere 方法的一些变体结合起来,但我不认为这是完全相同的问题。

【问题讨论】:

    标签: python python-2.7 jython


    【解决方案1】:

    您的装饰器显然注册了一个稍后调用的函数。因此,它完全不适合在类方法上使用,因为它不知道在哪个类的实例上调用该方法。

    您能够做到这一点的唯一方法是从特定类实例手动注册 绑定方法 - 这不能使用装饰器语法来完成。例如,把它放在你的类定义之后的某个地方:

    panelOpenHandler(context.controls.PerformanceTuneDemoPanel)(Test().callbackOpen)
    

    【讨论】:

    • 谢谢,有道理;但这现在给了我以下错误; AttributeError: 'instancemethod' object has no attribute 'panelOpenHandler'
    • 我认为您没有正确复制代码(它应该是一个独立的语句) - 没有什么应该将 panelOpenHandler 作为属性查找。
    • 我猜这是在 panelOpenHandler 装饰器内部执行的操作。我正确复制了您的代码。
    【解决方案2】:

    我找到了解决此问题的方法。我不确定是否有更优雅的解决方案,但基本上问题归结为必须将回调函数公开到global() 范围,然后使用 API 装饰器使用f()(g) 语法对其进行装饰。

    因此,我编写了一个基类 (CallbackRegisterer),它为任何派生类提供了 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__
    

    然后我可以从中派生一个类并传入我的回调,它将使用 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 派生更多子类,并且绑定在那里的任何回调都将根据类名字符串获得自己唯一的 handler_name

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多