Python:如何使用不同的基类来改变或动态替换类 [英] Python: How to rebase or dynamically replace a class with a different base class

查看:200
本文介绍了Python:如何使用不同的基类来改变或动态替换类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个公共类,我想在不同的应用程序中使用。


这个想法:可以用作ui创作的基类。这个类将基于它的特定gui系统(PySide或PyQt)中的应用程序的小部件。


遵循相同的代码流来生成gui的。我喜欢在我的管道中有相同的结构,并且使用相同的命令在不同的应用程序中工作更容易。


问题: PyQt和PySide是用C ++编译的,不遵循Python类结构


我尝试了很多不同的东西,


  1. 尝试(rebase):



    在这次尝试中,我使用 __ class __ 来重命名类本身。



    从PyQt4导入QtGui,QtCore

    类UiMainBase(对象)

     PARENT = QtGui.QMainWindow 
    def __init __(self,uiFile = None,parent = None):
    如果父代:self.PARENT = parent
    self .__ class__ = self。 PARENT


    如果__name__ ==__main__:
    import sys
    从PySide导入QtGui作为PySideGui

    类MyGui(UiMainBase) :
    def __init __(self,uiFile):
    super(MyGui,self).__ init __(uiFile,PySideGui.QMainWindow)

    Ui = rC:\pythonTest\\ \\ui\test.ui
    app = PySideGui.QApplication(sys.argv)
    win = MyGui(Ui)
    win.show()
    sys.exit .exec_())





我得到的错误是合理的,不必解释。




 #1错误:
回溯(最近一次调用):
#[..]
文件C:/pythonTest/ui/ui.py行18,在__init__
self .__ class__ = self.PARENT
TypeError:__class__赋值:仅适用于堆类型

#2错误(不解析父
#参数to_init__ of super in MyGui):
Traceback(最近最后调用):
#[..]
文件C:/pythonTest/ui/uiTest.py,第18行,in __init__
self .__ class__ = self.PARENT
TypeError:__class__ assignment:'MyGui'对象布局与'QMainWindow'不同




  1. 尝试(rebase):



    我使用 __ bases __ 来重命名类本身。



    从PyQt4导入QtGui,QtCore

    类UiMainBase(对象)

     PARENT = QtGui.QMainWindow 
    def __init __(self,uiFile = None,parent = None):
    如果父代:self.PARENT = parent
    self .__ bases __ = .PARENT,)


    如果__name__ ==__main__:
    import sys
    从PySide导入QtGui作为PySideGui

    类MyGui (UiMainBase):
    def __init __(self,uiFile):
    super(MyGui,self).__ init __(uiFile,PySideGui.QMainWindow)

    Ui = rC: \\ pythonTest\ui\test.ui
    app = PySideGui.QApplication(sys.argv)
    win = MyGui(Ui)
    print win .__ bases__#< - 对象有
    win.show()
    sys.exit(app.exec_())





在这个结果中,我们可以看到对象现在得到了正确的基础,但是没有得到它的方法和变量即使我在设置 __ bases __ 后调用 super




 < type'PySide.QtGui.QMainWindow' ; #<  - 基础
Traceback(最近最后调用):
文件C:/pythonTest/ui/uiTest.py,第34行,在< module>
win.show()
AttributeError:'MyGui'对象没有属性'show'


$ b b


  1. 尝试(装饰):



    而不是rebase一个对象我试图替换类与另一个有正确的基础

     从PyQt4 import QtGui,QtCore 


    def GetUiObject(uiClass):
    parentWidget = uiClass.PARENT#< - 获取原始值,
    #不是最新的,为什么?
    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):
    构造函数。 。
    #[..]



    如果__name__ ==__main__:
    import sys
    从PySide导入QtGui as PySideGui

    UiMainBase.PARENT = PySideGui.QMainWindow#< - 指定应用程序
    #ui架构为父

    class MyGui(UiMainBase):
    def __init __(self,uiFile = None):
    #这个基类定义的变体不允许解析
    #parent参数与它的
    #(__init__将执行后基本设置)
    super(MyGui,self).__ init __(uiFile = None)
    print self.menuBar()#< - check used ui architecture





