You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过QItemDelegate为QTableView指定行添加粗下划线?

问题:根据表格首列内容为整行添加粗下划线

我需要实现:根据每行第一列的内容(如'revenue avg 3'、'tangible book'),为对应整行的所有单元格文本添加粗下划线。尝试用QItemDelegate实现,但当前自定义Delegate完全无效,不知道问题出在哪。

原代码如下:

模型与视图代码

import pandas as pd
from PySide6 import QtCore, QtWidgets
from PySide6.QtCore import Qt
from PySide6.QtGui import QColor, QPen, QBrush
from PySide6.QtWidgets import QStyle


class TickerTableModel(QtCore.QAbstractTableModel):

    def __init__(self,  parent=None, *args):
        super(TickerTableModel,  self).__init__(parent,  *args)
        self._filters = {}
        self._sortBy = []
        self._sortDirection = []
        self._dfSource = pd.DataFrame()
        self._dfDisplay = pd.DataFrame()

    def rowCount(self,  parent=QtCore.QModelIndex()):
        if parent.isValid():
            return 0
        return self._dfDisplay.shape[0]

    def columnCount(self,  parent=QtCore.QModelIndex()):
        if parent.isValid():
            return 0
        return self._dfDisplay.shape[1]

    def data(self, index, role):
        if index.isValid() and role == QtCore.Qt.ItemDataRole.DisplayRole:
            return self._dfDisplay.values[index.row()][index.column()]
        return None

    def headerData(self, col, orientation=QtCore.Qt.Orientation.Horizontal, role=QtCore.Qt.ItemDataRole.DisplayRole):
        if orientation == QtCore.Qt.Orientation.Horizontal and role == QtCore.Qt.ItemDataRole.DisplayRole:
            return str(self._dfDisplay.columns[col])
        return None

    def setupModel(self, data):
        self._dfSource = data
        self._sortBy = []
        self._sortDirection = []
        self.setFilters({})

    def setFilters(self, filters):
        self.modelAboutToBeReset.emit()
        self._filters = filters
        self.updateDisplay()
        self.modelReset.emit()

    def updateDisplay(self):

        dfDisplay = self._dfSource.copy()

        # Filtering
        cond = pd.Series(True, index=dfDisplay.index)
        for column, value in self._filters.items():
            cond = cond & \
                   (dfDisplay[column].str.lower().str.find(str(value).lower()) >= 0)
        dfDisplay = dfDisplay[cond]

        # Sorting
        if len(self._sortBy) != 0:
            dfDisplay.sort_values(by=self._sortBy,
                                  ascending=self._sortDirection,
                                  inplace=True)

        # Updating
        self._dfDisplay = dfDisplay


class TickerTableView(QtWidgets.QTableView):
    def setupUi(self, TickerTableWidget):
        self.verticalLayout = QtWidgets.QVBoxLayout(TickerTableWidget)
        self.tableView = QtWidgets.QTableView(TickerTableWidget)
        self.tableView.setSortingEnabled(True)
        self.tableView.setAlternatingRowColors(True)
        self.verticalLayout.addWidget(self.tableView)
        QtCore.QMetaObject.connectSlotsByName(TickerTableWidget)


class TickerTableWidget(QtWidgets.QWidget):
    def __init__(self, data, parent=None):
        super(TickerTableWidget, self).__init__(parent)

        self.ui = TickerTableView()
        self.ui.setupUi(self)

        self.tableModel = TickerTableModel()
        self.tableModel.setupModel(data)
        self.ui.tableView.setModel(self.tableModel)

        # Set delegate here
        self.delegate = TickerTableDelegate(self.ui)
        self.ui.setItemDelegate(self.delegate)


class TickerTableDelegate(QtWidgets.QItemDelegate):
    def __init__(self,  parent=None, *args):
        QtWidgets.QItemDelegate.__init__(self, parent, *args)

    def paint(self, painter, option, index):
        painter.save()

        # set text color
        painter.setPen(QPen(Qt.black))
        text = index.data(Qt.DisplayRole)

        if text:
            painter.setPen(QPen(Qt.red))

        painter.drawText(option.rect, Qt.AlignCenter, text)
        painter.restore()

主函数代码

if __name__ == '__main__':
    app = QApplication(sys.argv)
    df = pd.DataFrame({'revenue avg 3' : [1,5,10], 'profit': [5, 4, 5], 'income': [3,5,6]})
    window = TickerTableWidget(df)
    window.show()
    sys.exit(app.exec())

问题分析与修复

1. Delegate设置错误

原代码中self.ui.setItemDelegate(self.delegate)是给TickerTableView实例设置Delegate,但实际需要给内部的tableView控件设置,应该改为:

self.ui.tableView.setItemDelegate(self.delegate)

2. Delegate的paint方法未实现下划线逻辑

修改TickerTableDelegate的paint方法,先获取当前行第0列的内容,判断是否需要添加下划线,同时保留表格原有样式:

class TickerTableDelegate(QtWidgets.QItemDelegate):
    def __init__(self,  parent=None, *args):
        super().__init__(parent, *args)
        # 定义需要添加下划线的首列关键词
        self.target_labels = ['revenue avg 3']

    def paint(self, painter, option, index):
        painter.save()

        # 获取当前行第0列的内容
        row = index.row()
        first_col_index = index.model().index(row, 0)
        first_col_text = index.model().data(first_col_index, Qt.DisplayRole)

        # 获取单元格文本
        text = index.data(Qt.DisplayRole) or ""

        # 保留默认的背景和焦点样式(选中、交替行颜色等)
        self.drawBackground(painter, option, index)
        self.drawFocus(painter, option, option.rect)

        # 设置画笔:目标行启用粗下划线
        pen = QPen(Qt.black)
        if first_col_text in self.target_labels:
            pen.setUnderline(True)
            pen.setWeight(2)  # 设置下划线粗细
        painter.setPen(pen)

        # 绘制居中文本
        painter.drawText(option.rect, Qt.AlignCenter, str(text))

        painter.restore()

