I set 4th row color to red of the QTableWidget
, When I select an item and then click the QLineEdit
, the Item
color had lose? Can someone solve this?
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtNetwork import *
from PyQt5.QtGui import *
import random
class Win(QWidget):
def __init__(self):
super().__init__()
self.le = QLineEdit()
self.table = QTableWidget()
self.table.setHorizontalHeaderLabels(list("ABCD"))
self.table.setColumnCount(4)
self.table.setRowCount(10)
for row in range(self.table.rowCount()):
for col in range(self.table.columnCount()):
it = QTableWidgetItem( chr(random.randint(11300, 21300) ) )
self.table.setItem(row, col, it)
row = 3
for col in range(self.table.columnCount()):
self.table.item(row, col).setBackground(QColor(Qt.red))
lay = QVBoxLayout()
lay.addWidget(self.le)
lay.addWidget(self.table)
self.setLayout(lay)
app = QApplication([])
win = Win()
win.show()
app.exec()
No, the color is not lost.
What you're seeing is the inactive selection color: if you look more carefully, it has not the same color as the other items.
There are various possibilities, then.
This is the easiest solution, but has a drawback: the table will look focused even if it's not, since the selected items will always have the same color.
p = self.table.palette()
p.setBrush(p.Inactive, p.Highlight, p.brush(p.Highlight))
self.table.setPalette(p)
This is slightly better than the above, but still not perfect: the selection should always be shown, even with a different color when the view is not focused, but with the following there is no way to tell which item is selected until the view gets focused again.
To do so, we can use an item delegate and just clear the State_Selected
flag from the option if the view is not active:
class MyDelegate(QStyledItemDelegate):
def initStyleOption(self, opt, index):
super().initStyleOption(opt, index)
if (opt.state & QStyle.State_Selected
and not opt.state & QStyle.State_Active
and index.data(Qt.BackgroundRole) is not None
):
opt.state &= ~QStyle.State_Selected
# ...
self.table.setItemDelegate(MyDelegate(self.table))
In this case, we blend the background color with the palette selection color, overlaying the Highlight
color by a factor of half its alpha channel. Note that this might not work in certain systems that don't directly use the palette for painting.
class MyDelegate(QStyledItemDelegate):
def initStyleOption(self, opt, index):
super().initStyleOption(opt, index)
if opt.state & QStyle.State_Selected:
bgd = index.data(Qt.BackgroundRole)
if bgd is None or not bgd.isOpaque():
return
r1, g1, b1, a1 = bgd.color().getRgbF()
if opt.widget and opt.widget.isEnabled():
if opt.state & QStyle.State_Active:
group = QPalette.Active
else:
group = QPalette.Inactive
else:
group = QPalette.Disabled
r2, g2, b2, a2 = opt.palette.color(group, QPalette.Highlight).getRgbF()
a2 *= .5
factor = (1 - a2) * a1
a = factor + a2
r = (factor * r1 + a2 * r2) / a
g = (factor * g1 + a2 * g2) / a
b = (factor * b1 + a2 * b2) / a
opt.palette.setColor(
QPalette.Highlight, QColor.fromRgbF(r, g, b, a))
In case the solution above won't work, the only remaining alternative is to override the painting of the item. For this situation, the simplest option is to draw the base of the item in an "unselected" state (showing the default background), then draw it again with the selected state but with half opacity, and finally draw the item contents clearing any possible background.
class MyDelegate(QStyledItemDelegate):
def paint(self, qp, opt, index):
if (opt.state & QStyle.State_Selected
and index.data(Qt.BackgroundRole) is not None
):
opt = QStyleOptionViewItem(opt)
self.initStyleOption(opt, index)
widget = opt.widget
style = widget.style() if widget else QApplication.style()
opt.state &= ~style.State_Selected
style.drawPrimitive(style.PE_PanelItemViewItem, opt, qp, widget)
qp.save()
qp.setOpacity(.5)
opt.state |= style.State_Selected
style.drawPrimitive(style.PE_PanelItemViewItem, opt, qp, widget)
qp.restore()
opt.palette.setColor(QPalette.Highlight, Qt.transparent)
opt.backgroundBrush = QBrush(Qt.NoBrush)
style.drawControl(style.CE_ItemViewItem, opt, qp, widget)
else:
super().paint(qp, opt, index)