从 QSortFilterProxyModel 进行回溯

问题描述 投票:0回答:1

我正在使用一个简单的

QTableView
/
QAbstractTabmeModel
/
QSortFilterProxyModel
设置(为简洁起见进行了编辑):

rom collections import namedtuple
from typing import Optional

from PyQt6 import uic
from PyQt6.QtCore import QAbstractTableModel, pyqtSlot, Qt, QSortFilterProxyModel
from PyQt6.QtWidgets import QWidget, QTableView, QHeaderView, QButtonGroup, QPushButton

import Fandom


class IModel(QAbstractTableModel):
    _column = namedtuple('_column', "name func hint align")

    def __init__(self, columns: [_column]):
        self._columns = columns
        super().__init__()
        self._rows = []
        self.select()

    def data(self, index, role=...):
        match role:
            case Qt.ItemDataRole.DisplayRole:
                row = self._rows[index.row()]
                return self._columns[index.column()].func(row)
            case Qt.ItemDataRole.TextAlignmentRole:
                return self._columns[index.column()].align
        return None

    def headerData(self, section, orientation, role=...):
        if orientation == Qt.Orientation.Horizontal:
            match role:
                case Qt.ItemDataRole.DisplayRole:
                    return self._columns[section].name
        return None

    def rowCount(self, parent=...):
        return len(self._rows)

    def columnCount(self, parent=...):
        return len(self._columns)

    def select(self, what=None):
        self.beginResetModel()
        self._rows = []
        self.endResetModel()

    def set_hints(self, view: QTableView):
        header = view.horizontalHeader()
        for i, x in enumerate(self._columns):
            header.setSectionResizeMode(i, x.hint)


class ItemModel(IModel):
    def __init__(self):
        super().__init__([
            IModel._column('ID', lambda x: x['ID'],
                           QHeaderView.ResizeMode.ResizeToContents,
                           Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter),
            IModel._column('Name', lambda x: x['Name'],
                           QHeaderView.ResizeMode.ResizeToContents,
                           Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter),
            IModel._column('Description', lambda x: x['desc'],
                           QHeaderView.ResizeMode.ResizeToContents,
                           Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter),
        ])

    def select(self, what='ALL'):
        self.beginResetModel()
        items = [
                 {'ID': 1, 'Name': 'foo', 'desc': 'Something'},
                 {'ID': 2, 'Name': 'fie', 'desc': 'Something else'},
                 {'ID': 3, 'Name': 'faa', 'desc': 'Another'},
                 {'ID': 4, 'Name': 'fum', 'desc': 'Another one'},
                ]
        self._rows = items
        self.endResetModel()

    def id(self, idx: int):
        return self._rows[idx]['ID']

    def value(self, idx: int):
        return self._rows[idx]

class Storage(QWidget):
    def __init__(self, *args, **kwargs):
        self.items: Optional[QTableView] = None
        self.storage: Optional[QTableView] = None
        super().__init__(*args, **kwargs)
        uic.loadUi("Storage.ui", self)
        self.item_model = ItemModel()
        self.item_proxy = QSortFilterProxyModel()
        self.item_proxy.setSourceModel(self.item_model)
        self.items.setModel(self.item_proxy)
        self.item_model.set_hints(self.items)
        self.items.setSortingEnabled(True)
        self.items.sortByColumn(1, Qt.SortOrder.AscendingOrder)
        self.items.reset()

    @pyqtSlot()
    def on_add_clicked(self):
        sel = self.items.currentIndex()
        if sel.isValid():
            idx = self.item_model.id(sel.row())
            print(idx)

    @pyqtSlot()
    def on_del_clicked(self):
        sel = self.items.currentIndex()
        if sel.isValid():
            idx = self.item_model.id(sel.row())
            print(idx)


if __name__ == '__main__':
    from PyQt6.QtWidgets import QMainWindow, QApplication

    class MainWindow(QMainWindow):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.storage = Storage()
            self.setCentralWidget(self.storage)


    app = QApplication([])
    win = MainWindow()
    win.show()

    from sys import exit
    exit(app.exec())

问题出在

on_[add|del]_clicked()
,我需要从已排序的行(由
self.items.current_index()
返回)映射回原始未排序的
ItemModel._rows
中的索引。 我怎样才能实现这个目标?

sorting model-view-controller pyqt6
1个回答
0
投票

我发现了。 我需要使用

QSortFilterProxyModel.mapToSource()
就我而言,类似:

    @pyqtSlot()
    def on_del_clicked(self):
        sel = self.items.currentIndex()              <-- this is proxy index
        if sel.isValid():
            orig = self.item_proxy.mapToSource(sel)  <-- this is model index
            idx = self.item_model.id(orig.row())     <-- use as needed
            self.storage_model.edit(idx, -1)

© www.soinside.com 2019 - 2024. All rights reserved.