0

我正在使用带有数据类作为项目的子类QAbstractTableModel 。每个数据类都包含一个带有list的字段“field1” ,我想在 listview 中显示它,并在我在 listview 中编辑或添加项目时自动更改它。

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

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

这是我到目前为止所拥有的:

ListView 有三个项目和一个 QPushButton 下面

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中做到这一点,而不使用自定义信号?不知何故,委托知道列表视图中的数据何时被编辑。添加新项目时如何触发相同的机制?

4

1 回答 1

2

TL; 博士;这不可以。


submitPolicyQDataWidgetMapper::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)
于 2021-04-16T17:33:02.230 回答