如何在不覆盖其他默认属性的情况下设置QTableView中复选框的样式属性
如何在不覆盖其他默认属性的情况下设置QTableView中复选框的样式属性
我太懂这种想给复选框改个小样式,结果牵一发而动全身的烦恼了!你想给QTableView里的复选框改边框颜色,但又不想丢掉默认的对勾和其他原生样式,确实得选对方法,下面咱们一步步说:
为啥直接用StyleSheet不太行?
你试过的setStyleSheet方法确实会踩坑——Qt的样式表对控件的绘制是「替换式」的,不是「增量式」的。当你给QTableView::indicator设置border属性时,相当于完全接管了复选框的绘制逻辑,默认的对勾、背景这些样式全没了,你得自己重新定义所有视觉元素,这成本太高了,完全没必要。
你之前写的这段代码就是典型的例子:
table_view.setStyleSheet( """ QTableView::indicator:unchecked { border: 2px solid red; } QTableView::indicator:checked { border: 2px solid green; } """ )
运行后确实能改边框,但对勾直接消失,还得自己想办法加回去,太折腾。
用QStyledItemDelegate:保留默认样式的折中方案
这种方法就灵活多了!我们可以自定义一个代理,先画我们想要的边框,再让Qt绘制默认的复选框,这样既保留了原生的对勾和样式,又能实现自定义边框。
你写的这个CheckboxDelegate就很靠谱,我理清楚它的核心逻辑:
- 先判断当前单元格是否是复选框类型(是否包含
CheckStateRole数据) - 构建
QStyleOptionButton来模拟复选框的状态(选中/未选中、启用状态) - 保存画家状态,设置自定义边框颜色后画出边框
- 调用系统样式绘制默认的复选框(这样对勾就保留了)
- 恢复画家状态,避免影响其他单元格的绘制
完整的代理代码:
class CheckboxDelegate(QStyledItemDelegate): def __init__(self, border_colour): super().__init__() self.border_colour = border_colour def paint(self, painter, option, index): if index.data(Qt.CheckStateRole) is not None: checkbox_option = QStyleOptionButton() checkbox_option.state |= QStyle.State_Enabled checkbox_option.state |= ( QStyle.State_On if index.data(Qt.CheckStateRole) == Qt.Checked else QStyle.State_Off ) # 绘制自定义边框和复选框 checkbox_option.rect = option.rect painter.save() painter.setPen(QColor(self.border_colour)) painter.drawRect(option.rect) QApplication.style().drawControl(QStyle.CE_CheckBox, checkbox_option, painter) painter.restore() else: super().paint(painter, option, index)
使用的时候也很简单,给指定列设置代理就行:
delegate = CheckboxDelegate("red") table_view.setItemDelegateForColumn(1, delegate)
完整可运行代码
下面是整合后的完整代码,你可以直接运行测试,里面注释了两种方法,方便你切换对比:
from PySide6.QtCore import Qt, QAbstractTableModel from PySide6.QtWidgets import ( QStyledItemDelegate, QStyle, QStyleOptionButton, QApplication, QTableView, ) from PySide6.QtGui import QColor class CheckboxDelegate(QStyledItemDelegate): def __init__(self, border_colour): super().__init__() self.border_colour = border_colour def paint(self, painter, option, index): if index.data(Qt.CheckStateRole) is not None: checkbox_option = QStyleOptionButton() checkbox_option.state |= QStyle.State_Enabled checkbox_option.state |= ( QStyle.State_On if index.data(Qt.CheckStateRole) == Qt.Checked else QStyle.State_Off ) # 绘制自定义边框和复选框 checkbox_option.rect = option.rect painter.save() painter.setPen(QColor(self.border_colour)) painter.drawRect(option.rect) QApplication.style().drawControl(QStyle.CE_CheckBox, checkbox_option, painter) painter.restore() else: super().paint(painter, option, index) class TableModel(QAbstractTableModel): def __init__(self, data, headers, parent=None): super().__init__(parent) self._data = data self._headers = headers def rowCount(self, parent=None): return len(self._data) def columnCount(self, parent=None): return len(self._headers) def data(self, index, role): if not index.isValid(): return None value = self._data[index.row()][index.column()] if role == Qt.DisplayRole: return value if role == Qt.CheckStateRole and index.column() == 1: return Qt.Checked if value else Qt.Unchecked return None def headerData(self, section, orientation, role): if role == Qt.DisplayRole: if orientation == Qt.Horizontal: return self._headers[section] if orientation == Qt.Vertical: return f"Row {section + 1}" return None def setData(self, index, value, role): if not index.isValid(): return False if role == Qt.CheckStateRole and index.column() == 1: self._data[index.row()][index.column()] = bool(value) self.dataChanged.emit(index, index, [role]) return True return False def flags(self, index): if not index.isValid(): return Qt.NoItemFlags if index.column() == 1: return Qt.ItemIsEnabled | Qt.ItemIsUserCheckable return Qt.ItemIsEnabled if __name__ == "__main__": import sys app = QApplication(sys.argv) data = [ ["Row1", True], ["Row2", False], ["Row3", True], ] headers = ["Text", "Checkbox"] model = TableModel(data, headers) table_view = QTableView() table_view.setModel(model) # # 方法1:直接用StyleSheet(会丢失对勾,需自定义完整样式) # table_view.setStyleSheet( # """ # QTableView::indicator:unchecked { # border: 2px solid red; # } # QTableView::indicator:checked { # border: 2px solid green; # } # """ # ) # 方法2:用自定义Delegate(保留默认对勾,仅修改边框) delegate = CheckboxDelegate("red") table_view.setItemDelegateForColumn(1, delegate) table_view.show() sys.exit(app.exec())
备注:内容来源于stack exchange,提问作者Louie Atkins-Turkish
相关产品推荐
相关产品推荐

