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

如何在QListView中展示QAbstractListModel自定义模型并实现带编辑按钮的自定义委托

两个问题的答案都是肯定的,具体实现逻辑如下:

1. 重写paint方法读取自定义角色绘制内容

不需要适配Qt.DisplayRole,你可以在paint方法中直接通过索引调取任意自定义角色的数据,按你需要的布局绘制即可:

  • 调用index.data(自定义角色枚举)即可拿到对应字段的值,比如index.data(ScheduleModel.DateStrRole)就能拿到条目的日期字符串
  • 你可以把条目绘制区域拆分为多个子矩形,分别绘制对应角色的文本,和你给出的示例UI结构对齐即可
    示例条目UI效果

简化的paint方法示例:

from PyQt5.QtWidgets import QStyledItemDelegate, QStyle, QApplication
from PyQt5.QtGui import QPainter
from PyQt5.QtCore import Qt, QSize

class ScheduleDelegate(QStyledItemDelegate):
    def paint(self, painter: QPainter, option, index):
        # 先绘制默认的条目背景(选中/hover效果会自动处理)
        self.initStyleOption(option, index)
        style = option.widget.style() if option.widget else QApplication.style()
        style.drawPrimitive(QStyle.PE_PanelItemViewItem, option, painter, option.widget)
        
        painter.save()
        # 避免文字绘制在选中高亮背景上看不清
        if option.state & QStyle.State_Selected:
            painter.setPen(option.palette.highlightedText().color())
        else:
            painter.setPen(option.palette.text().color())
            
        # 拆分区域 读取各自定义角色数据绘制
        rect = option.rect.adjusted(5, 2, -5, -2)
        # 绘制SFI号
        sfi = index.data(ScheduleModel.SfiRole)
        painter.drawText(rect.left(), rect.top() + 15, str(sfi))
        # 绘制任务名称
        name = index.data(ScheduleModel.NameRole)
        painter.drawText(rect.left() + 50, rect.top() + 15, str(name))
        # 绘制日期
        date = index.data(ScheduleModel.DateStrRole)
        painter.drawText(rect.left() + 300, rect.top() + 15, str(date))
        # 其他字段按你需要的位置依次绘制即可
        
        # 绘制编辑按钮(用原生样式绘制)
        btn_rect = rect.adjusted(rect.width() - 60, 5, 0, -5)
        option.text = "编辑"
        option.rect = btn_rect
        style.drawControl(QStyle.CE_PushButton, option, painter, option.widget)
        
        painter.restore()
        
    # 必须重写这个方法返回条目高度,不然默认行高不够显示多段内容
    def sizeHint(self, option, index):
        return QSize(option.rect.width(), 40)

2. 点击按钮唤起编辑器的实现

需要额外重写editorEvent方法监听点击事件,判断点击位置是否落在按钮区域,触发编辑逻辑:

def editorEvent(self, event, model, option, index):
        if event.type() == event.MouseButtonRelease:
            # 计算按钮区域
            btn_rect = option.rect.adjusted(option.rect.width() - 60, 5, 0, -5)
            if btn_rect.contains(event.pos()):
                # 触发编辑逻辑,视图会自动调用createEditor
                option.widget.edit(index)
                return True
        return super().editorEvent(event, model, option, index)
    
    # 自定义编辑器实现,你可以根据编辑的字段返回不同的编辑器
    def createEditor(self, parent, option, index):
        # 示例:弹出一个表单编辑器修改所有字段,也可以单独弹出对应字段的编辑器
        from PyQt5.QtWidgets import QDialog, QFormLayout, QDateEdit, QSpinBox, QDialogButtonBox
        from PyQt5.QtCore import QDate
        dialog = QDialog(parent)
        dialog.setWindowTitle("修改日程")
        layout = QFormLayout(dialog)
        
        # 对应各个角色的输入框
        self.date_edit = QDateEdit(dialog)
        self.date_edit.setDate(QDate.fromString(index.data(ScheduleModel.DateStrRole), "yyyy-MM-dd"))
        layout.addRow("日期", self.date_edit)
        
        self.hour_spin = QSpinBox(dialog)
        self.hour_spin.setRange(0, 23)
        self.hour_spin.setValue(index.data(ScheduleModel.HourRole))
        layout.addRow("开始小时", self.hour_spin)
        
        # 其他字段的输入控件按需添加
        btn_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, dialog)
        btn_box.accepted.connect(dialog.accept)
        btn_box.rejected.connect(dialog.reject)
        layout.addRow(btn_box)
        return dialog
    
    def setEditorData(self, editor, index):
        # 上述示例中我们在createEditor已经填充了数据,这里可以留空,或者单独处理逻辑
        pass
    
    def setModelData(self, editor, model, index):
        if editor.exec() == QDialog.Accepted:
            # 提交修改到模型,注意你的模型现在还没有实现setData方法,需要先补全
            new_date = self.date_edit.date().toString("yyyy-MM-dd")
            new_hour = self.hour_spin.value()
            model.setData(index, new_date, ScheduleModel.DateStrRole)
            model.setData(index, new_hour, ScheduleModel.HourRole)

注意:你当前的ScheduleModel还没有重写setData方法,修改数据前需要先补全该方法,修改完成后调用dataChanged信号通知视图刷新。

最后你只需要给QListView设置自定义委托即可:

list_view = QListView()
list_view.setItemDelegate(ScheduleDelegate(list_view))
list_view.setModel(your_schedule_model)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.08 01:45:03