We ran into a really strange bug at work. We have a GUI with a tree view and some delegates, and it works just fine. While that GUI was open, a user opened and closed another GUI. This second GUI had an unhandled exception in the close event. After that point, the first GUI started throwing all sorts of errors. It was complaining that the delegate paint function was receiving a QCloseEvent instead of a QPainter. The delegate is called once for every cell in the tree, so that is a lot of errors. The only way to make it stop was to restart Maya.
I made a minimal test case. I discovered that the error happens only when I am using QTreeView. If I use QListView, there is no problem. Here is the code:
import shiboken
from PySide import QtCore, QtGui
from maya import OpenMayaUI
#------------------------------------------------------------------------------
def getMayaMainWindow():
parentWindow = OpenMayaUI.MQtUtil.mainWindow()
if parentWindow:
return shiboken.wrapInstance(long(parentWindow), QtGui.QWidget)
#------------------------------------------------------------------------------
class ErrorGUI(QtGui.QMainWindow):
def __init__(self, parent=getMayaMainWindow()):
super(ErrorGUI, self).__init__(parent)
self.setWindowTitle('Close event error')
self.setObjectName('closeEventErrorWindow')
self.centralWidget = QtGui.QWidget()
self.setCentralWidget(self.centralWidget)
self._foo = []
def closeEvent(self, event):
x = self._foo[0][-1]
#------------------------------------------------------------------------------
class SelectedItemDelegate(QtGui.QStyledItemDelegate):
""" A delegate that ensures the text color of selected items matches what is
specified in the model's ForegroundRole.
Without this delegate, the text of selected items is always white, instead
of the color specified in the model.
"""
#----- Overridden functions ------------------------------------------------
def paint(self, qPainter, option, qModelIndex):
if (option.state & QtGui.QStyle.State_Selected):
textColor = qModelIndex.data(QtCore.Qt.ForegroundRole)
if textColor is not None:
brush = QtGui.QBrush(textColor)
option.palette.setBrush(QtGui.QPalette.HighlightedText, brush)
super(SelectedItemDelegate, self).paint(qPainter, option, qModelIndex)
#------------------------------------------------------------------------------
class MyListModel(QtCore.QAbstractListModel):
def __init__(self, parent=None):
super(MyListModel, self).__init__(parent)
self._data = []
self._fgColors = []
#----- Overridden functions -----------------------------------------------
def data(self, index, role):
if not index.isValid():
return None
if role in (QtCore.Qt.DisplayRole, QtCore.Qt.EditRole):
return self._data[index.row()]
elif role == QtCore.Qt.ForegroundRole:
return self._fgColors[index.row()]
#--------------------------------------------------------------------------
def rowCount(self, parentIndex):
return len(self._data)
#----- Other functions ----------------------------------------------------
def setModelData(self, stringList, foregroundColorList):
if len(stringList) != len(foregroundColorList):
print 'Arrays do not match in size'
return
self._data = stringList
self._fgColors = foregroundColorList
#------------------------------------------------------------------------------
class DelegateGUI(QtGui.QMainWindow):
def __init__(self, parent=getMayaMainWindow()):
super(DelegateGUI, self).__init__(parent)
self.setWindowTitle('GUI with delegate')
self.setObjectName('testDelegateWindow')
# # QListView version. DOES NOT BREAK
# self.listView = QtGui.QListView()
# self.listModel = MyListModel()
# self.fillListModel()
# self.listView.setModel(self.listModel)
# textColorDelegate = SelectedItemDelegate()
# self.listView.setItemDelegate(textColorDelegate)
# self.setCentralWidget(self.listView)
# QTreeView version. THIS BREAKS!
self.treeView = QtGui.QTreeView()
self.listModel = MyListModel()
self.fillListModel()
self.treeView.setModel(self.listModel)
textColorDelegate = SelectedItemDelegate()
self.treeView.setItemDelegate(textColorDelegate)
self.setCentralWidget(self.treeView)
def fillListModel(self):
items = ['alpha', 'bravo', 'charlie', 'delta']
color = QtGui.QColor(225, 130, 0)
colors = [None, color, color, None]
self.listModel.setModelData(items, colors)
#------------------------------------------------------------------------------
def openErrorWin():
'''This ensures that only one instance of the UI is open at a time.'''
global errorWin
try: errorWin.close()
except: pass
errorWin = ErrorGUI()
errorWin.setAttribute(QtCore.Qt.WA_DeleteOnClose)
errorWin.show()
return errorWin
#------------------------------------------------------------------------------
def openDelegateWin():
'''This ensures that only one instance of the UI is open at a time.'''
global delegateWin
try: delegateWin.close()
except: pass
delegateWin = DelegateGUI()
delegateWin.setAttribute(QtCore.Qt.WA_DeleteOnClose)
delegateWin.show()
return delegateWin
Then in Maya, I run the following:
import closeEventError
dWin = closeEventError.openDelegateWin()
eWin = closeEventError.openErrorWin()
[ol]
[li]At this point there are two windows open, and everything is fine.
[/li][li]Close the window titled “Close event error”.
[/li][li]Put the focus back on the window titled “GUI with delegate”.
[/li][li]ERRORS!
[/li][/ol]