首页 > 解决方案 > 如何在 QListView 中显示从 QAbstractListModel 派生的自定义模型

问题描述

我将在下面基于 QAbstractListModel 提供的自定义数据模型将在两个视图中:基于 QWidget 的控制界面和基于 QML 的“仪表板”。基于 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 的委托创建一个绘制方法,以绘制与此类似的内容。例如,我在图片上标记了角色。简而言之,问题是:是否可以覆盖 QStyledItemDelegate 的绘制方法以显示自定义数据角色提供的字符串,并显示是否可以在自定义委托中绘制用于调用编辑器小部件的按钮?

标签: pythonqtpyqtpyside

解决方案


我试图创建一个基于 QWidget 的解决方案,但我失败了。之后尝试创建和绘制方法以实现我想要的外观。我将在下面留下我的委托类和绘制方法作为复杂委托的示例。

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

我通过模型的 flags 方法将模型设置为可选择和可编辑。

def flags(self, index):
        if index.isValid():
            return (Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable)

        return super().flags(index)

我现在正在创建编辑器小部件和相关方法。在我获得可行的解决方案后,可能会更新此答案以留下全面的答案。


推荐阅读