基于父 QTableWidget 的 :selected 伪状态对 QTableWidget 的 cellWidget 进行样式化
Styling a cellWidget of a QTableWidget based on the :selected pseudo-state of the parent QTableWidget
我有一个带有一些行的 QTableWidget。
在每一行中,其中一个单元格通过 setCellWidget 设置了另一个小部件。
我想根据行是否被选中来设置此 cellWidget 的样式。作为参考,cellWidget 是另一个 QTableWidget,但它是 disabled/not editable 并且本质上是只读的。
我找到了访问子控件的语法(特别是访问父 QTableWidget 的项目)——即 MainTable::item
https://doc.qt.io/qt-5/stylesheet-reference.html#list-of-sub-controls
我还找到了(更标准的)css-用于访问控件伪状态的语法——即 MainTable::item:selected
。 https://doc.qt.io/qt-5/stylesheet-reference.html#list-of-pseudo-states
如果我天真地使用它来将所选项目(table行)设置为黄色,如下所示
def add_file(self, row, element):
"""populate a new row in the table"""
# self is the parent QTableWidget
self.setRowHeight(row, self.ICON_SIZE.height())
img = self.create_thumbnail(element['filepath'])
# add an image to the first column
item = QTableWidgetItem("",0)
item.setFlags(Qt.ItemIsEnabled|Qt.ItemIsSelectable)
item.setData(Qt.DecorationRole, img)
item.setData(Qt.TextAlignmentRole, Qt.AlignHCenter|Qt.AlignCenter)
item.setData(Qt.SizeHintRole, self.ICON_SIZE)
self.setItem(row, self.THUMBCOL, item)
# StatsTable is another nested QTableWidget
stats = StatsTable(element)
# add the new nested table to the outer main tables second column
self.setCellWidget(row, self.STATSCOL, stats)
self.setStyleSheet("""
MainTable::item:selected
{
background: yellow;
color: purple;
}
""")
除了 cellWidget 之外的整行都将具有黄色背景。
现在,如果我修改 QSS 选择器以尝试访问子小部件,我会得到意想不到的结果:
MainTable::item:selected QTableWidget
{
background: yellow;
color: purple;
}
这导致每一行的 cellWidget-table 都被赋予黄色背景,而与该行的选择状态无关(不像之前只有选定的行没有嵌套的 table 有一个黄色背景)。
我在这里忽略了一些简单的事情,还是我必须创建一些回调以在选择行时手动应用和取消应用样式?
这是应用了第一个 QSS 的选定行
这是应用了第二个 QSS 的选定行
如果选中该行,这两者都没有设置 cellWidget 样式。
parenthood选择器不能使用subcontrol和pseudo元素,如果选择了whole行,也无法根据选择设置特定item的背景。
项视图的背景使用 Base
palette color role 绘制,通常是白色不透明的。您可以做的是覆盖它并使其透明:
def add_file(self, row, element):
# ...
palette = stats.palette()
palette.setColor(palette.Base, QtCore.Qt.transparent)
stats.setPalette(palette)
遗憾的是,这只会修复背景部分,不会改变显示文本的颜色。为此,您需要了解选择的状态并根据项目选择更新样式表。
您可以连接到 QTableWidget 的 selectionChanged
of the main table's selectionModel()
(or itemSelectionChanged
),然后相应地设置项目样式:
# somewhere in the __init__
self.TableQSS = '''
QTableWidget
{
background: yellow;
color: purple;
}
'''
self.itemSelectionChanged.connect(self.updateTables)
def updateTables(self):
selected = self.selectedIndexes()
for row in range(self.rowCount()):
table = self.cellWidget(row, self.STATSCOL)
if not isinstance(table, StatsTable):
continue
if self.model().index(row, self.STATSCOL) in selected:
table.setStyleSheet(self.TableQSS)
else:
table.setStyleSheet('')
考虑到样式表和调色板并不总能很好地协同工作,设置调色板颜色通常是首选解决方案,因为它(理论上)更安全,当前样式将使用调色板定义其他颜色,例如渐变,阴影等
所以,继续按照开头解释的方式设置调色板,仍然如上连接itemSelectionChanged
信号,然后:
def updateTables(self):
# get the default colors for the text from the palette of the main
# table (we cannot rely on the child tables as they've been changed)
basePalette = self.palette()
colors = [basePalette.color(cg, basePalette.Text) for cg in range(3)]
selected = self.selectedIndexes()
for row in range(self.rowCount()):
table = self.cellWidget(row, self.STATSCOL)
if not isinstance(table, StatsTable):
continue
palette = table.palette()
if self.model().index(row, self.STATSCOL) in selected:
palette.setColor(palette.Text, QtGui.QColor('purple'))
else:
# restore default colors
for cg, color in enumerate(colors):
palette.setColor(cg, palette.Text, color)
table.setPalette(palette)
请注意,使用嵌套项目视图通常不是一个好主意,因为它会使事情变得更加复杂(尤其是选择和 keyboard/mouse 交互)并且在某些情况下可能会产生问题。
既然您似乎只需要显示数据,您应该考虑实现自己的项目委托(参见 QStyledItemDelegate) and eventually draw formatted text using a basic HTML table (see )。
或者,使用禁用滚动条的 QPlainTextEdit 并设置为只读模式(在这种情况下,您仍然需要执行上述操作)。
作为使用项目委托的替代方法,我向 itemSelectionChanged
信号添加了回调,并遍历主 table 中的行。我根据是否选择了该行,在 child-table 小部件上设置了一个 属性 值。此 属性 在样式表中访问。
不幸的是,我似乎必须通过整体设置样式表来强制重新计算它,所以看似聪明的解决方法实际上并不是那么聪明。
由于我的嵌套小部件非常受限(只读、禁用因此无法导航到...)我认为我不需要自定义项委托的灵活性,即使它可能更好解决方案。我还希望少于 100 行,因此性能可能不是问题。
def __init__(self, ...):
...
# called whenever the main table has its selected row(s) change.
self.itemSelectionChanged.connect(self.update_selection)
def update_selection(self):
for row in range(self.rowCount()):
item = self.item(row, 0)
widg = self.cellWidget(row, 1)
if item.isSelected():
widg.setProperty("row_is_selected", "true")
else:
widg.setProperty("row_is_selected", "false")
# it is apparently necessary to force a recalculation anyway so the
# above property-change is a roundabout way to adjust the style
# compared to just setting or removing it below.
# this forces a recalculation nonetheless.
widg.setStyleSheet(widg.styleSheet())
def add_file(self, row, element):
...
stats.setProperty("row_is_selected", "false")
self.setStyleSheet("""
StatsTable[row_is_selected="true"]
{
background: yellow;
color: purple;
}
""")
我有一个带有一些行的 QTableWidget。
在每一行中,其中一个单元格通过 setCellWidget 设置了另一个小部件。
我想根据行是否被选中来设置此 cellWidget 的样式。作为参考,cellWidget 是另一个 QTableWidget,但它是 disabled/not editable 并且本质上是只读的。
我找到了访问子控件的语法(特别是访问父 QTableWidget 的项目)——即 MainTable::item
https://doc.qt.io/qt-5/stylesheet-reference.html#list-of-sub-controls
我还找到了(更标准的)css-用于访问控件伪状态的语法——即 MainTable::item:selected
。 https://doc.qt.io/qt-5/stylesheet-reference.html#list-of-pseudo-states
如果我天真地使用它来将所选项目(table行)设置为黄色,如下所示
def add_file(self, row, element):
"""populate a new row in the table"""
# self is the parent QTableWidget
self.setRowHeight(row, self.ICON_SIZE.height())
img = self.create_thumbnail(element['filepath'])
# add an image to the first column
item = QTableWidgetItem("",0)
item.setFlags(Qt.ItemIsEnabled|Qt.ItemIsSelectable)
item.setData(Qt.DecorationRole, img)
item.setData(Qt.TextAlignmentRole, Qt.AlignHCenter|Qt.AlignCenter)
item.setData(Qt.SizeHintRole, self.ICON_SIZE)
self.setItem(row, self.THUMBCOL, item)
# StatsTable is another nested QTableWidget
stats = StatsTable(element)
# add the new nested table to the outer main tables second column
self.setCellWidget(row, self.STATSCOL, stats)
self.setStyleSheet("""
MainTable::item:selected
{
background: yellow;
color: purple;
}
""")
除了 cellWidget 之外的整行都将具有黄色背景。
现在,如果我修改 QSS 选择器以尝试访问子小部件,我会得到意想不到的结果:
MainTable::item:selected QTableWidget
{
background: yellow;
color: purple;
}
这导致每一行的 cellWidget-table 都被赋予黄色背景,而与该行的选择状态无关(不像之前只有选定的行没有嵌套的 table 有一个黄色背景)。
我在这里忽略了一些简单的事情,还是我必须创建一些回调以在选择行时手动应用和取消应用样式?
这是应用了第一个 QSS 的选定行
这是应用了第二个 QSS 的选定行
如果选中该行,这两者都没有设置 cellWidget 样式。
parenthood选择器不能使用subcontrol和pseudo元素,如果选择了whole行,也无法根据选择设置特定item的背景。
项视图的背景使用 Base
palette color role 绘制,通常是白色不透明的。您可以做的是覆盖它并使其透明:
def add_file(self, row, element):
# ...
palette = stats.palette()
palette.setColor(palette.Base, QtCore.Qt.transparent)
stats.setPalette(palette)
遗憾的是,这只会修复背景部分,不会改变显示文本的颜色。为此,您需要了解选择的状态并根据项目选择更新样式表。
您可以连接到 QTableWidget 的 selectionChanged
of the main table's selectionModel()
(or itemSelectionChanged
),然后相应地设置项目样式:
# somewhere in the __init__
self.TableQSS = '''
QTableWidget
{
background: yellow;
color: purple;
}
'''
self.itemSelectionChanged.connect(self.updateTables)
def updateTables(self):
selected = self.selectedIndexes()
for row in range(self.rowCount()):
table = self.cellWidget(row, self.STATSCOL)
if not isinstance(table, StatsTable):
continue
if self.model().index(row, self.STATSCOL) in selected:
table.setStyleSheet(self.TableQSS)
else:
table.setStyleSheet('')
考虑到样式表和调色板并不总能很好地协同工作,设置调色板颜色通常是首选解决方案,因为它(理论上)更安全,当前样式将使用调色板定义其他颜色,例如渐变,阴影等
所以,继续按照开头解释的方式设置调色板,仍然如上连接itemSelectionChanged
信号,然后:
def updateTables(self):
# get the default colors for the text from the palette of the main
# table (we cannot rely on the child tables as they've been changed)
basePalette = self.palette()
colors = [basePalette.color(cg, basePalette.Text) for cg in range(3)]
selected = self.selectedIndexes()
for row in range(self.rowCount()):
table = self.cellWidget(row, self.STATSCOL)
if not isinstance(table, StatsTable):
continue
palette = table.palette()
if self.model().index(row, self.STATSCOL) in selected:
palette.setColor(palette.Text, QtGui.QColor('purple'))
else:
# restore default colors
for cg, color in enumerate(colors):
palette.setColor(cg, palette.Text, color)
table.setPalette(palette)
请注意,使用嵌套项目视图通常不是一个好主意,因为它会使事情变得更加复杂(尤其是选择和 keyboard/mouse 交互)并且在某些情况下可能会产生问题。
既然您似乎只需要显示数据,您应该考虑实现自己的项目委托(参见 QStyledItemDelegate) and eventually draw formatted text using a basic HTML table (see
或者,使用禁用滚动条的 QPlainTextEdit 并设置为只读模式(在这种情况下,您仍然需要执行上述操作)。
作为使用项目委托的替代方法,我向 itemSelectionChanged
信号添加了回调,并遍历主 table 中的行。我根据是否选择了该行,在 child-table 小部件上设置了一个 属性 值。此 属性 在样式表中访问。
不幸的是,我似乎必须通过整体设置样式表来强制重新计算它,所以看似聪明的解决方法实际上并不是那么聪明。
由于我的嵌套小部件非常受限(只读、禁用因此无法导航到...)我认为我不需要自定义项委托的灵活性,即使它可能更好解决方案。我还希望少于 100 行,因此性能可能不是问题。
def __init__(self, ...):
...
# called whenever the main table has its selected row(s) change.
self.itemSelectionChanged.connect(self.update_selection)
def update_selection(self):
for row in range(self.rowCount()):
item = self.item(row, 0)
widg = self.cellWidget(row, 1)
if item.isSelected():
widg.setProperty("row_is_selected", "true")
else:
widg.setProperty("row_is_selected", "false")
# it is apparently necessary to force a recalculation anyway so the
# above property-change is a roundabout way to adjust the style
# compared to just setting or removing it below.
# this forces a recalculation nonetheless.
widg.setStyleSheet(widg.styleSheet())
def add_file(self, row, element):
...
stats.setProperty("row_is_selected", "false")
self.setStyleSheet("""
StatsTable[row_is_selected="true"]
{
background: yellow;
color: purple;
}
""")