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

PyQt5中如何获取QTableView里QStyledItemDelegate项的实际尺寸?

问题解决:QTableView空单元格装饰图与resizeToContents循环放大问题

需求

  • 空单元格显示自适应大小的装饰图形
  • 支持随时执行resizeToContents操作
  • 初始状态为单个空单元格

现有问题

  • 禁用装饰逻辑时,首次点击resizeToContents单元格会缩小至最小尺寸,后续点击无变化
  • 启用装饰逻辑时,每次点击resizeToContents单元格都会持续变大,陷入循环放大的死循环

问题根源

直接用QTableView.rowHeight()和columnWidth()获取的是单元格整体尺寸(包含边框、内边距),以此生成的装饰图尺寸等于单元格整体大小。执行resizeToContents时,表格会根据装饰图尺寸调整单元格大小,导致单元格被进一步放大,进而生成更大的装饰图,形成循环。

解决方案

通过自定义QStyledItemDelegate,在paint方法中直接绘制装饰图形,利用单元格的实际可用内容区域进行绘制,避免依赖当前单元格尺寸生成固定大小的图片,彻底解决循环放大问题。同时通过sizeHint控制空单元格的最小尺寸,避免禁用装饰时单元格缩得过小。

修改后的代码

import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import (Qt, QSize, QPointF, QRectF)
from PyQt5.QtGui import (QColor, QPen, QPainter)
from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QPushButton, QStyleOptionViewItem, QStyledItemDelegate)

class EmptyCellDelegate(QStyledItemDelegate):
    def paint(self, painter, option, index):
        # 先执行默认绘制,处理有内容的单元格
        super().paint(painter, option, index)
        
        # 检查单元格是否为空
        value = index.data(Qt.DisplayRole)
        if not value:
            # 获取单元格实际可用的内容区域(去掉边距)
            content_rect = option.rect.adjusted(4, 4, -4, -4)
            if content_rect.isValid():
                painter.save()
                # 设置画笔样式
                pen = QPen(QColor("#446600"), 4, Qt.SolidLine)
                painter.setPen(pen)
                # 绘制自适应图形
                center = content_rect.center()
                painter.drawLine(content_rect.bottomLeft(), QPointF(center.x(), center.y() + 4))
                painter.drawLine(content_rect.bottomRight(), QPointF(center.x(), center.y() + 4))
                painter.drawLine(content_rect.topLeft(), QPointF(center.x(), center.y() - 4))
                painter.drawLine(content_rect.topRight(), QPointF(center.x(), center.y() - 4))
                painter.restore()
    
    def sizeHint(self, option, index):
        # 为空单元格设置最小尺寸,避免缩得过小
        value = index.data(Qt.DisplayRole)
        if not value:
            return QSize(40, 40)
        return super().sizeHint(option, index)

class TableModel(QtCore.QAbstractTableModel):
    def __init__(self, data):
        super(TableModel, self).__init__()
        self._data = data

    def data(self, index, role):
        if role == Qt.DisplayRole:
            return self._data[index.row()][index.column()]
        # 移除原Model中的装饰逻辑,交由Delegate负责

    def rowCount(self, index):
        return len(self._data)

    def columnCount(self, index):
        return len(self._data[0])

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.centralw = QWidget()
        self.setCentralWidget(self.centralw)
        self.vlayout = QVBoxLayout(self.centralw)

        self.btn = QPushButton("resizeToContents")
        self.btn.clicked.connect(self.resizeToContents)
        self.vlayout.addWidget(self.btn)

        self.table_view = QtWidgets.QTableView()
        data = [[""]]
        self.model = TableModel(data)
        self.table_view.setModel(self.model)
        # 设置自定义Delegate处理空单元格装饰
        self.table_view.setItemDelegate(EmptyCellDelegate())
        self.vlayout.addWidget(self.table_view)

    def resizeToContents(self):
        self.table_view.resizeColumnsToContents()
        self.table_view.resizeRowsToContents()

app=QtWidgets.QApplication(sys.argv)
window=MainWindow()
window.show()
window.resize(280, 140)
app.exec_()

代码说明

  1. 自定义Delegate:EmptyCellDelegate重写paint方法,在空单元格内绘制自适应图形,通过option.rect.adjusted()去掉边距,确保图形在内容区域内绘制,不会触发循环放大。
  2. 最小尺寸控制:sizeHint方法为空单元格设置最小尺寸,避免无内容时单元格缩至不可见。
  3. 职责分离:将装饰逻辑从Model转移到Delegate,符合MVC设计模式,Model仅负责数据管理,Delegate专注于显示逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 17:54:53