如何在QListView中展示QAbstractListModel自定义模型并实现带编辑按钮的自定义委托
两个问题的答案都是肯定的,具体实现逻辑如下:
1. 重写paint方法读取自定义角色绘制内容
不需要适配Qt.DisplayRole,你可以在paint方法中直接通过索引调取任意自定义角色的数据,按你需要的布局绘制即可:
- 调用
index.data(自定义角色枚举)即可拿到对应字段的值,比如index.data(ScheduleModel.DateStrRole)就能拿到条目的日期字符串 - 你可以把条目绘制区域拆分为多个子矩形,分别绘制对应角色的文本,和你给出的示例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
相关产品推荐
相关产品推荐

