PYQT QTableView Delegate 在使用 Proxy 时无法显示 createEditor

PYQT QTableView Delegate can not show createEditor when applied with Proxy

在代理情况下应用委托时,我无法显示编辑器小部件。 -> self.table.setModel(self.proxy)

如果Delegate应用于View/Model结构,那么完全没有问题。 -> #self.table.setModel(self.model)

参考:https://www.pythonfixing.com/2021/10/fixed-adding-row-to-qtableview-with.html

查看下面的代码:

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import sys

class Delegate(QItemDelegate):
    def __init__(self):
        QItemDelegate.__init__(self)
        self.type_items = ["1", "2", "3"]

    def createEditor(self, parent, option, index):
        if index.column() == 0:
            comboBox = QComboBox(parent)
            comboBox.addItems(self.type_items)
            return comboBox
        # no need to check for the other columns, as Qt automatically creates a
        # QLineEdit for string values and QTimeEdit for QTime values;
        return super().createEditor(parent, option, index)
        

class TableModel(QAbstractTableModel):
    def __init__(self, data):
        super(TableModel, self).__init__()
        self._data = data

    def appendRowData(self, data):
        self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount())
        self._data.append(data)
        self.endInsertRows()

    def data(self, index, role=Qt.DisplayRole):
        if role in (Qt.DisplayRole, Qt.EditRole):
            return self._data[index.row()][index.column()]

    def setData(self, index, value, role=Qt.EditRole):
        if role == Qt.EditRole:
            self._data[index.row()][index.column()] = value
            self.dataChanged.emit(index, index)
            return True
        return False

    def rowCount(self, index=None):
        return len(self._data)

    def columnCount(self, index=None):
        return len(self._data[0])

    def flags(self, index):
        # allow editing of the index
        return super().flags(index) | Qt.ItemIsEditable
        

class CustomProxyModel(QSortFilterProxyModel):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._filters = dict()

    @property
    def filters(self):
        return self._filters

    def setFilter(self, expresion, column):
        if expresion:
            self.filters[column] = expresion
        elif column in self.filters:
            del self.filters[column]
        self.invalidateFilter()

    def filterAcceptsRow(self, source_row, source_parent):
        for column, expresion in self.filters.items():
            text = self.sourceModel().index(source_row, column, source_parent).data()
            regex = QRegExp(
                expresion, Qt.CaseInsensitive, QRegExp.RegExp
            )
            if regex.indexIn(text) == -1:
                return False
        return True
        

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

        localWidget = QWidget()

        self.table = QTableView(localWidget)

        data = [["1", "Hi", QTime(2, 1)], ["2", "Hello", QTime(3, 0)]]

        self.model = TableModel(data)
        
        self.proxy = CustomProxyModel()   # Customized Filter
        self.proxy.setSourceModel(self.model)
       
        #self.table.setModel(self.model)    # Original code, for View/Model
        self.table.setModel(self.proxy)     # Revised code, for View/Proxy/Model
        
        self.table.setItemDelegate(Delegate())

        self.add_row = QPushButton("Add Row", localWidget)
        self.add_row.clicked.connect(self.addRow)

        for row in range(self.model.rowCount()):
            for column in range(self.model.columnCount()):
                index = self.model.index(row, column)
                self.table.openPersistentEditor(index)    # openPersistentEditor for createEditor

        layout_v = QVBoxLayout()
        layout_v.addWidget(self.table)
        layout_v.addWidget(self.add_row)
        localWidget.setLayout(layout_v)
        self.setCentralWidget(localWidget)
        self.show()
        
    def addRow(self):
        row = self.model.rowCount()

        new_row_data = ["3", "Howdy", QTime(9, 0)]
        self.model.appendRowData(new_row_data)

        for i in range(self.model.columnCount()):
            index = self.model.index(row, i)
            self.table.openPersistentEditor(index)    # openPersistentEditor for createEditor
            
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
  1. 使用 View/Model、小部件编辑器显示进行测试。

  2. 使用 View/Proxy/Model 进行测试,小工具编辑器不显示。

任何访问 视图 索引的尝试 必须 使用视图的 模型

您的代码不起作用,因为您提供的索引属于另一个模型,因此无法创建编辑器,因为视图无法将索引的模型识别为自己的模型:视图使用代理模型,当您尝试打开 source 模型的编辑器时。

虽然在这种情况下,最简单的解决方案是使用 self.proxy.index(),但 正确的 解决方案始终参考视图的模型。

self.model.index(...) 都更改为 self.table.model().index(...)

好的,非常感谢。

修改代码如下,现在Widget Editor相应显示。

for row in range(self.model.rowCount()):
    for column in range(self.model.columnCount()):
        #index = self.model.index(row, column)    # original code, which get the wrong index from the model
        index = self.proxy.index(row, column)     # revised code, get the correct index from the proxy
        self.table.openPersistentEditor(index)    # openPersistentEditor for createEditor