PyQt/PySide QTableView, Houdini UI Palette and row colors

   8962   5   6
User Avatar
Member
53 posts
Joined: June 2009
Offline
Hi,

I am trying to display QTableView rows with different colors thanks to its abstract model.
It works as long as I do not inheritate the Houdini palette.
But has soon as I inheritate the palette, the colors are not used anymore.

I built this little example to show what I mean (see below)
On the shelf, create a new tool, add the code to it and accept.
Launch the tool.
Then Edit the tool and comment/uncomment line 34.
I also attached pictures so you see the expected result.
The same code (without line #2 and line #34) works well in Maya.

Does anyone know if there is a way to inheritate the palette AND change row colors when using the QTableView with abstract model?

thanks!

from PySide2 import QtGui, QtCore, QtWidgets
import hou

class Model(QtCore.QAbstractTableModel):
    def __init__(self):
        QtCore.QAbstractTableModel.__init__(self)
        self.tableList = [["Item %02d" % (i+1)] for i in range(5)]

    def rowCount(self, parent):
        return len(self.tableList)

    def columnCount(self, parent):
        return 1

    def data(self, index, role):
        if (role == QtCore.Qt.DisplayRole):
            return self.tableList[index.row()]

        if role == QtCore.Qt.BackgroundRole:
            return QtGui.QBrush(QtGui.QColor(255, 25+30*index.row(), 75))


class TableView(QtWidgets.QWidget):
    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent)
        tableModel = Model()
        tableView = QtWidgets.QTableView()
        tableView.setModel(tableModel)
        hbox = QtWidgets.QHBoxLayout()
        hbox.addWidget(tableView)
        self.setLayout(hbox)

        # comment / uncomment this line to see colors 
        # self.setParent(hou.ui.mainQtWindow(), QtCore.Qt.Window)
        
dialog = TableView()
dialog.show()

In Houdini, with line #34

In Houdini, without line #34

In Maya
Edited by bebe - June 5, 2019 05:26:02

Attachments:
houdini_no_setparent.png (12.9 KB)
maya.png (12.4 KB)
houdini_setparent.png (13.3 KB)

User Avatar
Member
191 posts
Joined: Oct. 2018
Offline
I've noticed that Houdini overwrites stuff like that, even things like trying to set the alternating row colors don't work. setStyleSheet seems to work for your case though. I'm not quite sure how to implement it with QTableView/QAbstractTableModel to iterate over certain rows/columns, but maybe you're more familiar with that?

tableView.setStyleSheet("QTableView {background: yellow;}")
User Avatar
Member
53 posts
Joined: June 2009
Offline
Hi,

Thanks for your reply
Your solution works fine in the view, but I could not make it work with the model

However, while trying to figure it out, I serendipitously found a solution with a QItemDelegate!
And digging further, I was helped by those two sources:
https://stackoverflow.com/questions/39995688/set-different-color-to-specifc-items-in-qlistwidget [stackoverflow.com]
https://www.saltycrane.com/blog/2008/01/pyqt4-qitemdelegate-example-with/ [www.saltycrane.com]
I added the code below.
So your idea really help me a lot, thank you very much

I am not sure whether this is the best solution though, since this adds some code complexity which is not necessary in Maya (I did not test any other app for the moment).
For instance, I have to retrieve all display information to have the text drawn correctly.
And yet, it is not aligned the same way, I don't knnow how to get the border/margin/padding value…

Anyway, I’ll go with that for now, hoping someone will stumble on this thread and know a better way.
Thanks again for your help

from PySide2 import QtGui, QtCore, QtWidgets
import hou

# solution here:
# https://stackoverflow.com/questions/39995688/set-different-color-to-specifc-items-in-qlistwidget
# https://www.saltycrane.com/blog/2008/01/pyqt4-qitemdelegate-example-with/
class MyDelegate(QtWidgets.QItemDelegate):
    def __init__(self, parent=None):
        QtWidgets.QItemDelegate.__init__(self, parent)

    def paint(self, painter, option, index):
        painter.save()

        # set background color
        painter.setPen(QtGui.QPen(QtCore.Qt.NoPen))
        if option.state & QtWidgets.QStyle.State_Selected:
            # If the item is selected, always draw background blue
            painter.setBrush(QtGui.QBrush(QtCore.Qt.blue))
        else:
            # Get the color
            c = index.data(QtCore.Qt.BackgroundRole) 
            painter.setBrush(QtGui.QBrush(QtGui.QColor(c)))

        # Draw the background rectangle            
        painter.drawRect(option.rect)

        # Get text data
        text = index.data(QtCore.Qt.DisplayRole)

        # Draw text data
        painter.setPen(QtGui.QPen(index.data(QtCore.Qt.FontRole)))
        painter.setPen(QtGui.QColor(index.data(QtCore.Qt.TextColorRole)))
        alignment = index.data(QtCore.Qt.TextAlignmentRole)
        painter.drawText(option.rect, alignment, text)

        painter.restore()

