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

PySide2自定义QTableView插入数据后隐藏列复现问题求助

问题解决:插入数据后隐藏列重新显示的处理

问题背景

使用QAbstractTableModel与QStyledItemDelegate实现QTableView,模型数据包含name、age、sex、phone number四个维度,希望表格以3列展示:将name和age合并到同一列,其余两列单独显示。已通过setColumnHidden(1, True)隐藏age对应的列1,委托的paint方法也实现了列0同时显示name和age的效果,但插入新数据后,隐藏的列1会重新显示。

解决方案

方案1:快速修复(视图层面)

问题根源是模型更新数据后,视图会重新同步列状态,之前的隐藏设置被覆盖。可以通过监听模型的rowsInserted信号,每次插入新行后重新隐藏列1。

修改MyTableView类,添加信号监听逻辑:

class MyTableView(QTableView):
    def __init__(self):
        super().__init__()
        self.verticalHeader().setDefaultSectionSize(40)
        self.horizontalHeader().setVisible(False)
        self.verticalHeader().setVisible(False)
        self.setFixedWidth(300)

    def setModel(self, model):
        super().setModel(model)
        # 初始隐藏列1
        self.setColumnHidden(1, True)
        # 监听模型行插入信号,重新隐藏列1
        model.rowsInserted.connect(lambda: self.setColumnHidden(1, True))

方案2:更优解(模型层面,符合MVC设计)

让模型直接提供视图需要的3列数据结构,避免视图层面的隐藏操作,更符合模型-视图-委托的职责划分:

  • 模型的columnCount返回3
  • 在data方法中映射原数据的字段到视图的3列,同时通过自定义角色给委托提供age数据

修改后的MyTableModel类:

class MyTableModel(QAbstractTableModel):
    def __init__(self, mydata):
        super().__init__()
        self._tabledata = mydata

    def rowCount(self, parent: QModelIndex = ...) -> int:
        return len(self._tabledata)

    def columnCount(self, parent: QModelIndex = ...) -> int:
        # 直接返回视图需要的3列
        return 3

    def data(self, index: QModelIndex, role: int = ...):
        if not index.isValid():
            return None
        if index.row() >= self.rowCount():
            return None
        row_data = self._tabledata[index.row()]
        if role == Qt.DisplayRole or role == Qt.EditRole:
            if index.column() == 0:
                return row_data[0]  # 返回name
            elif index.column() == 1:
                return row_data[2]  # 对应sex
            elif index.column() == 2:
                return row_data[3]  # 对应phone number
        elif role == Qt.UserRole + 1:
            # 自定义角色返回age,供委托获取
            if index.column() == 0:
                return row_data[1]
        return None

    def insertNewRow(self):
        self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
        self._tabledata.append(['test','20','female', 234])
        self.endInsertRows()

同时修改委托中获取age的逻辑,从自定义角色获取:

# 替换原代码中获取age的部分
elidedstr = fm.elidedText(index.model().data(index, Qt.UserRole + 1), Qt.ElideRight, colWid -10)

这样视图不需要隐藏任何列,模型直接提供3列数据,彻底避免列状态重置的问题。

完整修改后的代码(方案1快速修复版)

import sys
from PySide2.QtWidgets import QApplication,QTableView, QStyledItemDelegate,QStyleOptionViewItem,QStyle, QPushButton,QWidget,QVBoxLayout
from PySide2.QtCore import Qt, QAbstractTableModel, QModelIndex
from PySide2.QtGui import QColor, QTextDocument, QPainter,QFont,QAbstractTextDocumentLayout, QPalette,QTextCursor,QTextCharFormat, QTextBlockFormat,QFontMetrics


class MyTableModel(QAbstractTableModel):

    def __init__(self, mydata):
        super().__init__()
        self._tabledata = mydata

    def rowCount(self, parent: QModelIndex = ...) -> int:
        return len(self._tabledata)

    def columnCount(self, parent: QModelIndex = ...) -> int:
        return len(self._tabledata[0] if self._tabledata else [])

    def data(self, index: QModelIndex, role: int = ...):
        if not index.isValid():
            return None
        if index.row() >= self.rowCount():
            return None
        if role == Qt.DisplayRole or role == Qt.EditRole:
            return self._tabledata[index.row()][index.column()]

        else:
            return None

    def insertNewRow(self):
        self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
        self._tabledata.append(['test','20','female', 234])
        self.endInsertRows()

