PySide in Maya 2013

I’ve been messing around this week getting PySide for Maya 2013 Win7 x64 up and running. Using a bunch of great guides on the net, I feel like I’ve pieced it together. First, I wanted to post the PySide version of Nathan’s PyQt tutorial for anyone else looking for this information. This is how I got it working on my end:


import shiboken
from PySide import QtGui, QtCore
import maya.OpenMayaUI as apiUI
from cStringIO import StringIO
import pysideuic
import xml.etree.ElementTree as xml


def getMayaWindow():
    """
    Get the main Maya window as a QtGui.QMainWindow instance
    @return: QtGui.QMainWindow instance of the top level Maya windows
    """
    ptr = apiUI.MQtUtil.mainWindow()
    if ptr is not None:
        return shiboken.wrapInstance(long(ptr), QtGui.QMainWindow)


def loadUiType(uiFile):
    """
    Pyside lacks the "loadUiType" command, so we have to convert the ui file to py code in-memory first
    and then execute it in a special frame to retrieve the form_class.
    """
    parsed = xml.parse(uiFile)
    widget_class = parsed.find('widget').get('class')
    form_class = parsed.find('class').text

    with open(uiFile, 'r') as f:
        o = StringIO()
        frame = {}

        pysideuic.compileUi(f, o, indent=0)
        pyc = compile(o.getvalue(), '<string>', 'exec')
        exec pyc in frame

        #Fetch the base_class and form class based on their type in the xml from designer
        form_class = frame['Ui_%s'%form_class]
        base_class = eval('QtGui.%s'%widget_class)
    return form_class, base_class


uiFile = 'c:/users/cadams/desktop/example1.ui'
listExample_form, listExample_base = loadUiType(uiFile)

class ListExample(listExample_form, listExample_base):
    def __init__(self, parent=getMayaWindow()):
        super(ListExample, self).__init__(parent)
        self.setupUi(self)

        #The names "addItemBtn" and "removeItemBtn"
        #come from the "objectName" attribute in Qt Designer
        #the attributes to access them are automatically created
        #for us when we call setupUi()
        #Designer ensures that the names are unique for us.
        self.addItemBtn.clicked.connect(self.addItem)
        self.removeItemBtn.clicked.connect(self.removeItem)

    def addItem(self):
        """
        Add a new item to the end of the listWidget
        """
        item = QtGui.QListWidgetItem(self.listWidget)
        item.setText('Item #%s!'%self.listWidget.count())

    def removeItem(self):
        """
        Remove the last item from the listWidget
        """
        count = self.listWidget.count()
        if count:
            self.listWidget.takeItem(count-1)
            
le = ListExample()
le.show()


Everything is largely the same, minus the obvious change of module imports. I did find that I had to change getMayaWindow() to return an object of type QtGui.QMainWindow instead of QtCore.QObject. Perhaps this is a nuance of the shiboken.wrapInstance() implementation, which is supposed to mirror sip.wrapinstance() in PyQt.

I’d love to hear from anyone else who’s got PySide to compile and work for 2013 to share experiences.

-csa

Hello Chris, did you build PySide for 32 bit if yes can you share the build at-least i can do something on my tiny windows laptop, I am Ubuntux64 user(workstation), On Ubuntu i discovered for pysideuic i have to download pyside-tools do you have to do the same for windows too is it provided with the PySide build setup, i am asking this with the build i have for PySide on Ubuntu for maya 2012x64 i do not have pysideuic package…

[QUOTE=csa3d;17470]

Perhaps this is a nuance of the shiboken.wrapInstance() implementation, which is supposed to mirror sip.wrapinstance() in PyQt.

I’d love to hear from anyone else who’s got PySide to compile and work for 2013 to share experiences.

-csa[/QUOTE]

Yeah PySide shiboken wrapInstance does not behave like the PyQt sip version… I wrote some code to work around that that determines the correct type to get the object as automatically. Here’s the page.

