如何在 QListView 中显示从 QAbstractListModel 派生的自定义模型
How can I show a custom model derived from QAbstractListModel in QListView
我将在下面提供的基于 QAbstractListModel 的自定义数据模型将在两个视图中:基于 QWidget 的控制界面和基于 QML 'dashboard'。基于 QWidget 的控件端必须在自定义委托中显示模型项(模型行),其中每个数据点都具有下面描述的模型中规定的自定义角色,并且至少提供一个按钮来调用编辑器小部件以更改角色提供的数据点(即日期或特定项目的开始时间)。
class ScheduleModel(QAbstractListModel):
SfiRole = Qt.UserRole + 1
NameRole = Qt.UserRole + 2
ClsRole = Qt.UserRole + 3
FlagRole = Qt.UserRole + 4
OwnrRole = Qt.UserRole + 5
RecordRole = Qt.UserRole + 6
DeptRole = Qt.UserRole + 7
DateStrRole = Qt.UserRole + 8
HourRole = Qt.UserRole + 9
EstTimeRole = Qt.UserRole + 10
StatusRole = Qt.UserRole + 11
def __init__(self, parent=None):
super().__init__(parent)
self._data = []
def rowCount(self, parent=QModelIndex()):
return len(self._data)
@Slot()
def updateSchedule(self, schedule_items: list):
self.beginResetModel()
self._data = schedule_items
self.endResetModel()
def data(self, index=QModelIndex(), role: int = Qt.DisplayRole):
if 0 <= index.row() < self.rowCount() and index.isValid():
item = self._data[index.row()]
if role == self.SfiRole:
return item.sfi
elif role == self.NameRole:
return item.item_name
elif role == self.ClsRole:
return item.class_attendance
elif role == self.FlagRole:
return item.flag_attendance
elif role == self.OwnrRole:
return item.owner_attendance
elif role == self.RecordRole:
return item.record_status
elif role == self.DeptRole:
return item.responsible_dept
elif role == self.DateStrRole:
return item.date
elif role == self.HourRole:
return item.start_hour
elif role == self.EstTimeRole:
return item.est
elif role == self.StatusRole:
return "Passive"
else:
return None
def roleNames(self):
roles = dict()
roles[self.SfiRole] = b'sfiRole'
roles[self.NameRole] = b'nameRole'
roles[self.ClsRole] = b'clsRole'
roles[self.FlagRole] = b'flagRole'
roles[self.OwnrRole] = b'ownrRole'
roles[self.RecordRole] = b'recordRole'
roles[self.DeptRole] = b'deptRole'
roles[self.DateStrRole] = b'dateStrRole'
roles[self.HourRole] = b'hourRole'
roles[self.EstTimeRole] = b'estTimeRole'
roles[self.StatusRole] = b'statusRole'
return roles
上述模型适用于保留数据并通过索引和角色名称进行调用。我选择使用 QListView 和 QStyledItemDelegate 在基于 Qt 的视图中显示数据。就我现在而言,我必须开发一种用于显示目的的绘制方法,对于基本模型,Qt 使用 DisplayRole 从数据模型中读取字符串。但是正如您从模型中看到的那样,我没有促进 DispplayRole 并且我有几个自定义角色用于数据点以显示模型中的每个项目。
我尝试为从 QStyledItemDelegate 继承的委托创建一个 paint 方法来绘制与此类似的东西。例如,我在图片上标记了角色。
简而言之,问题是:是否可以覆盖 QStyledItemDelegate 的 paint 方法以显示自定义数据角色提供的字符串,并显示是否可以绘制按钮以在自定义委托中调用编辑器小部件?
我试图创建一个基于 QWidget 的解决方案,但我失败了。之后尝试创建和绘制方法以获得我想要的外观。我将在下面留下我的委托 class 和 paint 方法作为复杂委托的示例。
class TestItemDelegate(QStyledItemDelegate):
"""A delegate to show test items in listview in qt side"""
def __init__(self, parent=None) -> None:
super().__init__(parent)
self.model = self.parent().model()
def paint(self, painter: QPainter,
option: QStyleOptionViewItem, index: QModelIndex):
model_ind = index.model()
canvas = option.rect.getRect()
painter.setRenderHint(QPainter.Antialiasing, on=True)
# Supplying data from model
sfi = index.data(model_ind.SfiRole)
name = index.data(model_ind.NameRole)
cls_att = index.data(model_ind.ClsRole)
flg_att = index.data(model_ind.FlagRole)
ownr_att = index.data(model_ind.OwnrRole)
# rec_stat = index.data(model_ind.RecordRole)
resp_dept = index.data(model_ind.DeptRole)
date_str = index.data(model_ind.DateStrRole)
hour_str = index.data(model_ind.HourRole)
est_duration = index.data(model_ind.EstTimeRole)
# status = model_ind.data(index, model_ind.StatusRole)
# Frame and Background(s)
pen = QPen()
pen.setColor("Black")
pen.setWidth(2)
painter.setPen(pen)
painter.drawRoundedRect(option.rect, 10, 10)
painter.setPen(Qt.blue)
# Coordinates for delegate background
x, y, w, h = canvas
# Drawing of the texts
painter.drawText(
QRect(x + 50, y, w-200, h//2),
Qt.AlignVCenter, name)
painter.drawText(
QRect(x, y, h, h),
Qt.AlignCenter, sfi)
painter.drawText(
QRect(x+w-150, y, 50, h//3),
Qt.AlignVCenter,
"C:{}".format('-' if cls_att == '' else cls_att))
painter.drawText(
QRect(x+w-150, y+h//3, 50, h//3),
Qt.AlignVCenter,
"F:{}".format('-' if flg_att == '' else flg_att))
painter.drawText(
QRect(x+w-150, y + 2*h//3, 50, h//3),
Qt.AlignVCenter,
"O:{}".format('-' if ownr_att == '' else ownr_att))
painter.drawText(
QRect(x+50, y+h//2, w//3, h//2),
Qt.AlignVCenter, resp_dept)
painter.drawText(
QRect(x+w-100, y, 100, h//2),
Qt.AlignCenter, date_str.strftime('%d-%m-%Y')
)
painter.drawText(
QRect(x+w-100, y+h//2, 50, h//2),
Qt.AlignCenter, hour_str.strftime('%H:%M')
)
painter.drawText(
QRect(x+w-50, y+h//2, 50, h//2),
Qt.AlignCenter, est_duration
)
def createEditor(self, parent, option, index):
print(type(parent))
def sizeHint(self, option, index):
size = QSize(300, 50)
return size
我通过模型的标志方法将模型设置为可选和可编辑。
def flags(self, index):
if index.isValid():
return (Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable)
return super().flags(index)
我现在正在创建编辑器小部件和相关方法。可能会更新这个答案,在我找到可行的解决方案后留下一个全面的答案。
我将在下面提供的基于 QAbstractListModel 的自定义数据模型将在两个视图中:基于 QWidget 的控制界面和基于 QML 'dashboard'。基于 QWidget 的控件端必须在自定义委托中显示模型项(模型行),其中每个数据点都具有下面描述的模型中规定的自定义角色,并且至少提供一个按钮来调用编辑器小部件以更改角色提供的数据点(即日期或特定项目的开始时间)。
class ScheduleModel(QAbstractListModel):
SfiRole = Qt.UserRole + 1
NameRole = Qt.UserRole + 2
ClsRole = Qt.UserRole + 3
FlagRole = Qt.UserRole + 4
OwnrRole = Qt.UserRole + 5
RecordRole = Qt.UserRole + 6
DeptRole = Qt.UserRole + 7
DateStrRole = Qt.UserRole + 8
HourRole = Qt.UserRole + 9
EstTimeRole = Qt.UserRole + 10
StatusRole = Qt.UserRole + 11
def __init__(self, parent=None):
super().__init__(parent)
self._data = []
def rowCount(self, parent=QModelIndex()):
return len(self._data)
@Slot()
def updateSchedule(self, schedule_items: list):
self.beginResetModel()
self._data = schedule_items
self.endResetModel()
def data(self, index=QModelIndex(), role: int = Qt.DisplayRole):
if 0 <= index.row() < self.rowCount() and index.isValid():
item = self._data[index.row()]
if role == self.SfiRole:
return item.sfi
elif role == self.NameRole:
return item.item_name
elif role == self.ClsRole:
return item.class_attendance
elif role == self.FlagRole:
return item.flag_attendance
elif role == self.OwnrRole:
return item.owner_attendance
elif role == self.RecordRole:
return item.record_status
elif role == self.DeptRole:
return item.responsible_dept
elif role == self.DateStrRole:
return item.date
elif role == self.HourRole:
return item.start_hour
elif role == self.EstTimeRole:
return item.est
elif role == self.StatusRole:
return "Passive"
else:
return None
def roleNames(self):
roles = dict()
roles[self.SfiRole] = b'sfiRole'
roles[self.NameRole] = b'nameRole'
roles[self.ClsRole] = b'clsRole'
roles[self.FlagRole] = b'flagRole'
roles[self.OwnrRole] = b'ownrRole'
roles[self.RecordRole] = b'recordRole'
roles[self.DeptRole] = b'deptRole'
roles[self.DateStrRole] = b'dateStrRole'
roles[self.HourRole] = b'hourRole'
roles[self.EstTimeRole] = b'estTimeRole'
roles[self.StatusRole] = b'statusRole'
return roles
上述模型适用于保留数据并通过索引和角色名称进行调用。我选择使用 QListView 和 QStyledItemDelegate 在基于 Qt 的视图中显示数据。就我现在而言,我必须开发一种用于显示目的的绘制方法,对于基本模型,Qt 使用 DisplayRole 从数据模型中读取字符串。但是正如您从模型中看到的那样,我没有促进 DispplayRole 并且我有几个自定义角色用于数据点以显示模型中的每个项目。
我试图创建一个基于 QWidget 的解决方案,但我失败了。之后尝试创建和绘制方法以获得我想要的外观。我将在下面留下我的委托 class 和 paint 方法作为复杂委托的示例。
class TestItemDelegate(QStyledItemDelegate):
"""A delegate to show test items in listview in qt side"""
def __init__(self, parent=None) -> None:
super().__init__(parent)
self.model = self.parent().model()
def paint(self, painter: QPainter,
option: QStyleOptionViewItem, index: QModelIndex):
model_ind = index.model()
canvas = option.rect.getRect()
painter.setRenderHint(QPainter.Antialiasing, on=True)
# Supplying data from model
sfi = index.data(model_ind.SfiRole)
name = index.data(model_ind.NameRole)
cls_att = index.data(model_ind.ClsRole)
flg_att = index.data(model_ind.FlagRole)
ownr_att = index.data(model_ind.OwnrRole)
# rec_stat = index.data(model_ind.RecordRole)
resp_dept = index.data(model_ind.DeptRole)
date_str = index.data(model_ind.DateStrRole)
hour_str = index.data(model_ind.HourRole)
est_duration = index.data(model_ind.EstTimeRole)
# status = model_ind.data(index, model_ind.StatusRole)
# Frame and Background(s)
pen = QPen()
pen.setColor("Black")
pen.setWidth(2)
painter.setPen(pen)
painter.drawRoundedRect(option.rect, 10, 10)
painter.setPen(Qt.blue)
# Coordinates for delegate background
x, y, w, h = canvas
# Drawing of the texts
painter.drawText(
QRect(x + 50, y, w-200, h//2),
Qt.AlignVCenter, name)
painter.drawText(
QRect(x, y, h, h),
Qt.AlignCenter, sfi)
painter.drawText(
QRect(x+w-150, y, 50, h//3),
Qt.AlignVCenter,
"C:{}".format('-' if cls_att == '' else cls_att))
painter.drawText(
QRect(x+w-150, y+h//3, 50, h//3),
Qt.AlignVCenter,
"F:{}".format('-' if flg_att == '' else flg_att))
painter.drawText(
QRect(x+w-150, y + 2*h//3, 50, h//3),
Qt.AlignVCenter,
"O:{}".format('-' if ownr_att == '' else ownr_att))
painter.drawText(
QRect(x+50, y+h//2, w//3, h//2),
Qt.AlignVCenter, resp_dept)
painter.drawText(
QRect(x+w-100, y, 100, h//2),
Qt.AlignCenter, date_str.strftime('%d-%m-%Y')
)
painter.drawText(
QRect(x+w-100, y+h//2, 50, h//2),
Qt.AlignCenter, hour_str.strftime('%H:%M')
)
painter.drawText(
QRect(x+w-50, y+h//2, 50, h//2),
Qt.AlignCenter, est_duration
)
def createEditor(self, parent, option, index):
print(type(parent))
def sizeHint(self, option, index):
size = QSize(300, 50)
return size
我通过模型的标志方法将模型设置为可选和可编辑。
def flags(self, index):
if index.isValid():
return (Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable)
return super().flags(index)
我现在正在创建编辑器小部件和相关方法。可能会更新这个答案,在我找到可行的解决方案后留下一个全面的答案。