class TableDelegate(QStyledItemDelegate):

    def paint(self, painter, option, index):
        if index.column() in [0, 3, 2]:
            painter.save()
            painter.setRenderHint(QPainter.Antialiasing, True)
            widget = option.widget
            style = widget.style()         
            style.drawControl(style.CE_ItemViewItem, option, painter, widget)   
            painter.restore()

        if index.column() == 0:
            
            self.doc = QTextDocument(self)
            colWid = option.rect.width()
            painter.save()
            painter.setPen(QColor(244,243,243,255))
            painter.drawLine(option.rect.bottomLeft(), option.rect.bottomRight())
            
            options = QStyleOptionViewItem(option)
            self.initStyleOption(options, index)
            self.doc.setPlainText('')
  
            cursor = QTextCursor(self.doc)
            fmt = QTextCharFormat()
            fmt.setFontFamily('Arial')
            fmt.setFontWeight(QFont.Bold)
            fmt.setFontPointSize(10)
            serifFont = QFont("Arial", 10, QFont.Bold)
            fm = QFontMetrics(serifFont)
            cursor.setCharFormat(fmt)
            cursor.beginEditBlock()
            cursor.insertText(fm.elidedText(options.text, Qt.ElideRight, colWid -10))
        
            cursor.endEditBlock()
            tbm = QTextBlockFormat()
            tbm.setTopMargin(10)
            cursor.insertBlock(tbm)
            cursor.beginEditBlock()
            fmt.setBackground(Qt.transparent)
            fmt.setFontWeight(QFont.Normal)
            fmt.setFontPointSize(8)

            fmt.setForeground(QColor(123,123,123,255))
            cursor.setCharFormat(fmt)
            serifFont = QFont("Arial", 8, QFont.Normal)
            fm = QFontMetrics(serifFont)
            elidedstr = fm.elidedText(index.model().data(index.siblingAtColumn(1), Qt.DisplayRole), Qt.ElideRight, colWid -10)
            cursor.insertText(elidedstr)
            cursor.endEditBlock()

            ctx = QAbstractTextDocumentLayout.PaintContext()
            if option.state & QStyle.State_Selected:
                ctx.palette.setColor(QPalette.Text, option.palette.color(
                    QPalette.Active, QPalette.HighlightedText))
            else:
                ctx.palette.setColor(QPalette.Text, option.palette.color(
                    QPalette.Active, QPalette.Text))

            textRect = option.widget.style().subElementRect(QStyle.SE_ItemViewItemText, options)
     
            textRect.adjust(5, 0, 0, 0)
            painter.translate(textRect.topLeft())
            self.doc.documentLayout().draw(painter, ctx)

            painter.restore()
        
        else:
            QStyledItemDelegate.paint(self, painter, option, index)


class MyTableView(QTableView):
    def __init__(self):
        super().__init__()
        self.verticalHeader().setDefaultSectionSize(40)
        self.horizontalHeader().setVisible(False)
        self.verticalHeader().setVisible(False)
        self.setFixedWidth(300)

    def setModel(self, model):
        super().setModel(model)
        # 初始隐藏列1
        self.setColumnHidden(1, True)
        # 监听行插入信号,重新隐藏列1
        model.rowsInserted.connect(lambda: self.setColumnHidden(1, True))


if __name__ == '__main__':
    app = QApplication(sys.argv)
    myWindow = QWidget()
    lyt = QVBoxLayout()
    myWindow.setLayout(lyt)

    t = MyTableView()
    t.setItemDelegate(TableDelegate())
    m = MyTableModel([['mike', '12', 'male', 124], ['jack', '15', 'male', 123]])
    t.setModel(m)

    btn = QPushButton('new')
    btn.clicked.connect(m.insertNewRow)
    lyt.addWidget(btn)
    lyt.addWidget(t)

    myWindow.show()
    sys.exit(app.exec_())

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 17:28:01