Frankly with the state of PySide as-is, I’m still inclined to continue using PyQt, one of the biggest issues I had was with the inability to use the QObject.sender() function when a slot was called via partial(). Their on-the-fly UI loading workflow is also more of a pain, compared to PyQt.

Hi csa3d,

This is a nice example of how to load an external UI file, thanks for sharing this. I’m having issues finding out how to detect if the window has already been created and thus deleting it before creating a new one. What I did was to also dock the window.

I added:


if cmds.window('MainWindow', exists = True):
	cmds.deleteUI('MainWindow')

As well as:


allowedAreas = ['right', 'left']
cmds.dockControl(label='Project panel', area='left', content='MainWindow', allowedArea=allowedAreas ) # Look in .ui file to get name of window

…but whenever the docControl code is present, the window (or panel) will not get deleted before the new one is drawn. Instead, multiple side tabs of the panel will get created. I assume ‘MainWindow’ is not always the correct name of the window…

Any ideas?

The full code below.


import maya.cmds as cmds
import shiboken
from PySide import QtGui, QtCore
import maya.OpenMayaUI as apiUI
from cStringIO import StringIO
import pysideuic
import xml.etree.ElementTree as xml


def getMayaWindow():
	"""
	Get the main Maya window as a QtGui.QMainWindow instance
	@return: QtGui.QMainWindow instance of the top level Maya windows
	"""
	ptr = apiUI.MQtUtil.mainWindow()
	print ptr
	if ptr is not None:
		return shiboken.wrapInstance(long(ptr), QtGui.QMainWindow)


def loadUiType(uiFile):
	"""
	Pyside lacks the "loadUiType" command, so we have to convert the ui file to py code in-memory first
	and then execute it in a special frame to retrieve the form_class.
	"""
	parsed = xml.parse(uiFile)
	widget_class = parsed.find('widget').get('class')
	form_class = parsed.find('class').text

	with open(uiFile, 'r') as f:
		o = StringIO()
		frame = {}

		pysideuic.compileUi(f, o, indent=0)
		pyc = compile(o.getvalue(), '<string>', 'exec')
		exec pyc in frame

		#Fetch the base_class and form class based on their type in the xml from designer
		form_class = frame['Ui_%s'%form_class]
		base_class = eval('QtGui.%s'%widget_class)
	return form_class, base_class


uiFile = 'c:/users/cadams/desktop/example1.ui'
listExample_form, listExample_base = loadUiType(uiFile)


class listExample(listExample_form, listExample_base):
	def __init__(self, parent=getMayaWindow()):

		if cmds.window('MainWindow', exists = True):
			cmds.deleteUI('MainWindow')

		super(listExample, self).__init__(parent)

		#print 'Self is set to: ' + str(self)
		self.setupUi(self)

		allowedAreas = ['right', 'left']
		cmds.dockControl(label='Project panel', area='left', content='MainWindow', allowedArea=allowedAreas ) # Look in .ui file to get name of window

		#The names "addItemBtn" and "removeItemBtn"
		#come from the "objectName" attribute in Qt Designer
		#the attributes to access them are automatically created
		#for us when we call setupUi()
		#Designer ensures that the names are unique for us.
		self.addItemBtn.clicked.connect(self.addItem)
		self.removeItemBtn.clicked.connect(self.removeItem)

	def addItem(self):
		"""
		Add a new item to the end of the listWidget
		"""
		item = QtGui.QListWidgetItem(self.listWidget)
		item.setText('Item #%s!'%self.listWidget.count())

	def removeItem(self):
		"""
		Remove the last item from the listWidget
		"""
		count = self.listWidget.count()
		if count:
			self.listWidget.takeItem(count-1)




le = listExample()
#le.show()


I’ve realized the window name can be gotten from the dockControl command, like this:

windowName = cmds.dockControl() …

… and so I can delete the docked window via cmds.deleteUI(windowName).

However, right now I need to store this variable/name outside of the class in order to perform a check for the window before it has been created, which maybe isn’t very nice. Any suggestions?