Search code examples
python-3.9pyside6qt6

Qt6: How to customize a QTableView highlight?


I want to customize how the highlight looks when an item is selected in the table, specifically change the area that is highlighted when an item is selected. However, for some reason this code, which has previously worked in another project of mine, is now not working as expected.

Previously working MRE:

from PySide6 import QtCore as qtc
from PySide6 import QtGui as qtg
from PySide6 import QtWidgets as qtw

class ListItemsDelegate(qtw.QStyledItemDelegate):
    def paint(self, painter: qtg.QPainter, option: qtw.QStyleOptionViewItem, index: qtc.QModelIndex) -> None:
        if option.state & qtw.QStyle.StateFlag.State_Selected:
            painter.fillRect(
                    qtc.QRect(
                        option.rect.left(),
                        option.rect.top(),
                        20,
                        option.rect.height()
                    ),
                    qtc.Qt.GlobalColor.blue,
            )
        option.state &= ~qtw.QStyle.StateFlag.State_Selected
        option.state &= ~qtw.QStyle.StateFlag.State_HasFocus
        painter.drawText(option.rect.topLeft(), "text")

app = qtw.QApplication()

model = qtc.QStringListModel([1, 2, 3])
view = qtw.QListView()
view.setModel(model)
view.setItemDelegate(ListItemsDelegate())
view.show()

app.exec()

Previously working image:

enter image description here

Now not working MRE:

from PySide6 import QtCore as qtc
from PySide6 import QtGui as qtg
from PySide6 import QtWidgets as qtw

class TableItemsDelegate(qtw.QStyledItemDelegate):
    def paint(self, painter: qtg.QPainter, option: qtw.QStyleOptionViewItem, index: qtc.QModelIndex) -> None:
        if option.state & qtw.QStyle.StateFlag.State_Selected:
            highlight_rect = qtc.QRect(option.rect)
            highlight_rect.adjust(20, 20, -20, -20)
            painter.fillRect(
                    highlight_rect,
                    qtc.Qt.GlobalColor.blue,
            )
        option.state &= ~qtw.QStyle.StateFlag.State_Selected
        option.state &= ~qtw.QStyle.StateFlag.State_HasFocus
        painter.drawText(option.rect.topLeft(), "text")

class TableModel(qtc.QAbstractTableModel):
    def data(self, *args) -> int:
        return 1

    def rowCount(self, *args) -> int:
        return 5

    def columnCount(self, *args) -> int:
        return 5

app = qtw.QApplication()

view = qtw.QTableView()
view.setModel(TableModel())
view.setItemDelegate(TableItemsDelegate())
view.show()

app.exec()

Not working image:

enter image description here

They're both quite literally the same code yet I only get the custom highlight with the QListView, while I get both the custom highlight and the default highlight with QTableView, why would that be?


Solution

  • Created a custom style and applied that to the QTableView object:

    class GridViewStyle(qtw.QProxyStyle):
        # To hide default selection highlight.
        def drawPrimitive(self, element, option, painter, widget=None) -> None:
            # Check if selected:
            if option.state & qtw.QStyle.StateFlag.State_Selected:
                # if selected, remove the State_Selected StateFlag,
                option.state &= ~qtw.QStyle.StateFlag.State_Selected
                # drawPrimitive call now does everything it does, except draw selection highlight,
                super().drawPrimitive(element, option, painter)
                # re-add State_Selected StateFlag so paint() in delegate can use it.
                option.state |= qtw.QStyle.StateFlag.State_Selected
            else:
                super().drawPrimitive(element, option, painter)