2012-05-22 55 views
3

我已經使用了PyQt4相當多 - 有時我喜歡重載一些對象以允許我添加一些功能。這PyQt4中正常工作,如:無法更改PySide.QtGui對象的__class__

from PyQt4 import QtGui 
button = QtGui.QPushButton() 
class MyPushButton(QtGui.QPushButton): pass 
button.__class__ = MyPushButton 

不過,我想,這樣它使用PySide而不是PyQt4的適應我的一些代碼。我的理解是他們應該具有相同的功能。 PySide不會允許我做同樣的事情。

from PySide import QtGui 
button = QtGui.QPushButton() 
class MyPushButton(QtGui.QPushButton): pass 
button.__class__ = MyPushButton 

這個錯誤有:

TypeError: __class__ assignment: only for heap types 

有另一種方式我可以改變我的類對象,以避免這個錯誤?我不確定是什麼原因造成的。

注意:創建對象後,需要更改對象的類,因爲該對象是在由pyuic編譯的庫中創建的。另外我的PySide安裝沒有uic模塊。

回答

3

PySide使用Shiboken從它的C++類生成Qt的CPython綁定從 。所有的Qt python類,如QPushButton都是用C++實現的 ,這就是爲什麼你不能覆蓋__class__

>>> button = QPushButton() 
>>> button.__class__ = MyButton 
TypeError: __class__ assignment: only for heap types 

按照Shiboken的文檔,你可以monkey patch (or duck punch) 已經實例化對象的方法,只要方法 虛擬(重寫):

import types 

def override_text(self): 
    return 'overridden' 

# Bind override_text() to button. 
button.text = types.MethodType(override_text, button, QPushButton) 

採取這種進一步則可以分類QPushButton作爲MyButton和 動態地將MyButton中的方法插入到QPushButton 實例中。製作MyButtonQPushButton的子類純粹是 可選,但除了修改的QPushButton實例外,還允許您創建自己的實例MyButton

讓我們將MyButton定義爲QPushButton的子類。

class MyButton(QPushButton): 

    def text(self): 
     # This will override QPushButton's text() method. 
     print("inside MyButton.text()") 
     return QPushButton.text(self) 
  • 注:必須使用調用父類方法的舊式。 super()由於自身實際上是QPushButton 而不是MyButton,因此在注入該方法時失敗了TypeError

或者,如果你想採取更多的一個mixin的辦法,讓我們定義MyButtonOverrides

class MyButtonOverrides(object): 

    def text(self): 
     # This will override QPushButton's text() method. 
     print("inside MyButtonOverrides.text()") 
     return self.__class__.text(self) 
  • 注意:您可以直接通過self.__class__ 致電QPushButton.text(),因爲你不會使用MyButtonOverrides直。

現在讓我們來定義extend_instance()這將注入你改寫從MyButton(或MyButtonOverrides) 方法進入QPushButton 實例:

import inspect 

def extend_instance(obj, cls): 
    for name, attr in vars(cls).items(): 
     if inspect.isroutine(attr): 
      # Bind instance, class and static methods to *obj*. 
      setattr(obj, name, attr.__get__(obj, obj.__class__)) 

如果您希望類的方法保持結合到它們的起源 (例如,MyButton)然後使用以下內容:

def extend_instance(obj, cls): 
    for name, attr in vars(cls).items(): 
     if inspect.isroutine(attr): 
      if isinstance(attr, classmethod): 
       # Bind class methods to *cls*. 
       setattr(obj, name, attr.__get__(cls, cls)) 
      else: 
       # Bind instance and static methods to *obj*. 
       setattr(obj, name, attr.__get__(obj, obj.__class__)) 

通過我的測試,這可以在Python 2.7和3.3中使用,但是 可以使用2.6+和3+。

最後修改按鈕,使用extend_instance()

>>> button = QPushButton() 
>>> extend_instance(button, MyButton) 
>>> button.text() 
inside MyButton.text() 
u'' 
1

猴子補丁__class__是完全沒有必要的。相反,你應該在Qt Designer中使用相關的小部件,以便它們自動使用你的子類。

爲此,在Qt Designer中,右鍵單擊該小部件並選擇「提升爲...」。在對話框中,爲您的子類設置「Promoted class name」(例如「MyPushButton」),並將包含子類的模塊(例如「myapp」或「myapp.gui」)的「Header file」設置爲python導入路徑,管他呢)。

現在單擊「添加」,然後單擊「提升」,您將在對象檢查器窗格中看到類從「QPushButton」更改爲「MyPushButton」。

當您重新生成pyuicpyside-uic你的UI模塊,它將包含這樣的代碼:

class Ui_Window(object): 
    def setupUi(self, Window): 
     ... 
     self.button = MyPushButton(Window) 
     self.button.setObjectName("button") 
     ... 

from myapp import MyPushButton 

所以推動按鈕將被創建爲MyPushButton一個實例,不再有任何需要破解__class__

這種提升小部件的技巧對PyQt和PySide都有效。

+0

我的函數接收一個已經實例化的Widget,我需要以編程方式「推廣」Widget。我無法控制Widget的創建。猴子補丁似乎是必要的。 – Hernan

+0

@Hernan。每次有人提供解決方案時,不要增加額外的條件,如果您在問題中清楚地說明了您的要求,那將會更好。 – ekhumoro

+0

@cpburnz。 OP實際上說:「...因爲對象是在由pyuic編譯的庫中創建的」。對我來說,這個問題是由pyuic創建的對象造成的。他也沒有提到不能控制物體的創造。但無論如何,我不會刪除我的答案,因爲它可能仍然會幫助一些正在尋找這個一般主題的人。 – ekhumoro