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