2017-08-03 32 views
0

我正在嘗試爲基於Eclipse的工具構建一些UI面板。該工具的API對事件的機制處理基礎上的裝飾,因此,例如,下面的關係callbackOpena_panel_object開口道:Python:在OOP中使用API​​事件處理程序

@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的一些變化,但我不認爲這是相同的問題。

回答

0

我找到了解決這個問題的方法。我不確定是否有更優雅的解決方案,但基本上問題歸結爲必須將回調函數公開到global()範圍,然後使用f()(g)語法對API修飾器進行修飾。因此,我寫了一個基類(CallbackRegisterer),它爲任何派生類提供了bindHandler()方法 - 此方法包裝一個函數,併爲其提供一個唯一的ID,每個實例爲CallbackRegisterer(我正在打開一些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

0

您的裝飾器顯然會註冊稍後調用的函數。因此,在類方法上使用它是完全不合適的,因爲它不知道調用該方法的類的哪個實例。

你能做到這一點的唯一方法是手動註冊一個綁定方法從一個特定的類實例 - 這不能使用裝飾器語法來完成。例如,把這個放在你的類的定義之後:

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

謝謝,有道理;但現在這給了我以下錯誤; 'AttributeError:'instancemethod'對象沒有屬性'panelOpenHandler'' – Aidenhjj

+0

我不認爲你正確地複製了代碼(它應該是一個獨立的語句) - 沒有什麼東西應該把'panelOpenHandler'作爲一個屬性來查找。 – jasonharper

+0

我想這是一個在'panelOpenHandler'裝飾器中執行的操作。我正確地複製了你的代碼。 – Aidenhjj