class Model(QtCore.QAbstractTableModel):
    def __init__(self):
        QtCore.QAbstractTableModel.__init__(self)
        self.tableList = ["Item %02d" % (i+1) for i in range(5)]

    def rowCount(self, parent):
        return len(self.tableList)

    def columnCount(self, parent):
        return 1

    def data(self, index, role):
        if (role == QtCore.Qt.DisplayRole):
            return self.tableList[index.row()]

        if role == QtCore.Qt.TextAlignmentRole:
            return QtCore.Qt.AlignVCenter

        if role == QtCore.Qt.TextColorRole:
            return QtGui.QColor(255, 255, 255)

        if role == QtCore.Qt.BackgroundRole:
            return QtGui.QColor(255, 25+30*index.row(), 75)


class TableView(QtWidgets.QWidget):
    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent)
        tableModel = Model()
        tableView = QtWidgets.QTableView()
        tableView.setModel(tableModel)
        mydelegate = MyDelegate(self)
        tableView.setItemDelegate(mydelegate)

        hbox = QtWidgets.QHBoxLayout()
        hbox.addWidget(tableView)
        self.setLayout(hbox)

        # comment / uncomment this line to see colors 
        self.setParent(hou.ui.mainQtWindow(), QtCore.Qt.Window)
        
dialog = TableView()
dialog.show()
Edited by bebe - June 7, 2019 07:03:28
User Avatar
Staff
1256 posts
Joined: July 2005
Offline
Houdini leans heavily on stylesheets to do the core styling of its Qt UI. Unfortunately what we have learned over time is that stylesheets make it much more difficult for end users to apply their own custom styling in general.

With the next major Houdini release we will begin moving away from stylesheets and towards using an application wide QStyle and QPalette to do the core styling. This should make it easier to apply custom styles.

For Houdini 17.5, typically the workaround is to apply local stylesheets on the widgets that will have customized looks. But in some cases the best and only way is to override the painting of the widget or item delegate.

Cheers,
Rob
User Avatar
Member
53 posts
Joined: June 2009
Offline
Hi

Thanks for your precisions, I am looking forward to the next release.

In the meantime I found how to get the full display style (font, color, alignment…) with the delegate.
It turns out there nothing to do in MyDelegate class, no paint() function to define nothing appart from the init() function.

Hope this helps!

import importlib
from PySide2 import QtGui, QtCore, QtWidgets


class MyDelegate(QtWidgets.QItemDelegate):
    def __init__(self, parent=None):
        QtWidgets.QItemDelegate.__init__(self, parent)


class Model(QtCore.QAbstractTableModel):
    def __init__(self):
        QtCore.QAbstractTableModel.__init__(self)
        self.tableList = ["Item %02d" % (i+1) for i in range(5)]

    def rowCount(self, parent):
        return len(self.tableList)

    def columnCount(self, parent):
        return 1

    def data(self, index, role):
        if (role == QtCore.Qt.DisplayRole):
            return self.tableList[index.row()]

        if role == QtCore.Qt.TextAlignmentRole:
            return QtCore.Qt.AlignVCenter

        if role == QtCore.Qt.TextColorRole:
            return QtGui.QColor(255, 255, 255)

        if role == QtCore.Qt.BackgroundRole:
            return QtGui.QColor(255, 25+30*index.row(), 75)


class TableView(QtWidgets.QWidget):
    def __init__(self, parent=None):
        QtWidgets.QWidget.__init__(self, parent)
        tableModel = Model()
        tableView = QtWidgets.QTableView()
        tableView.setModel(tableModel)
        mydelegate = MyDelegate(self)
        tableView.setItemDelegate(mydelegate)

        hbox = QtWidgets.QHBoxLayout()
        hbox.addWidget(tableView)
        self.setLayout(hbox)

        try:
            import hou
            self.setParent(hou.ui.mainQtWindow(), QtCore.Qt.Window)
        except:
            pass


dialog = TableView()
dialog.show()
User Avatar
Member
15 posts
Joined: Sept. 2014
Offline
You can simplify it even more. You don't need to subclass anything. Here is an example with a QTableWidget but it works similar for QTableView:

from PySide2 import QtGui, QtCore, QtWidgets


tableWidget = QtWidgets.QTableWidget()
tableWidget.setItemDelegate(QtWidgets.QItemDelegate())
tableWidget.setColumnCount(1)

for i in range(5):
    tableWidget.insertRow(i)
    item = QtWidgets.QTableWidgetItem("Item %02d" % (i+1))
    item.setBackground(QtGui.QColor(255, 25+30*i, 75))
    tableWidget.setItem(i, 0, item)

try:
    import hou
    tableWidget.setParent(hou.ui.mainQtWindow(), QtCore.Qt.Window)
except:
    pass

tableWidget.show()
Edited by RichardFr - June 17, 2019 20:08:48
Richard Frangenberg
Founder and developer
https://prism-pipeline.com [prism-pipeline.com]
  • Quick Links