Python:在OOP中使用API事件处理程序

2024-10-02 14:21:47 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试为基于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,但我不认为这是完全相同的问题。你知道吗


Tags: 工具selfevent面板参数hereobjectdef
2条回答

我找到了解决这个问题的办法。我不确定是否有更优雅的解决方案,但基本上问题归结为必须将回调函数公开给global()范围,然后使用f()(g)语法用API修饰器修饰它。你知道吗

因此,我编写了一个基类(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。你知道吗

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

唯一能做到这一点的方法是从特定的类实例手动注册绑定方法——这不能使用decorator语法来完成。例如,把这个放在类的定义之后:

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

相关问题 更多 >