Я пытаюсь заставить QTreeView работать с QSortFilterProxyModel . Я написал минимальный рабочий пример (который, к сожалению, не настолько минимален из-за сложности вопроса). Полный код:
import logging
from PyQt5 import QtCore, QtWidgets
import sys
class DBObject:
def __init__(self, name, parent, children=None):
self.name = name
self.parent = parent
self.children = children or list()
def __repr__(self):
return f"name: {self.name}, parent: {self.parent.name if self.parent is not None else '-'}"
class Model(QtCore.QAbstractItemModel):
def __init__(self, root, parent=None):
super().__init__(parent)
self._root = root
def columnCount(self, parent=None, *args, **kwargs):
return 1
def rowCount(self, parent=None, *args, **kwargs):
if not parent.isValid():
return 1
parentItem = parent.internalPointer()
rowCount = len(parentItem.children)
logging.info(f"rowCount({parentItem}): rowCount={rowCount}")
return rowCount
def parent(self, index):
if not index.isValid():
return QtCore.QModelIndex()
item = index.internalPointer()
parentItem = item.parent
logging.info(f"parent({item}): parent={parentItem}")
if parentItem is None:
return QtCore.QModelIndex()
else:
if parentItem.parent is None:
return self.createIndex(0, 0, parentItem)
else:
return self.createIndex(parentItem.parent.children.index(parentItem), 0, parentItem)
def index(self, row, column, parent=None, *args, **kwargs):
if not parent.isValid():
if row != 0 or column != 0:
return QtCore.QModelIndex()
else:
logging.info(f"index({row}, {column}, None): index={self._root}")
return self.createIndex(0, 0, self._root)
parentItem = parent.internalPointer()
if 0 <= row < len(parentItem.children):
logging.info(f"index({row}, {column}, {parentItem}): index={parentItem.children[row]}")
return self.createIndex(row, column, parentItem.children[row])
else:
logging.info(f"index({row}, {column}, {parentItem}): index=None")
return QtCore.QModelIndex()
def data(self, index, role=None):
if not index.isValid():
return QtCore.QVariant()
item = index.internalPointer()
if role == QtCore.Qt.DisplayRole:
return item.name
else:
return QtCore.QVariant()
def flags(self, index):
if not index.isValid():
return QtCore.Qt.NoItemFlags
return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsSelectable
def setData(self, index, value, role=None):
if not index.isValid():
return False
item = index.internalPointer()
if role == QtCore.Qt.EditRole:
item.name = value
self.dataChanged.emit(index, index, [role])
return True
else:
return False
def insertRows(self, row, count, parent):
self.beginInsertRows(parent, row, row + count - 1)
parentItem = parent.internalPointer()
for i in range(count):
parentItem.children.append(DBObject("new", parentItem))
self.endInsertRows()
self.layoutChanged.emit()
return True
class ProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, root, parent=None):
super().__init__(parent)
self._root = root
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, root):
super().__init__()
self._root = root
self.setMinimumSize(640, 480)
centralWidget = QtWidgets.QWidget(self)
self.setCentralWidget(centralWidget)
layout = QtWidgets.QVBoxLayout(centralWidget)
self._treeView = QtWidgets.QTreeView(self)
layout.addWidget(self._treeView)
self._model = Model(self._root, self)
self._proxyModel = ProxyModel(self._root, self)
self._proxyModel.setSourceModel(self._model)
self._treeView.setModel(self._proxyModel)
self._treeView.expandAll()
button = QtWidgets.QPushButton("Add")
layout.addWidget(button)
button.clicked.connect(self._Clicked)
def _Clicked(self):
self._model.insertRow(len(self._root.children), self._model.index(0, 0, QtCore.QModelIndex()))
self._model.layoutChanged.emit()
self._treeView.expandAll()
def main():
root = DBObject("root", None)
items = ["foo", "bar", "baz"]
for x in items:
child = DBObject(x + "0", root)
root.children.append(child)
for y in items:
child.children.append(DBObject(y + "1", child))
app = QtWidgets.QApplication(sys.argv)
mainWindow = MainWindow(root)
mainWindow.show()
app.exec_()
if __name__ == "__main__":
main()
До сих пор меня интересовало только отображение доступных данных, и я мог выбирать и даже редактировать отображаемые данные. Однако теперь я также хотел бы добавить данные в модель, но мне это не удалось.
Чтобы добавить данные, мне нужно перегрузить метод insertRows , приведенный ниже, и у меня есть пара вопросов по этому поводу:
def insertRows(self, row, count, parent):
self.beginInsertRows(parent, row, row + count - 1)
parentItem = parent.internalPointer()
for i in range(count):
parentItem.children.append(DBObject("new", parentItem))
self.endInsertRows()
self.layoutChanged.emit()
return True
- Использует ли этот метод нужно быть в производном классе QAbstractItemModel или QSortFilterProxyModel?
- Нужно ли использовать createIndex внутри этого метода?
- Нужно ли вызывать layoutAboutToBeChanged внутри этого метода?
Проблема возникает, когда я начинаю выбирать различные элементы в древовидном представлении, а также добавлять новые данные. Отказы варьируются от ошибок сегментации до QSortFilterProxyModel: index from wrong model passed to mapFromSource
.
. Я подавал свою модель через набор тестов, найденный здесь , и все в порядке. Используемая мной QSortFilterProxyModel не выполняет никакой фильтрации или сортировки в этом примере, поэтому проблема не связана с этим.
Любая помощь, подсказка или обратная связь приветствуются.