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
我尝试了很多不同的方法来让它发挥作用,但每次我在某个时候进货,却没有得到我想要的结果。
尝试(变基):
在这次尝试中,我使用 __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'
尝试(变基):
在这次尝试中,我使用 __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'
尝试(装饰器):
我尝试将 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
?:
因为我想让它开放供一般使用和以后的扩展。应该可以自由设置由应用程序定义的父窗口小部件(PySide
或 PyQt
)。但是每个应用程序都有自己的方法或函数来获取它的主窗口。所以如果你想扩展这个 MainWindow 的一些小部件,或者只是将一个新的 ui 作为它的父级,你需要将它定义为父级,或者直接基于它。某些应用程序及其 API 不支持从类型 PySide.QtGui.QMainWindow
或 PyQt4.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)
我正在尝试创建一个通用的 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
我尝试了很多不同的方法来让它发挥作用,但每次我在某个时候进货,却没有得到我想要的结果。
尝试(变基):
在这次尝试中,我使用
__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'
尝试(变基):
在这次尝试中,我使用
__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'
尝试(装饰器):
我尝试将 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>
, butMyGui
is not based toPySideGui.QMainWindow
like I expected
编辑:
Why do not defining classes with a base of
PySide
and one with a base ofPyQt4
?:
因为我想让它开放供一般使用和以后的扩展。应该可以自由设置由应用程序定义的父窗口小部件(PySide
或 PyQt
)。但是每个应用程序都有自己的方法或函数来获取它的主窗口。所以如果你想扩展这个 MainWindow 的一些小部件,或者只是将一个新的 ui 作为它的父级,你需要将它定义为父级,或者直接基于它。某些应用程序及其 API 不支持从类型 PySide.QtGui.QMainWindow
或 PyQt4.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)