Python:如何使用不同的基数 class 变基或动态替换 class

Python: How to rebase or dynamically replace a class with a different base class

我正在尝试创建一个通用的 class,我想在不同的应用程序中使用它。

The idea: a class which could be used as a base class for ui creations. This class will be based to a widget of the applications in it's specific gui system (PySide, or PyQt)

这将允许我遵循相同的代码流来生成 gui。我喜欢在我的管道中使用相同的结构,这样可以更轻松地使用相同的命令在不同的应用程序中工作。

The problem: PyQt and PySide are compiled in C++ and do not follow the Python class structure

我尝试了很多不同的方法来让它发挥作用,但每次我在某个时候进货,却没有得到我想要的结果。

  1. 尝试(变基):

    在这次尝试中,我使用 __class__ 来变基 class 本身。

    from PyQt4 import QtGui, QtCore
    
    class UiMainBase(object):
        PARENT = QtGui.QMainWindow
        def __init__(self, uiFile=None, parent=None):
            if parent: self.PARENT = parent
            self.__class__ = self.PARENT
    
    
    if __name__ == "__main__":
        import sys
        from PySide import QtGui as PySideGui
    
        class MyGui(UiMainBase):
            def __init__(self, uiFile):
                super(MyGui, self).__init__(uiFile, PySideGui.QMainWindow)
    
        Ui = r"C:\pythonTest\ui\test.ui"
        app = PySideGui.QApplication(sys.argv)
        win = MyGui(Ui)
        win.show()
        sys.exit(app.exec_())
    

The errors I get are reasonable and doesn't has to be explained.

    # 1st Error :
    Traceback (most recent call last):
      #[..]
      File "C:/pythonTest/ui/ui.py", line 18, in __init__
        self.__class__ = self.PARENT
    TypeError: __class__ assignment: only for heap types

    # 2nd Error (without parsing the parent
    # argument to __init__ of super in MyGui) :
    Traceback (most recent call last):
      #[..]
      File "C:/pythonTest/ui/uiTest.py", line 18, in __init__
        self.__class__ = self.PARENT
    TypeError: __class__ assignment: 'MyGui' object layout differs from 'QMainWindow'
  1. 尝试(变基):

    在这次尝试中,我使用 __bases__ 来变基 class 本身。

    from PyQt4 import QtGui, QtCore
    
    class UiMainBase(object):
        PARENT = QtGui.QMainWindow
        def __init__(self, uiFile=None, parent=None):
            if parent: self.PARENT = parent
            self.__bases__= (self.PARENT,)
    
    
    if __name__ == "__main__":
        import sys
        from PySide import QtGui as PySideGui
    
        class MyGui(UiMainBase):
            def __init__(self, uiFile):
                super(MyGui, self).__init__(uiFile, PySideGui.QMainWindow)
    
        Ui = r"C:\pythonTest\ui\test.ui"
        app = PySideGui.QApplication(sys.argv)
        win = MyGui(Ui)
        print win.__bases__ # <- shows what base the object has
        win.show()
        sys.exit(app.exec_())
    

In this result, we can see that the object now get's the right base, but don't get it's methods and variables (not even if I call super after setting __bases__).

    <type 'PySide.QtGui.QMainWindow'> # <- the base
    Traceback (most recent call last):
      File "C:/pythonTest/ui/uiTest.py", line 34, in <module>
        win.show()
    AttributeError: 'MyGui' object has no attribute 'show'
  1. 尝试(装饰器):

    我尝试将 class 替换为另一个具有正确基数

    的对象,而不是对对象进行变基
    from PyQt4 import QtGui, QtCore
    
    
    def GetUiObject(uiClass):
        parentWidget = uiClass.PARENT # <- Gets the original value, 
                                      # not the latest, why?
        class ParentUI(parentWidget, uiClass):
            def __init__(self, *args, **kwargs):
                super(ParentUI, self).__init__()
                uiClass.__init__(self, *args, **kwargs)
                #[..]
            def __call__(self, cls):
                for func in uiClass.__dict__:
                    setattr(cls, func, uiClass.__dict__[func])
        #ParentUI = type("ParentUI", (parentWidget,),ParentUI.__dict__.copy())
        return ParentUI
    
    @GetUiObject
    class UiMainBase( object ):
        PARENT = QtGui.QMainWindow
        def __init__(self, uiFile=None, *args, **kwargs):
            """constructor.."""
            #[..]
    
    
    
    if __name__ == "__main__":
        import sys
        from PySide import QtGui as PySideGui
    
        UiMainBase.PARENT = PySideGui.QMainWindow # <- specify the application
                                                  # ui architecture as parent
    
        class MyGui(UiMainBase):
            def __init__(self, uiFile=None): 
                # This variant of base class definition doesn't allow parsing
                # the parent argument with with it
                # (__init__ will be executed after base is set)
                super(MyGui, self).__init__(uiFile=None)
                print self.menuBar () # <- check used ui architecture
    

Th e result of this variant doesn't error and prints: <PyQt4.QtGui.QMenuBar object at 0x00000000021C9D38>, but MyGui is not based to PySideGui.QMainWindow like I expected

编辑:

Why do not defining classes with a base of PySide and one with a base of PyQt4?:

因为我想让它开放供一般使用和以后的扩展。应该可以自由设置由应用程序定义的父窗口小部件(PySidePyQt)。但是每个应用程序都有自己的方法或函数来获取它的主窗口。所以如果你想扩展这个 MainWindow 的一些小部件,或者只是将一个新的 ui 作为它的父级,你需要将它定义为父级,或者直接基于它。某些应用程序及其 API 不支持从类型 PySide.QtGui.QMainWindowPyQt4.QtGui.QMainWindow 中免费生成 ui。这是我尝试以这种方式进行操作的主要原因。当然我可以为每个应用程序创建一个 UiMainWindow class 并将其基于它的主要 window (这就是我现在所做的),但我试图使它更通用并且我的管道全局 API.

用法示例:

此示例适用于 3DS-Max 并且位于其管道集成的模块内。

from pythonLibrary.file import Path 
from qtLibrary.ui import UiMainBase
from blurdev.gui import Window

UiMainBase.PARENT = Window
class UiWidget( UiMainBase ):
    """
    This class creates a widget which is parented
    to the PySide implementation of blur's Python
    in 3DS-Max
    """
    def __init__(self, uiFile=None):
        super(UiWidget, self).__init__()
        if uiFile: self.loadUi(uiFile, self)
        self.show()
    #[..]

class PublishUi(UiWidget):
    def __init__(self):
        uiFile = Path.Combine(__file__,self.__class__.__name__+".ui")
        super(PublishUi, self).__init__(uiFile)

    #[..]

if __name__ == "__main__":
    # This should be called from a menu entry inside 3DS-Max
    publishWindow = PublishUi()

有人有办法解决这种情况吗?

干杯, 迈克尔

创建一个动态生成正确基数的工厂函数class。在现实生活中,您可能会记住工厂,以便它始终 returns 相同的 class 给定父级的对象,而不是创建多个相同的 classes.

from PyQt4 import QtGui, QtCore

def BaseFactory(parent):

    class UiMainBase(parent):
        ....

    return UiMainBase

if __name__ == "__main__":
    import sys
    from PySide import QtGui as PySideGui

    # UiMainBase = BaseFactory(QtGui.QMainWindow)
    UiMainBase = BaseFactory(PySideGui.QMainWindow)
    class MyGui(UiMainBase):
        ...

    Ui = r"C:\pythonTest\ui\test.ui"
    app = PySideGui.QApplication(sys.argv)
    win = MyGui(Ui)