So I am experimenting withe PySide/Qt in Maya 2016.
I thought I t might be useful to have windows that can launch both independently as a QApplication() and within maya. At least that way I can work on layouts etc outside of Maya directly.
This is the approach that I got working
import sys
from PySide.QtGui import *
class Form(QDialog):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
self.label=QLabel("hello world")
layout=QVBoxLayout()
layout.addWidget(self.label)
self.setLayout(layout)
#try running otuside of maya
try:
app = QApplication(sys.argv)
form = Form()
form.show()
app.exec_()
#Within in Maya, QApplication exists and we get a RuntimeError, so try running inside of maya
except RuntimeError:
from shiboken import wrapInstance
from maya import OpenMayaUI as omui
mayaMainWindowPtr = omui.MQtUtil.mainWindow()
mayaMainWindow= wrapInstance(long(mayaMainWindowPtr), QWidget)
form = Form(parent=mayaMainWindow)
form.show()
While this works, I would rather have the class handle things itself,
though I am unsure if that is even possible. My attempts to move the functionality inside the form are failing.
has anybody solved this?
Whenever Iâve written something like this, Iâve done it rather similar to yours.
Though I tend to just have all the maya specific code in its own module for launching the tool within maya.
One thing to watch out for, if you run this when a QtCore.QCoreApplication is running, youâll get some errors about not being able to create a QWidget when no gui is being used. (This is also crashing mayapy for me in 2016) Which can happen if you run maya.standalone.initialize before running your tool.
i develop GUIs outside Maya with PySide so i can launch directly from Wing IDE and validate the layout, buttons, states, etc.
I split the GUI into two files - one file that is pure python, and the other file for use inside Maya which imports the first files and extends the GUI with maya specific functions.
I have done something similar, called universal_tool_template,
a template runs in Maya, standalone and also cross-platform in either pyside or pyqt4, and a lot of automated functions, like Ui translation export and load
also, the ui creation can be text based, and support creating complex ui layout in a few lines of code.
I faced some months ago the same necessity: i wanted a Qt launcher that would world exactly the same when launching the QApplication as standalone, as well as when launching the tool within the Maya QApp.
The reason for this is, itâs just better and easier for me when dealing with complicated widgets layouts, QDialogs, QMainWindows, etc to test them outside Maya.
My solution uses a ContextManager to handle this but it is not strictly necessary.
You could just use the âapplication()â function and replace the âyieldâ statement with the code that gets the window both ways, but since the only difference is how you obtain the window instance and the rest of the code is the same i think it is ideal for a context manager. (Donât Repeat Yourself)
Iâve been coding for almost 2 years little scripts in python for Maya and iâm still learning each day new things!!
I typically use the same approach as rgkovach - primarily because it allows my core UI code to be dropped into multiple applications (maya, max, Mobu, Modo etc) without any bloated code to handle all the possible (and future!) scenarios.
Thus keeping the application agnostic code/modules completely pure ensures they are easy to maintain and allows each host application to subclass and make per-application tweaks or extensions.
[QUOTE=MikeM;30854]I typically use the same approach as rgkovach - primarily because it allows my core UI code to be dropped into multiple applications (maya, max, Mobu, Modo etc) without any bloated code to handle all the possible (and future!) scenarios.
Thus keeping the application agnostic code/modules completely pure ensures they are easy to maintain and allows each host application to subclass and make per-application tweaks or extensions.[/QUOTE]
Yeah, i agree, in my case i havent faced yet the situation where i need to replicate same Ui for different applications so for now my approach works (I only work with Maya). But i agree that if i ever need to extend it to, for example Nuke, my approach would need to add for each new application an if statement that would detect which host app iâm running in.
I thought I would come back to this thread to update how I now approach the issue -if only to make sure I am understanding things correctly. My solution is a just variation on the suggestions above. Here is the complete module. The approach is explained in the comments and doc strings.
'''
widget.py
a module for QtWidget class that can launch stand-alone or from maya
The two basic obstacles to this are:
1: the QApplication : stand-alone we have to instantiate a QApplication object, in Maya we do not (Maya Main Window is the QApplication object)
2: the QWidget instances `parent` argument : stand-alone = None, in maya, = reference to Maya Main Window
Each obstacle is tackled with helper functions:
1: launch_widget()
2: get_parent()
*note : We use Maya 2017 so this is using Pyside2.
for pyside you'll need to use QtGui instead fo QtWidgets (untested)
'''
from PySide2 import QtWidgets
def get_parent():
'''
get a value for a QtQWidget's parent
if in Maya, returns MayaMainWindow Qt object
else returns None
:return: None or MayaWindow Qt Object
'''
parent=None
try:
parent = {o.objectName(): o for o in QtWidgets.qApp.topLevelWidgets()}["MayaWindow"]
except Exception as e:
print e
pass
print "parent={}".format(parent)
return parent
class MyWidget(QtWidgets.QDialog):
'''
A basic QtWidget class.
The only unusual bit is using get_parent() in the __init__()
'''
def __init__(self, parent=get_parent(), message="default text"):
super( MyWidget, self).__init__(parent)
self.label=QtWidgets.QLabel()
layout=QtWidgets.QVBoxLayout()
layout.addWidget(self.label)
self.setLayout(layout)
self.label.setText(message)
self.setWindowTitle("MyWidget")
def launch_widget(message):
'''
a wrapper function for convenience
Save this module as 'widget.py' and use :
import widget
widget.launch_widget("my message")
it works the same in and outside of maya
:param message: str
:return: None
'''
# try standalone
try:
app = QtWidgets.QApplication([])
widget = MyWidget(message=message)
widget.show()
app.exec_()
# Within in Maya, QApplication exists and we get a RuntimeError, so try running inside of maya
except RuntimeError:
widget = MyWidget(message=message)
widget.exec_() # QDialog instances have the exec_() method. For other QtWidgets use widget.show()
# for stand-alone testing
if __name__ == '__main__':
launch_widget("I Stand Alone...")
Save this module as âwidget.pyâ and use :
import widget
widget.launch_widget("my message")
it works the same in and outside of maya.
Iâve used this approach on a few tools so far without issue, but if anyone spots any serious flaws inn my code or my thinking Iâd love to hear about it.
I have learned that in the Maya âexceptâ clause , using exec_() only works for an instance of QDialog.
Other QtWidgets such as QMainWindow do not have and exec_() method, and must use show().
Code comments updated accordingly.