修改后的完整代码

import pandas as pd
from PySide6 import QtCore, QtWidgets
from PySide6.QtCore import Qt
from PySide6.QtGui import QColor, QPen, QBrush
from PySide6.QtWidgets import QStyle


class TickerTableModel(QtCore.QAbstractTableModel):

    def __init__(self,  parent=None, *args):
        super(TickerTableModel,  self).__init__(parent,  *args)
        self._filters = {}
        self._sortBy = []
        self._sortDirection = []
        self._dfSource = pd.DataFrame()
        self._dfDisplay = pd.DataFrame()

    def rowCount(self,  parent=QtCore.QModelIndex()):
        if parent.isValid():
            return 0
        return self._dfDisplay.shape[0]

    def columnCount(self,  parent=QtCore.QModelIndex()):
        if parent.isValid():
            return 0
        return self._dfDisplay.shape[1]

    def data(self, index, role):
        if index.isValid() and role == QtCore.Qt.ItemDataRole.DisplayRole:
            return self._dfDisplay.values[index.row()][index.column()]
        return None

    def headerData(self, col, orientation=QtCore.Qt.Orientation.Horizontal, role=QtCore.Qt.ItemDataRole.DisplayRole):
        if orientation == QtCore.Qt.Orientation.Horizontal and role == QtCore.Qt.ItemDataRole.DisplayRole:
            return str(self._dfDisplay.columns[col])
        return None

    def setupModel(self, data):
        self._dfSource = data
        self._sortBy = []
        self._sortDirection = []
        self.setFilters({})

    def setFilters(self, filters):
        self.modelAboutToBeReset.emit()
        self._filters = filters
        self.updateDisplay()
        self.modelReset.emit()

    def updateDisplay(self):

        dfDisplay = self._dfSource.copy()

        # Filtering
        cond = pd.Series(True, index=dfDisplay.index)
        for column, value in self._filters.items():
            cond = cond & \
                   (dfDisplay[column].str.lower().str.find(str(value).lower()) >= 0)
        dfDisplay = dfDisplay[cond]

        # Sorting
        if len(self._sortBy) != 0:
            dfDisplay.sort_values(by=self._sortBy,
                                  ascending=self._sortDirection,
                                  inplace=True)

        # Updating
        self._dfDisplay = dfDisplay


class TickerTableView(QtWidgets.QTableView):
    def setupUi(self, TickerTableWidget):
        self.verticalLayout = QtWidgets.QVBoxLayout(TickerTableWidget)
        self.tableView = QtWidgets.QTableView(TickerTableWidget)
        self.tableView.setSortingEnabled(True)
        self.tableView.setAlternatingRowColors(True)
        self.verticalLayout.addWidget(self.tableView)
        QtCore.QMetaObject.connectSlotsByName(TickerTableWidget)


class TickerTableWidget(QtWidgets.QWidget):
    def __init__(self, data, parent=None):
        super(TickerTableWidget, self).__init__(parent)

        self.ui = TickerTableView()
        self.ui.setupUi(self)

        self.tableModel = TickerTableModel()
        self.tableModel.setupModel(data)
        self.ui.tableView.setModel(self.tableModel)

        # 修复:给tableView设置Delegate
        self.delegate = TickerTableDelegate(self.ui)
        self.ui.tableView.setItemDelegate(self.delegate)


class TickerTableDelegate(QtWidgets.QItemDelegate):
    def __init__(self,  parent=None, *args):
        super().__init__(parent, *args)
        # 定义需要添加下划线的首列关键词
        self.target_labels = ['revenue avg 3']

    def paint(self, painter, option, index):
        painter.save()

        # 获取当前行第0列的内容
        row = index.row()
        first_col_index = index.model().index(row, 0)
        first_col_text = index.model().data(first_col_index, Qt.DisplayRole)

        # 获取单元格文本
        text = index.data(Qt.DisplayRole) or ""

        # 保留默认的背景和焦点样式
        self.drawBackground(painter, option, index)
        self.drawFocus(painter, option, option.rect)

        # 设置画笔样式
        pen = QPen(Qt.black)
        if first_col_text in self.target_labels:
            pen.setUnderline(True)
            pen.setWeight(2)  # 加粗下划线
        painter.setPen(pen)

        # 绘制居中文本
        painter.drawText(option.rect, Qt.AlignCenter, str(text))

        painter.restore()


if __name__ == '__main__':
    import sys
    from PySide6.QtWidgets import QApplication
    app = QApplication(sys.argv)
    df = pd.DataFrame({'revenue avg 3' : [1,5,10], 'profit': [5, 4, 5], 'income': [3,5,6]})
    window = TickerTableWidget(df)
    window.show()
    sys.exit(app.exec())

修改后,首列内容为'revenue avg 3'的整行都会显示粗下划线文本,同时保留表格原有选中、交替行颜色等样式,Delegate可正常生效。

内容的提问来源于stack exchange,提问作者David Frick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 16:45:57