Leaving my working solution here.
How do I draw the combobox (with its QSS), without creating an instance of the QComboBox within the delegate? (see comment "Q1." in example).
This remains unresolved. It's not a big deal.
How do I apply the foreground/text color? (see comment "Q2." in example).
I apparently created this "bug" in my example, it did not exist in my working code and is no longer an issue.
If the dummy widget is created in paint, Qt ignores the foreground/text palette.
Moving it to __init__ resolves the issue.
This doesn't make any sense to me, but it is what it is. 🤷♂️
Solution
My cursor is hovering over row 3, col 4.
ed6ce536-2cf7-4fe0-b6a9-7811831564c3-image.png
Respects the item's background/foreground roles as well:
c5977c18-85c9-4b6a-b675-d7d65f59b724-image.png
Snippet
import sys
from PySide2 import QtCore, QtGui, QtWidgets
QSS = """
QComboBox {
background: rgb(125, 125, 125);
color: cyan;
border: 2px solid transparent;
}
QComboBox QAbstractItemView {
background: rgb(125, 125, 125);
border: 2px solid cyan;
}
"""
TEST_DATA = ["AAA", "BBB", "CCC", "DDD"]
class ExampleComboBoxDelegate(QtWidgets.QStyledItemDelegate):
def __init__(self, parent: QtWidgets.QAbstractItemView):
super().__init__(parent=parent)
self.parent().setMouseTracking(True)
# Q1. How do I completely eliminate this step?
self.__proxy_cbox = QtWidgets.QComboBox(parent=self.parent())
self.__proxy_cbox.blockSignals(True)
self.__proxy_cbox.setVisible(False)
def createEditor(self, parent, option, index) -> QtWidgets.QWidget:
editor = QtWidgets.QComboBox(parent=parent)
editor.addItems(TEST_DATA)
return editor
def setEditorData(self, editor, index):
data = str(index.data(QtCore.Qt.DisplayRole))
item_index = editor.findText(data)
if item_index >= 0:
editor.setCurrentIndex(item_index)
def setModelData(self, editor: QtWidgets.QWidget, model, index):
data = editor.currentText()
model.setData(index, data)
# With QSS.
def paint(self, painter, option, index):
# Paint.
painter.save()
self.initStyleOption(option, index)
widget_option = QtWidgets.QStyleOptionComboBox()
widget_option.initFrom(self.__proxy_cbox) # Inherit style from QComboBox.
widget_option.rect = option.rect
widget_option.currentText = str(index.data(QtCore.Qt.DisplayRole))
style = self.__proxy_cbox.style() or QtWidgets.QApplication.style()
# Hover: Draw QComboBox control without widget implementation.
if option.state & QtWidgets.QStyle.State_MouseOver:
style.drawComplexControl(QtWidgets.QStyle.CC_ComboBox, widget_option, painter, self.__proxy_cbox)
style.drawControl(QtWidgets.QStyle.CE_ComboBoxLabel, widget_option, painter, self.__proxy_cbox)
# Draw standard control.
else:
style.drawControl(QtWidgets.QStyle.CE_ItemViewItem, option, painter, self.__proxy_cbox)
painter.restore()
class Example(QtWidgets.QWidget):
def __init__(self, parent: QtWidgets.QWidget = None):
super().__init__(parent=parent)
self.setStyleSheet(QSS)
self.setMinimumSize(500, 500)
self.__setup()
self.__populate()
def __setup(self):
# Base.
main_layout = QtWidgets.QVBoxLayout()
self.setLayout(main_layout)
# Styled combobox.
lyt = QtWidgets.QHBoxLayout()
self.__lbl = QtWidgets.QLabel("This is what the delegate should look like: ", self)
self.__cbox = QtWidgets.QComboBox(self)
self.__cbox.addItems(TEST_DATA)
lyt.addWidget(self.__lbl)
lyt.addWidget(self.__cbox)
main_layout.addLayout(lyt)
# Table.
self.__model = QtGui.QStandardItemModel()
self.__table = QtWidgets.QTableView(self)
self.__table.setModel(self.__model)
main_layout.addWidget(self.__table)
def __populate(self):
# Populate with test data.
for index in range(0, 10):
items = []
for item_data in TEST_DATA:
item = QtGui.QStandardItem(item_data)
# item.setBackground(QtGui.QColor(255, 0, 0))
items.append(item)
self.__model.appendRow(items)
# Delegate.
self.__table.setItemDelegateForColumn(3, ExampleComboBoxDelegate(parent=self.__table))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Example()
window.show()
sys.exit(app.exec_())
Thanks for the help @JonB