在QTreeView中插入使用QSortFilterProxyModel的行

时间:2018-05-27 21:36:13

标签: python pyqt pyqt5 qtreeview qsortfilterproxymodel

我有一个非常基本的应用程序,它使用当前选择作为父视图,显示树视图和一个将项添加到该树视图的按钮。插入第一级子项时效果很好,因为插入3d级别子项因某些原因失败(插入完成后它不会显示。我已经准备好了可以自行测试的完全可验证的代码,测试用例如下:

  1. 点击任何项目
  2. 点击“添加行”按钮
  3. 点击新创建的项目
  4. 再次点击“添加行”按钮 预期结果:添加子项 实际结果:没有任何反应。
  5. 这是代码

    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 :(得分:1)

仅在激活元素编辑器时才会出现此问题,这意味着在某种程度上视图不会通知模型中的更改。那时,模型的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设置为模型。