Моя проблема возникает, когда я раскрываю любой элемент в своем дереве (кроме верхнего элемента) и выбираю его дочерний элемент. Затем верхний элемент также расширяется, чтобы показать его потомков.
Пример:
У меня есть 2 детей в возрасте до root, у каждого из которых есть собственный дочерний элемент:
root
|-A
|-child AA
|
|-B
|-child BB
Проблема: Когда я раскрываю B и выбираю дочерний BB в своем дереве, он также почему-то открывает (расширяет) A.
Если мы добавим еще один элемент в root, C с ребенком CC и расширьте это вместо B; A будет по-прежнему расширяться, как только будет выбран дочерний CC.
Я использую пользовательский класс в качестве базовых элементов и использую свой собственный класс QAbstractItemModel в качестве sourceModel для QSortFilterProxyModel. Я уверен, что проблема лежит где-то в этих двух пользовательских классах, но я не могу понять, почему это происходит.
У меня есть рабочий пример ниже. Он включает в себя несколько фрагментов исходного скрипта, поэтому он может показаться немного грязным.
В верхнем представлении дерева используется прокси-сервер (и возникают проблемы), а в приведенном ниже примере пропускается прокси-сервер, и он не возникли проблемы.
from PyQt5 import QtWidgets, QtCore, QtGui
class MainWindow(QtWidgets.QWidget):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.custom_nodes = [] #A list that will contain all our custom nodes.
#list of stand-in dicts:
self.all_node_list = [{'node_name': 'A', 'parent': None, 'children': [{'node_name': 'AB', 'parent': 'A','children': [{'node_name': 'ABC','parent': 'AB','children': []}]}]},
{'node_name': 'Prop', 'parent': None, 'children': [{'node_name': 'SubProp', 'parent': 'Prop','children': [{'node_name': 'Asset', 'parent': 'SubProp', 'children': []}]}]}]
self.MakeNodesFromDict(self.all_node_list,None)#Make custom nodes from dict
self.tree_model = CustomModel(self.custom_nodes) #Create custom model
self.CreateWindow()
def CreateWindow(self):
self.setWindowTitle("Top proxy - below normal")
self.main_layout = QtWidgets.QVBoxLayout()
#SEARCH BAR
self.menu_search_bar = QtWidgets.QLineEdit()
self.main_layout.addWidget(self.menu_search_bar)
#TREE VIEW USING PROXY MODEL
self.proxy_tree = QtWidgets.QTreeView()
self.proxy_tree.setObjectName("treeView")
self.proxy_tree.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
self.proxy_tree.setHeaderHidden(True)
self.proxy_tree.setExpandsOnDoubleClick(True)
self.proxy_tree.setAnimated(True)
self.proxy_tree.setIndentation(20)
self.proxy_tree.setSortingEnabled(True)
self.proxy_tree.setObjectName("proxy_treeView")
self.proxy_tree.setWindowTitle("Dir View")
#THE PROXY MODEL
self.proxyModel = QtCore.QSortFilterProxyModel(self.proxy_tree)
self.proxyModel.setSourceModel(self.tree_model)
self.proxyModel.setRecursiveFilteringEnabled(True)
self.proxyModel.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive)
self.proxyModel.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
self.proxy_tree.setModel(self.proxyModel)
self.proxyModel.sort(0, 0)
#Connect proxy bar to tree
self.menu_search_bar.textChanged.connect(self.ProxyUpdate)
self.menu_search_bar.returnPressed.connect(self.SearchEnter)
#TREE VIEW WITHOUT PROXY MODEL
self.tree = QtWidgets.QTreeView()
self.tree.setObjectName("treeView")
self.tree.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
self.tree.setHeaderHidden(True)
self.tree.setExpandsOnDoubleClick(True)
self.tree.setAnimated(True)
self.tree.setIndentation(20)
self.tree.setSortingEnabled(True)
self.tree.setModel(self.tree_model)
self.main_layout.addWidget(self.proxy_tree)
self.main_layout.addWidget(self.tree)
self.setLayout(self.main_layout)
###########################################################
#################### FUNCTIONS ############################
###########################################################
def MakeNodesFromDict(self,cur_list, cur_parent):
for t in cur_list:
cur_node = CustomNode(node_name=t["node_name"], children=[],_parent=cur_parent)
if cur_parent:
cur_parent.addChild(cur_node)
self.MakeNodesFromDict(t["children"], cur_node)
self.custom_nodes.append(cur_node)
@QtCore.pyqtSlot(str) # <--Not sure if this is needed?
def ProxyUpdate(self,text):
my_pattern = QtCore.QRegularExpression()
my_pattern.setPatternOptions(QtCore.QRegularExpression.CaseInsensitiveOption)
my_pattern.setPattern(text)
self.proxyModel.setFilterRegularExpression(my_pattern)
def SearchEnter(self): #opens or closes the tree view after search
if self.menu_search_bar.text() == "":
self.proxy_tree.collapseAll()
else:
self.proxy_tree.expandAll()
class CustomModel(QtCore.QAbstractItemModel): #Custom model to work with custom nodes
def __init__(self, nodes): #Give all custom nodes at init
QtCore.QAbstractItemModel.__init__(self)
self._root = CustomNode() #Create root object of custom model as a custom node aswell
self.nodes = nodes
for node in self.nodes:
if node.parent()==None: #Go through and add nodes with no parents (top nodes)
self._root.addChild(node)
def emitDataChanged(self):
self.dataChanged.emit(QtCore.QModelIndex(), QtCore.QModelIndex())
def rowCount(self, index):
if index.isValid():
node = index.internalPointer()
return node.childCount()
return self._root.childCount()
def addChild(self, node, _parent):
if not _parent or not _parent.isValid():
parent = self._root
else:
parent = _parent.internalPointer()
parent.addChild(node)
def index(self, row, column, _parent=None):
if not _parent or not _parent.isValid():
node_parent = self._root
else:
node_parent = _parent.internalPointer()
if not QtCore.QAbstractItemModel.hasIndex(self, row, column, _parent):
return QtCore.QModelIndex()
child = node_parent.child(row)
if child:
return QtCore.QAbstractItemModel.createIndex(self, row, column, child)
else:
return QtCore.QModelIndex()
def parent(self, index):
if index.isValid():
p = index.internalPointer().parent()
if p:
return QtCore.QAbstractItemModel.createIndex(self, p.row(), 0, p)
return QtCore.QModelIndex()
def columnCount(self, index):
if index.isValid():
return index.internalPointer().columnCount()
return self._root.columnCount()
def data(self, index, role):
if not index.isValid():
return None
node = index.internalPointer()
if role == QtCore.Qt.DisplayRole:
return node.data(index.column())
return None
def getNode(self, index):
return index.internalPointer()
def update(self, index):
pass
class CustomNode(object):
def __init__(self, column_count=1,row=0,children=[],node_name="root",_parent=None):
self._column_count = column_count
self._row = row
self.node_name = node_name
self._parent = _parent
self._children = children
def addChild(self, child):
child._parent = self
child._row = self.childCount()
self._children.append(child)
self._column_count = max(child.columnCount(), self._column_count)
def GetName(self):
return self.node_name
def GetChildren(self):
return self._children
def data(self, column):
return self.node_name
def columnCount(self):
return self._column_count
def childCount(self):
return len(self._children)
def child(self, row):
if row >= 0 and row < self.childCount():
return self._children[row]
def parent(self):
return self._parent
def row(self):
return self._row
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
mainWin = MainWindow()
mainWin.show()
sys.exit(app.exec_())
У меня возникли проблемы с разработкой подкласса QAbstractItemModel и использованием собственного класса объектов в качестве элементов. Итак, вы видите половину проб и ошибок и половину ответов stackoverflow, все вместе взятые :)