I have this table, and what I want is a few things:
When I click on a cell, I want the entire row from that cell's location to be highlighted,something white-ish, and for some function to be able to get all the values from all the columns for that row
I have colors for all the combobox items, but I want to be able to color the combobox background with the same color as the one I chose for the selected item, the colors in painter.fillRect()
Non-divine code:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets,uic
from PyQt5.QtCore import QSize, Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton,QTableWidget,QStyledItemDelegate
class MyQComboBox(QtWidgets.QComboBox):
def __init__(self, scrollWidget=None, *args, **kwargs):
super(MyQComboBox, self).__init__(*args, **kwargs)
self.scrollWidget=scrollWidget
self.setFocusPolicy(QtCore.Qt.StrongFocus)
def wheelEvent(self, *args, **kwargs):
return
class StatusItemDelegate(QtWidgets.QStyledItemDelegate):
def __init__(self, parent=None):
QtWidgets.QStyledItemDelegate.__init__(self, parent=parent)
def paint(self, painter, option, index):
if index.row() == 0 :
if option.state & QtWidgets.QStyle.State_Selected: # highligh background if selected
painter.fillRect(option.rect, option.palette.highlight())
painter.save()
painter.fillRect(option.rect, QtGui.QBrush(QtGui.QColor(228, 221, 131))) # Initializing...
painter.restore()
elif index.row() == 1 :
painter.save()
painter.fillRect(option.rect, QtGui.QBrush(QtGui.QColor(185, 183, 159))) # Not Started
painter.restore()
elif index.row() == 2 :
painter.save()
painter.fillRect(option.rect, QtGui.QBrush(QtGui.QColor(65, 203, 53))) # Track
painter.restore()
elif index.row() == 3 :
painter.save()
painter.fillRect(option.rect, QtGui.QBrush(QtGui.QColor(38, 121, 31))) # Track Apporval
painter.restore()
elif index.row() == 4 :
painter.save()
painter.fillRect(option.rect, QtGui.QBrush(QtGui.QColor(31, 121, 104))) # For Solve
painter.restore()
elif index.row() == 5 :
painter.save()
painter.fillRect(option.rect, QtGui.QBrush(QtGui.QColor(48, 216, 226))) # Solve
painter.restore()
elif index.row() == 6 :
painter.save()
painter.fillRect(option.rect, QtGui.QBrush(QtGui.QColor(226, 167, 48))) # For GeoBuild
painter.restore()
elif index.row() == 7 :
painter.save()
painter.fillRect(option.rect, QtGui.QBrush(QtGui.QColor(226, 142, 48))) # GeoBuild
painter.restore()
elif index.row() == 8 :
painter.save()
painter.fillRect(option.rect, QtGui.QBrush(QtGui.QColor(243, 222, 108))) # For Rotomation
painter.restore()
elif index.row() == 9 :
painter.save()
painter.fillRect(option.rect, QtGui.QBrush(QtGui.QColor(243, 141, 108))) # Rotomation
painter.restore()
elif index.row() == 10 :
painter.save()
painter.fillRect(option.rect, QtGui.QBrush(QtGui.QColor(154, 76, 79))) # Waiting Assets
painter.restore()
elif index.row() == 11 :
painter.save()
painter.fillRect(option.rect, QtGui.QBrush(QtGui.QColor(76, 112, 154))) # For Packing
painter.restore()
elif index.row() == 12 :
painter.save()
painter.fillRect(option.rect, QtGui.QBrush(QtGui.QColor(53, 89, 201))) # Packing
painter.restore()
elif index.row() == 13 :
painter.save()
painter.fillRect(option.rect, QtGui.QBrush(QtGui.QColor(114, 228, 118))) # Preview
painter.restore()
elif index.row() == 14 :
painter.save()
painter.fillRect(option.rect, QtGui.QBrush(QtGui.QColor(178, 114, 228))) # Ready
painter.restore()
elif index.row() == 15 :
painter.save()
painter.fillRect(option.rect, QtGui.QBrush(QtGui.QColor(147, 169, 236))) # For Delivery
painter.restore()
elif index.row() == 16 :
painter.save()
painter.fillRect(option.rect, QtGui.QBrush(QtGui.QColor(81, 50, 199))) # Delivered
painter.restore()
elif index.row() == 17 :
painter.save()
painter.fillRect(option.rect, QtGui.QBrush(QtGui.QColor(84, 95, 239))) # Approved
painter.restore()
elif index.row() == 18 :
painter.save()
painter.fillRect(option.rect, QtGui.QBrush(QtGui.QColor(239, 84, 84))) # Cancelled
painter.restore()
elif index.row() == 19 :
painter.save()
painter.fillRect(option.rect, QtGui.QBrush(QtGui.QColor(98, 81, 81))) # On Hold
painter.restore()
elif index.row() == 20 :
painter.save()
painter.fillRect(option.rect, QtGui.QBrush(QtGui.QColor(98, 1, 1))) # Got Issue
painter.restore()
QtWidgets.QStyledItemDelegate.paint(self, painter, option, index)
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.UI=uic.loadUi('VertigoCentral.ui', self)
self.show()
self.TYPE = ("UV","LD")
self.STATUS = ("Initializing ...","Not Started","Track","Track Approval",
"For Solving", "Solve", "For GeoBuild", "GeoBuild", "For Rotomation",
"Rotomation","Waiting Assets",
"For Packing", "Packing","Preview","Ready",
"For Delivery","Delivered","Approved",
"Cancelled","On Hold","Got Issue!")
app = QApplication(sys.argv)
window = MainWindow()
window.setFixedSize(1920, 1080)
for row in range(window.ShotTable.rowCount()):
window.ShotTable.setRowHeight(row,36)
scrollArea = QtWidgets.QScrollArea()
frmScroll = QtWidgets.QFrame(scrollArea)
cmbOption = MyQComboBox(frmScroll)
Status = cmbOption
Status.setObjectName("Status")
for index,it in enumerate(window.STATUS):
Status.addItem("")
Status.setItemText(index, it)
Status.setItemDelegate(StatusItemDelegate())
window.ShotTable.setCellWidget(row, 5, Status)
view = Status.view()
view.setFixedHeight(570)
window.ShotTable.setFocusPolicy(Qt.NoFocus)
window.show()
app.exec()
There are some issues with your implementation, starting with the creation of the index widget (both the scrollArea
and frmScroll
parents are useless), and painting issues (calling the base paint()
implementation will potentially paint over the previous fillRect()
).
Also, since you probably need to correlate the combo with the actual model data, using an index widget is not the more appropriate choice, and you should instead use a delegate on the table that will create the combo as editor for the cell.
A QComboBox uses a Qt model for its contents, and these models allow setting background and foreground roles that automatically draw items with the specified colors, if set.
While you can just use the combo setItemData()
function to set the colors, since you're going to use multiple instances of the same combo class, a better solution is to use a shared custom model.
Then, in order to also draw the combo with the selected color, you have two possibilities: either you override its paintEvent()
and "mimic" the default behavior by altering the palette, or you just use a specific stylesheet. For this case, I'd suggest the latter, as properly implementing the painting might be unnecessary difficult.
Finally, once the delegate for the table is implemented (by overriding its createEditor()
, setEditorData()
and setModelData()
functions) and set, you must call openPersistentEditor()
for each row of the model. For obvious reasons, whenever you need to add new rows, you must remember to call openPersistentEditor()
again (which could be done automatically if you connect to the table's model rowsInserted
signal).
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
StatusData = [
("Initializing ...", QColor(228, 221, 131)),
("Not Started", QColor(185, 183, 159)),
("Track", QColor(65, 203, 53)),
("Track Approval", QColor(38, 121, 31)),
("For Solving", QColor(31, 121, 104)),
("Solve", QColor(48, 216, 226)),
("For GeoBuild", QColor(226, 167, 48)),
("GeoBuild", QColor(226, 142, 48)),
("For Rotomation", QColor(243, 222, 108)),
("Rotomation", QColor(243, 141, 108)),
("Waiting Assets", QColor(154, 76, 79)),
("For Packing", QColor(76, 112, 154)),
("Packing", QColor(53, 89, 201)),
("Preview", QColor(114, 228, 118)),
("Ready", QColor(178, 114, 228)),
("For Delivery", QColor(147, 169, 236)),
("Delivered", QColor(81, 50, 199)),
("Approved", QColor(84, 95, 239)),
("Cancelled", QColor(239, 84, 84)),
("On Hold", QColor(98, 81, 81)),
("Got Issue!", QColor(98, 1, 1)),
]
StatusRole = Qt.UserRole + 1
def textColorForBackground(bgd):
'''
A simple helper function that returns a foreground color that will
always contrast to the background.
'''
if isinstance(bgd, QBrush):
bgd = bgd.color()
r, g, b, a = bgd.getRgb()
if (r * .3 + g * .5 + b * .1) < 100:
return QColor(Qt.white)
return QColor(Qt.black)
class MyQComboBox(QComboBox):
_model = None # a single model shared between instances
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if not self._model:
self.__class__._model = QStandardItemModel(0, 1)
for row, (text, color) in enumerate(StatusData):
item = QStandardItem(text)
item.setData(color, Qt.BackgroundRole)
item.setData(textColorForBackground(color), Qt.ForegroundRole)
self._model.appendRow(item)
self.setModel(self._model)
self.setFocusPolicy(Qt.StrongFocus)
self.currentIndexChanged.connect(self.updateBackground)
self.updateBackground(self.currentIndex())
def updateBackground(self, index):
if index < 0:
self.setStyleSheet('')
return
self.setStyleSheet('''
MyQComboBox {{
color: {foreground};
background: {background};
}}
'''.format(
foreground=self.itemData(index, Qt.ForegroundRole).name(),
background=self.itemData(index, Qt.BackgroundRole).name()
))
def wheelEvent(self, event):
return
def showPopup(self):
maxHeight = min(
s.availableGeometry().height() for s in QApplication.screens())
view = self.view()
model = view.model()
margins = view.parent().contentsMargins()
spacing = view.spacing()
heightHint = margins.top() + margins.bottom() - spacing
for row in range(self.count()):
itemHeight = view.visualRect(model.index(row, 0)).height() + spacing
if heightHint + itemHeight > maxHeight:
break
heightHint += itemHeight
view.parent().setFixedHeight(heightHint)
super().showPopup()
class StatusDelegate(QStyledItemDelegate):
def createEditor(self, parent, option, index):
def updateModel(comboIndex):
current = pIndex.data(StatusRole) or 0
if comboIndex != current:
self.commitData.emit(editor)
pIndex = QPersistentModelIndex(index)
editor = MyQComboBox(parent)
editor.clearFocus()
editor.currentIndexChanged.connect(updateModel)
return editor
def setEditorData(self, editor, index):
statusIndex = index.data(StatusRole)
if (
statusIndex is not None
and editor.currentIndex() != statusIndex
):
editor.setCurrentIndex(statusIndex)
def setModelData(self, editor, model, index):
current = index.data(StatusRole) or 0
editorIndex = editor.currentIndex()
if editorIndex != current:
model.setData(index, editorIndex, StatusRole)
if editorIndex > 0:
background = editor.itemData(editorIndex, Qt.BackgroundRole)
else:
background = None
model.setData(index, background, Qt.BackgroundRole)
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.UI=uic.loadUi('VertigoCentral.ui', self)
self.ShotTable.setFocusPolicy(Qt.NoFocus)
self.ShotTable.verticalHeader().setDefaultSectionSize(36)
self.ShotTable.horizontalHeader().setSectionResizeMode(
5, QHeaderView.ResizeToContents)
self.ShotTable.setItemDelegateForColumn(
5, StatusDelegate(self.ShotTable))
for row in range(self.ShotTable.rowCount()):
item = self.ShotTable.item(row, 5)
if not item:
item = QTableWidgetItem()
self.ShotTable.setItem(row, 5, item)
self.ShotTable.openPersistentEditor(item)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())