Вставка строк в QTreeView, который использует QSortFilterProxyModel - PullRequest
0 голосов
/ 28 мая 2018

У меня есть очень простое приложение, которое отображает древовидное представление и кнопку, которая добавляет элементы в это древовидное представление, используя текущий выбор в качестве родителя.Вставка дочернего элемента первого уровня работает хорошо, в то время как вставка дочернего элемента третьего уровня по какой-то причине завершается неудачно (после вставки он не отображается. Я подготовил полностью проверяемый код, который вы можете проверить самостоятельно, контрольный пример следующий:

  1. Нажмите на любой элемент
  2. Нажмите кнопку "Добавить строку"
  3. Нажмите на вновь созданный элемент
  4. Нажмите кнопку "Добавить строку" еще разОжидаемый результат: добавлен дочерний элемент Фактический результат: ничего не происходит.

Вот код

main.py

from PyQt5 import QtWidgets
import application
import sys


def main():
    app = QtWidgets.QApplication(sys.argv)
    window = application.Application()  
    window.show()  
    app.exec_()

main()

application.py

from PyQt5 import QtWidgets
from PyQt5.QtCore import QSortFilterProxyModel, QModelIndex

import tree
from TreeModel import TreeModel


class Application(QtWidgets.QMainWindow, tree.Ui_MainWindow):
    data = [
        "test1",
        "test2",
        "test3"
    ]

    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.proxy_model = QSortFilterProxyModel(self.treeView)
        self.model = TreeModel(self.treeView)
        for data in self.data:
            index = QModelIndex()
            self.model.insertRows(self.model.rowCount(index), 1, index)
            self.model.setData(self.model.index(self.model.rowCount(index) - 1, 0, index), data)
            self.proxy_model.setSourceModel(self.model)
        self.treeView.setModel(self.proxy_model)
        self.pushButton.clicked.connect(lambda: self.add_row_click())

    def add_row_click(self):
        index = self.treeView.selectionModel().selectedIndexes()[0]
        self.proxy_model.insertRows(self.proxy_model.rowCount(index), 1, index)
        self.proxy_model.setData(self.proxy_model.index(self.proxy_model.rowCount(index) - 1, 0, index), "new_test")

TreeItem.py

class TreeItem(object):
    ind_column_name = 0
    ind_column_id = 1
    ind_column_parent_id = 2

    key_name = "name"
    key_id = "id"
    key_parent_id = "parent"
    key_new_id = "new_item_id"

    def __init__(self, data, parent=None):
        self.parentItem = parent
        self.itemData = data
        self.childItems = []

    def child(self, row):
        try:
            return self.childItems[row]
        except IndexError:
            return ""

    def childCount(self):
        return len(self.childItems)

    def childNumber(self):
        if self.parentItem is None:
            return self.parentItem.childItems.index(self)
        return 0

    def columnCount(self):
        return len(self.itemData)

    def data(self, column):
        if column != self.ind_column_id and column != self.ind_column_parent_id:
            return self.itemData[column]
        return None

    def id_data(self, column):
        if column == self.ind_column_id or column == self.ind_column_parent_id:
            return self.itemData[column]

    def insertChildren(self, position, count, columns):
        if position < 0 or position > len(self.childItems):
            return False

        for row in range(count):
            data = [None for v in range(columns)]
            item = TreeItem(data, self)
            self.childItems.insert(position, item)

        return True

    def insertColumns(self, position, columns):
        if position < 0 or position > len(self.itemData):
            return False

        for column in range(columns):
            self.itemData.insert(position, None)

        for child in self.childItems:
            child.insertColumns(position, columns)

        return True

    def parent(self):
        return self.parentItem

    def removeChildren(self, position, count):
        if position < 0 or position + count > len(self.childItems):
            return False

        for row in range(count):
            self.childItems.pop(position)

        return True

    def removeColumns(self, position, columns):
        if position < 0 or position + columns > len(self.itemData):
            return False

        for column in range(columns):
            self.itemData.pop(position)

        for child in self.childItems:
            child.removeColumns(position, columns)

        return True

    def setData(self, column, value):
        if column < 0 or column >= len(self.itemData):
            return False

        self.itemData[column] = value

        return True

    def to_json(self):
        parent_id = self.itemData[self.ind_column_parent_id]
        item_id = self.itemData[self.ind_column_id]
        json_data = dict()
        json_data[self.key_name] = self.itemData[self.ind_column_name]
        if parent_id is not None:
            json_data[self.key_parent_id] = parent_id
        if item_id is not None:
            json_data[self.key_id] = item_id
        return json_data

TreeModel.py

from PyQt5.QtCore import (QAbstractItemModel, QModelIndex, Qt)

from TreeItem import TreeItem


class TreeModel(QAbstractItemModel):
    def __init__(self, parent=None):
        super(TreeModel, self).__init__(parent)
        self.rootItem = TreeItem(["Категории", None, None])

    def columnCount(self, parent=QModelIndex()):
        # subtract hidden columns
        return self.rootItem.columnCount() - 2

    def all_rows_count(self, root_item, row_count=0):
        if root_item is None:
            root_item = self.rootItem
        for x in range(root_item.childCount()):
            row_count += 1
            row_count = self.all_rows_count(root_item.child(x), row_count)
        return row_count

    def data(self, index, role):
        if not index.isValid():
            return None

        if role != Qt.DisplayRole and role != Qt.EditRole:
            return None

        item = self.getItem(index)
        return item.data(index.column())

    def flags(self, index):
        if not index.isValid():
            return 0

        return Qt.ItemIsEditable | super(TreeModel, self).flags(index)

    def getItem(self, index):
        if index.isValid():
            item = index.internalPointer()
            if item:
                return item

        return self.rootItem

    def headerData(self, section, orientation, role=Qt.DisplayRole):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self.rootItem.data(section)

        return None

    def index(self, row, column, parent=QModelIndex()):
        if parent.isValid() and parent.column() != 0:
            return QModelIndex()

        parentItem = self.getItem(parent)
        childItem = parentItem.child(row)
        if childItem:
            return self.createIndex(row, column, childItem)
        else:
            return QModelIndex()

    def insertColumns(self, position, columns, parent=QModelIndex()):
        self.beginInsertColumns(parent, position, position + columns - 1)
        success = self.rootItem.insertColumns(position, columns)
        self.endInsertColumns()

        return success

    def insertRows(self, position, rows, parent=QModelIndex(), *args, **kwargs):
        parentItem = self.getItem(parent)
        self.beginInsertRows(parent, position, position + rows - 1)
        success = parentItem.insertChildren(position, rows,
                self.rootItem.columnCount())
        self.endInsertRows()

        return success

    def parent(self, index):
        if not index.isValid():
            return QModelIndex()

        childItem = self.getItem(index)
        parentItem = childItem.parent()

        if parentItem == self.rootItem:
            return QModelIndex()

        return self.createIndex(parentItem.childNumber(), 0, parentItem)

    def removeColumns(self, position, columns, parent=QModelIndex()):
        self.beginRemoveColumns(parent, position, position + columns - 1)
        success = self.rootItem.removeColumns(position, columns)
        self.endRemoveColumns()

        if self.rootItem.columnCount() == 0:
            self.removeRows(0, self.rowCount())

        return success

    def removeRows(self, position, rows, parent=QModelIndex()):
        parentItem = self.getItem(parent)

        self.beginRemoveRows(parent, position, position + rows - 1)
        success = parentItem.removeChildren(position, rows)
        self.endRemoveRows()

        return success

    def rowCount(self, parent=QModelIndex()):
        parentItem = self.getItem(parent)

        return parentItem.childCount()

    def setData(self, index, value, role=Qt.EditRole):
        if role != Qt.EditRole:
            return False

        item = self.getItem(index)
        result = item.setData(index.column(), value)

        if result:
            print("setData(), item name = %s, index row = %d" % (str(item.data(TreeItem.ind_column_name)), index.row()))
            self.dataChanged.emit(index, index)
        else:
            print("Failed to set value: " + str(value))
        return result

    def setHeaderData(self, section, orientation, value, role=Qt.EditRole):
        if role != Qt.EditRole or orientation != Qt.Horizontal:
            return False

        result = self.rootItem.setData(section, value)
        if result:
            self.headerDataChanged.emit(orientation, section, section)

        return result

tree.py (просмотреть файл)

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'forso.ui'
#
# Created by: PyQt5 UI code generator 5.10.1
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 523)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.treeView = QtWidgets.QTreeView(self.centralwidget)
        self.treeView.setGeometry(QtCore.QRect(10, 10, 771, 411))
        self.treeView.setObjectName("treeView")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(10, 430, 75, 23))
        self.pushButton.setObjectName("pushButton")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton.setText(_translate("MainWindow", "Add row"))

1 Ответ

0 голосов
/ 28 мая 2018

Проблема возникает только при активированном редакторе элементов, что означает, что представление каким-то образом не уведомляется об изменениях в модели.В это время dataChanged модели не отправляется на прокси.Альтернативное решение - вызвать прокси dataChanged и изменить состояние расширения или сжатия, чтобы обновить представление, в конце восстановить состояние расширения и сжатия.

def add_row_click(self):
    index = self.treeView.selectionModel().selectedIndexes()[0]
    self.treeView.setCurrentIndex(index)
    self.proxy_model.insertRows(self.proxy_model.rowCount(index), 1, index)
    self.proxy_model.setData(self.proxy_model.index(self.proxy_model.rowCount(index) - 1, 0, index), "new_test")
    self.proxy_model.dataChanged.emit(index, index)
    v = self.treeView.isExpanded(index)
    self.treeView.setExpanded(index, not v)
    self.treeView.setExpanded(index, v)

В заключение, если ребеноквставлен, но представление не обновляется корректно, это можно проверить с помощью другого QTreeView и установки self.model в качестве модели.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...