Как я могу показать пользовательскую модель, полученную из QAbstractListModel, в QListView

#python #qt #pyqt #pyside

Вопрос:

Пользовательская модель данных, которую я представлю ниже на основе 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 для чтения строк из моделей данных. Но, как вы можете видеть из модели, я не облегчил роль dispplay, и у меня есть несколько пользовательских ролей для отображения точек данных для каждого элемента в модели.
Я пытаюсь создать метод рисования для делегата, который наследуется от QStyledItemDelegate, чтобы нарисовать что-то подобное этому. Например, я отметил роли на картинке.
Короче говоря, вопрос в следующем: можно ли переопределить метод рисования QStyledItemDelegate для отображения строк, предоставляемых пользовательскими ролями данных, а также отобразить, можно ли рисовать кнопки для вызова виджета редактора в пользовательском делегате?

Комментарии:

1. Представление элемента на основе виджета Qt по умолчанию отображает данные на основе ролей: DisplayRole возвращает печатаемое значение (строку или число), BackgroundRole и ForegroundRole используются для цвета фона и текста элемента соответственно и т. Д. Каждая запись должна содержать только одно поле и возвращать данные только об этом поле, но, видя ваши пользовательские имена ролей, кажется, что в вашей модели данных для каждой записи много полей, поэтому вам, вероятно, следует вместо этого выбрать табличную модель.

2. Я тоже рассматривал это в течение очень короткого периода. Но мне показалось, что это неправильно, потому что «расписание», с которым я работаю, также поставляется в виде электронной таблицы Excel. Таким образом, я хочу представить данные в более «округленном» виде на стороне управления на основе QWidget. По этой причине я ищу метод «по книге» для использования пользовательских ролей в QListView. Надеюсь, я смогу объясниться. Я все еще относительно новичок в программировании пользовательского интерфейса.

3. Если вы имеете в виду, что хотите изменить способ отображения записей пользователю, это не проблема: делегаты также используются именно по этой причине. Если вы уточните, что должно отображаться в представлении списка, мы сможем лучше понять, что вы пытаетесь сделать, а затем дадим вам более подходящие ответы.

4. Отредактировал мой вопрос, чтобы немного прояснить проблему. @musicamante спасибо, что терпишь меня.

5. @PerturbedMachinist ваш вопрос неясен

Ответ №1:

Я попытался создать решение на основе 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
 

Я установил модель как выбираемую и редактируемую с помощью метода флагов модели.

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

        return super().flags(index)

 

Прямо сейчас я создаю виджет редактора и относительные методы. Возможно, обновите этот ответ, чтобы оставить исчерпывающий ответ после того, как я найду приемлемое решение.