此变体的结果不会出错, < PyQt4.QtGui.QMenuBar对象在0x00000000021C9D38> ,但 MyGui 不是基于 PySideGui.QMainWindow 像我预期的


EDIT: b
$ b


为什么不用基础定义类 PySide code> PyQt4 ?:


一个genral使用和以后的扩展。应该可以自由地设置一个由应用程序( PySide PyQt )定义的父窗口小部件。但每个应用程序都有自己的方法或函数来获取它的MainWindow。因此,如果你想扩展这个MainWindow的一些小部件,或者只是父的一个新的ui到它,你需要将其定义为父,或直接基于它。一些应用程序和它的API不支持从 PySide.QtGui.QMainWindow 的自由生成的ui PyQt4.QtGui.QMainWindow 。这是我试图以这种方式做的主要原因。当然,我可以为每个应用程序创建一个 UiMainWindow 类,并将其放到主窗口(这是我现在做的),但我试图使它更通用



此示例使用示例:

是用于3DS-Max和其管道集成的一个模块。

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

UiMainBase.PARENT = Window
类UiWidget(UiMainBase):

此类创建一个为父目标的小部件
到3DS-Max中的模糊的Python
的PySide实现

def __init __(self,uiFile = None):
super(UiWidget,self).__ init __()
如果uiFile:self.loadUi(uiFile,self)
self.show()
#[..]
$ b b类PublishUi(UiWidget):
def __init __(self):
uiFile = Path.Combine(__ file __,self .__ class __.__ name __ +。ui)
super(PublishUi,self) .__ init __(uiFile)

#[..]

如果__name__ ==__main__:
#这应该从3DS- Max
publishWindow = PublishUi()

任何人都有这种情况的解决方案吗?



干杯,
Michael

解决方案

函数,动态生成正确的基类。在现实生活中,你可能会记住工厂,这样它总是为给定的父类返回相同的类对象,而不是创建多个相同的类。

 从PyQt4 import QtGui,QtCore 

def BaseFactory(parent):

class UiMainBase(parent):
....

return UiMainBase

如果__name__ ==__main__:
import sys
从PySide导入QtGui作为PySideGui

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

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


I am trying to create a common class which I want to use inside different applications.

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)

This would allow me to follow the same code flow to generate gui's. I like to have same structures inside my pipeline and it makes it much easier to work in different applications with the same commands.

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

I tried a lot of different things to get it to work, but every time I got stock at some point and doesn't get the result I want.

  1. Try (rebase):

    in this try I used __class__ to rebase the class itself.

    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. Try (rebase):

    in this try I used __bases__ to rebase the class itself.

    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. Try (decorator) :

    instead of rebase an object I tried to replace the class with another one that has the right base

    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

EDIT:

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

Because I want to leave it open for a genral use and later extensions. It should be free to set a parent widget, which is defined by the application (PySide or PyQt). But each application has it's own method or function to get it's MainWindow. So if you want extend some widgets of this MainWindow, or just parent a new ui to it you need to define it as the parent, or directly base from it. Some applications and it's API's do not support a free generated ui from type PySide.QtGui.QMainWindow or PyQt4.QtGui.QMainWindow. That's the main reason I was trying to do it in this way. Of course I could create a UiMainWindow class for each application and base it to it's main window (this is what I do for now), but I was trying to make it more generic and global for my pipeline API.

Example of usage:

This example is for 3DS-Max and inside a module of it's pipeline integration.

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()

Does anyone has a solution for this situation?

Cheers, Michael

解决方案

Make a factory function which dynamically generates the correct base class. In real life, you'd probably memorize the factory so that it always returns the same class object for a given parent, rather than creating multiple identical 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)

这篇关于Python:如何使用不同的基类来改变或动态替换类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