【问题标题】:Alerting QDataWidgetMapper to changes when using a custom Model & Delegate使用自定义模型和委托时提醒 QDataWidgetMapper 发生变化
【发布时间】:2021-04-16 16:56:19
【问题描述】:

我正在使用一个子类 QAbstractTableModel,其中 dataclasses 作为项目。每个 dataclass 都包含一个带有 list 的字段“field1”,我希望将其显示在列表视图中,并在我编辑或添加项目时自动更改列表视图。

为此,我为 QDataWidgetMapper 设置了一个自定义委托,该委托将从该 dataclass 中检索和设置值。这按我想要的方式工作。

我的问题是我想通过按下按钮向该列表视图添加其他项目,并让 QDataWidgetMapper 自动将它们添加到模型中。

这是我目前所拥有的:

import sys
import dataclasses
from typing import List, Any
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *


@dataclasses.dataclass()
class StorageItem:

    field1: List[str] = dataclasses.field(default_factory=list)


class StorageModel(QAbstractTableModel):

    def __init__(self, parent=None):
        super().__init__(parent)

        test = StorageItem()
        test.field1 = ['Item °1', 'Item °2']
        self._data: List[StorageItem] = [test]

    def data(self, index: QModelIndex, role: int = ...) -> Any:
        if not index.isValid():
            return

        item = self._data[index.row()]
        col = index.column()

        if role in {Qt.DisplayRole, Qt.EditRole}:
            if col == 0:
                return item.field1
            else:
                return None

    def setData(self, index: QModelIndex, value, role: int = ...) -> bool:

        if not index.isValid() or role != Qt.EditRole:
            return False

        item = self._data[index.row()]
        col = index.column()

        if col == 0:
            item.field1 = value

        self.dataChanged.emit(index, index)
        print(self._data)
        return True

    def flags(self, index: QModelIndex) -> Qt.ItemFlags:
        return Qt.ItemFlags(
            Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
        )

    def rowCount(self, parent=None) -> int:
        return len(self._data)

    def columnCount(self, parent=None) -> int:
        return len(dataclasses.fields(StorageItem))


class TestDelegate(QStyledItemDelegate):

    def __init__(self, parent=None):
        super().__init__(parent)

    def setEditorData(self, editor: QWidget, index: QModelIndex) -> None:
        if isinstance(editor, QListView):
            data = index.model().data(index, Qt.DisplayRole)
            editor.model().setStringList(data)
        else:
            super().setEditorData(editor, index)

    def setModelData(
            self, editor: QWidget,
            model: QAbstractItemModel,
            index: QModelIndex
    ) -> None:

        if isinstance(editor, QListView):
            data = editor.model().stringList()
            model.setData(index, data, Qt.EditRole)
        else:
            super().setModelData(editor, model, index)


class CustomListView(QListView):

    item_added = pyqtSignal(name='itemAdded')

    def __init__(self, parent=None):
        super().__init__(parent)

        self.setModel(QStringListModel())

    def add_item(self, item: str):
        str_list = self.model().stringList()
        str_list.append(item)
        self.model().setStringList(str_list)
        self.item_added.emit()


class MainWindow(QMainWindow):

    def __init__(self, parent=None):
        super().__init__(parent)

        cent_widget = QWidget()
        self.setCentralWidget(cent_widget)

        # Vertical Layout
        v_layout = QVBoxLayout()
        v_layout.setContentsMargins(10, 10, 10, 10)

        # Listview
        self.listview = CustomListView()
        v_layout.addWidget(self.listview)

        # Button
        self.btn = QPushButton('Add')
        self.btn.clicked.connect(lambda: self.listview.add_item('New Item'))
        v_layout.addWidget(self.btn)

        cent_widget.setLayout(v_layout)

        # Set Mapping
        self.mapper = QDataWidgetMapper()
        self.mapper.setItemDelegate(TestDelegate())
        self.mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit)
        self.mapper.setModel(StorageModel())
        self.mapper.addMapping(self.listview, 0)
        self.mapper.toFirst()

        self.listview.itemAdded.connect(self.mapper.submit)


def main():
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    app.exec()


if __name__ == '__main__':
    main()

目前,我正在使用自定义 ListView 内部的信号 itemAdded 来手动提交 QDataWidgetMapper

有没有办法在 CustomListView 中做到这一点,而不使用自定义信号? 委托不知何故知道列表视图中的数据何时被编辑。添加新项目时如何触发相同的机制?

【问题讨论】:

  • 我不明白,你在这里使用了两个不同的模型,你的子类和字符串列表。除此之外,QDataWidgetMapper 负责“添加项目”。
  • 我不坚持使用QStringListModel,我只想在QListView中显示“field1”的内容,并能够在“ field1" 通过按下按钮并将该更改反映在列表视图中。
  • @SirTeddytheFirst 据您了解,您有一个表类型模型,其中一个项目(在您的情况下为 0,0)有一个列表对象作为数据,然后您想要创建与 QListView 关联的编辑器类似于如果数据是文本,那么编辑器将是 QLineEdit。我是对的?

标签: python pyqt qabstracttablemodel qdatawidgetmapper


【解决方案1】:

TL; DR; 不能。


submitPolicy QDataWidgetMapper::AutoSubmit 表示失去焦点时模型会更新。当调用委托的 commitData 或 closeEditor 信号时,模型也会更新,默认情况下会在按下某些特定键时发生。

更好的实现是创建一个信号,每次在 QListView 模型中进行更改时都会发出信号,并将其连接到提交,而不仅仅是添加元素的方法。此外,最好使用自定义 qproperty。

class CustomListView(QListView):
    items_changed = pyqtSignal(name="itemsChanged")

    def __init__(self, parent=None):
        super().__init__(parent)

        self.setModel(QStringListModel())
        self.model().rowsInserted.connect(self.items_changed)
        self.model().rowsRemoved.connect(self.items_changed)
        self.model().dataChanged.connect(self.items_changed)
        self.model().layoutChanged.connect(self.items_changed)

    def add_item(self, item: str):
        self.items += [item]

    @pyqtProperty(list, notify=items_changed)
    def items(self):
        return self.model().stringList()

    @items.setter
    def items(self, data):
        if len(data) == len(self.items) and all(
            x == y for x, y in zip(data, self.items)
        ):
            return
        self.model().setStringList(data)
        self.items_changed.emit()
# Set Mapping
self.mapper = QDataWidgetMapper()
self.mapper.setModel(StorageModel())
self.mapper.addMapping(self.listview, 0, b"items")
self.mapper.toFirst()

self.listview.items_changed.connect(self.mapper.submit)

【讨论】:

  • 这是一个改进(谢谢),但我的实际问题是模型(StorageModel)应该在调用 add_item 时更新,目前只有在我单击按钮后在列表视图中编辑一个项目之后。
  • @SirTeddytheFirst 不幸的是你不能。理由在我的帖子中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-12-04
  • 1970-01-01
  • 2020-03-23
  • 2020-09-15
  • 1970-01-01
  • 2016-08-12
  • 2018-11-13
相关资源
最近更新 更多