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.
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'
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'
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>
, butMyGui
is not based toPySideGui.QMainWindow
like I expected
EDIT:
Why do not defining classes with a base of
PySide
and one with a base ofPyQt4
?:
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