删除 QTreeView 中项目的最后一个子项时 UI 崩溃

问题描述 投票:0回答:1

当我尝试删除 QTreeView 中项目的最后一个子项时,我的 UI 崩溃而不返回任何错误消息。我看到 this post 给出了一个解决方案,但它对我不起作用,即使我在

index
方法中添加这些行:

if not self.hasIndex(row, column, parent):
            return QModelIndex()

所以我尝试使用

QAbstractItemModelTester(self.model, QAbstractItemModelTester.FailureReportingMode.Fatal)
来看看我的模型是否有问题。它返回此错误消息:
FAIL! flags == Qt::ItemIsDropEnabled || flags == 0 () returned FALSE (C:\Users\qt\work\qt\qtbase\src\testlib\qabstractitemmodeltester.cpp:373)
首先,我不知道如何修复这个错误,所以如果您有任何想法,欢迎您。其次,我不知道这是否是删除项目的最后一个子项时 UI 崩溃的原因。 我尝试制作一个 MRE,但错误并未显示在其中。 Ui 根本不会崩溃,所以我将其保留在下面,以便您检查是否看到任何我没有看到的错误,并解释了模型中的此错误。

import sys

from PySide6.QtGui import *
from PySide6.QtCore import *
from PySide6.QtWidgets import *
from PySide6.QtTest import QAbstractItemModelTester




class MainWindow(QMainWindow):
    def __init__(self, parent: QWidget = None):
        super().__init__(parent)
        self.resize(573, 468)

        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.contextMenuEvent)
        self.menu : QMenu = QMenu(self)
        self.delete_action = self.menu.addAction("Delete")
        self.delete_action.triggered.connect(self.delitem)

        self.frame = QFrame()
        self.setCentralWidget(self.frame)
        self.hlayout = QHBoxLayout()
        self.frame.setLayout(self.hlayout)


        self.view = QTreeView()
        self.view.setAlternatingRowColors(True)
        self.view.setSelectionBehavior(QAbstractItemView.SelectItems)
        self.view.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
        self.view.setAnimated(False)
        self.view.setAllColumnsShowFocus(True)
        self.hlayout.addWidget(self.view)
        headers = ["value",'Type','Progress Bar','Test','Test','Test','Test']


        self.model = TreeModel(headers, self)
        self.view.setModel(self.model)

        # QAbstractItemModelTester(self.model, QAbstractItemModelTester.FailureReportingMode.Fatal)

        self.view.expandAll()
        for column in range(self.model.columnCount()):
            self.view.resizeColumnToContents(column)

    def delitem(self) :
        index: QModelIndex = self.view.currentIndex()
        model: QAbstractItemModel = self.model
        model.removeRow(index.row(), index.parent())
        print('deleted')

    def contextMenuEvent(self, event: QContextMenuEvent, point : QPoint = QPoint()) -> None:
        selection_model = self.view.selectionModel()
        has_selection: bool = not selection_model.selection().isEmpty()
        if has_selection :
            self.menu.exec(QCursor.pos())




class TreeItem:

    def __init__(self, data: list, parent: 'TreeItem' = None):
        self.item_data = data
        self.parent_item = parent
        self.child_items = []

    def child(self, number: int) -> 'TreeItem':
        if number < 0 or number >= len(self.child_items):
            return None
        return self.child_items[number]

    def last_child(self) -> 'TreeItem':
        return self.child_items[-1] if self.child_items else None

    def child_count(self) -> int:
        return len(self.child_items)

    def child_number(self) -> int:
        if self.parent_item:
            return self.parent_item.child_items.index(self)
        return 0

    def column_count(self) -> int:
        return len(self.item_data)

    def data(self, column: int):
        if column < 0 or column >= len(self.item_data):
            return None
        return self.item_data[column]

    def insert_children(self, position: int, count: int, columns: int) -> bool:
        if position < 0 or position > len(self.child_items):
            return False

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

        return True

    def insert_columns(self, position: int, columns: int) -> bool:
        if position < 0 or position > len(self.item_data):
            return False

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

        for child in self.child_items:
            child.insert_columns(position, columns)

        return True

    def parent(self):
        return self.parent_item
    
    def has_parent(self) -> bool:
        if self.parent_item :
            return(True)
        else : 
            return(False)

    def remove_children(self, position: int, count: int) -> bool:
        if position < 0 or position + count > len(self.child_items):
            return False

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

        return True

    def remove_columns(self, position: int, columns: int) -> bool:
        if position < 0 or position + columns > len(self.item_data):
            return False

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

        for child in self.child_items:
            child.remove_columns(position, columns)

        return True

    def set_data(self, column: int, value):
        if column < 0 or column >= len(self.item_data):
            return False

        self.item_data[column] = value
        return True

    def __repr__(self) -> str:
        result = f"<treeitem.TreeItem at 0x{id(self):x}"
        for d in self.item_data:
            result += f' "{d}"' if d else " <None>"
        result += f", {len(self.child_items)} children>"
        return result
    
    def __str__(self) -> str:
        result : str = str(self.parent())
        for data in self.item_data :
            result += ',' + str(data)
        return(result)




class TreeModel(QAbstractItemModel):
    
    def __init__(self, headers: list, parent=None):
        super().__init__(parent)

        self.root_data = headers
        self.root_item = TreeItem(self.root_data.copy())
        self.setup_model_data(self.root_item)


    def columnCount(self, parent: QModelIndex = None) -> int:
        return self.root_item.column_count()

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

        if role not in (0,1,2) :
            return None
        
        item: TreeItem = self.get_item(index)

        return item.data(index.column())

    def flags(self, index: QModelIndex) -> Qt.ItemFlags:
        if not index.isValid():
            return Qt.ItemIsEditable

        return Qt.ItemIsEditable | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled | Qt.ItemIsDropEnabled | QAbstractItemModel.flags(self, index)
    
    def get_item(self, index: QModelIndex = QModelIndex()) -> TreeItem:
        if index.isValid():
            item: TreeItem = index.internalPointer()
            if item:
                return item

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

        return None

    def index(self, row: int, column: int, parent: QModelIndex = QModelIndex()) -> QModelIndex:

        # if not self.hasIndex(row, column, parent) :
        #     return QModelIndex()

        if parent.isValid() and parent.column() != 0:
            return QModelIndex()

        parent_item: TreeItem = self.get_item(parent)
        if not parent_item:
            return QModelIndex()
        

        child_item: TreeItem = parent_item.child(row)
        if child_item:
            return self.createIndex(row, column, child_item)
        return QModelIndex()

    def insertColumns(self, position: int, columns: int,
                      parent: QModelIndex = QModelIndex()) -> bool:
        self.beginInsertColumns(parent, position, position + columns - 1)
        success: bool = self.root_item.insert_columns(position, columns)
        self.endInsertColumns()

        return success

    def insertRows(self, position: int, rows: int,
                   parent: QModelIndex = QModelIndex()) -> bool:
        parent_item: TreeItem = self.get_item(parent)
        if not parent_item:
            return False

        self.beginInsertRows(parent, position, position + rows - 1)
        column_count = self.root_item.column_count()
        success: bool = parent_item.insert_children(position, rows, column_count)
        self.endInsertRows()

        return success

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

        child_item: TreeItem = self.get_item(index)
        if child_item:
            parent_item: TreeItem = child_item.parent()
        else:
            parent_item = None

        if parent_item == self.root_item or not parent_item:
            return QModelIndex()

        return self.createIndex(parent_item.child_number(), 0, parent_item)

    def removeColumns(self, position: int, columns: int,
                      parent: QModelIndex = QModelIndex()) -> bool:
        self.beginRemoveColumns(parent, position, position + columns - 1)
        success: bool = self.root_item.remove_columns(position, columns)
        self.endRemoveColumns()

        if self.root_item.column_count() == 0:
            self.removeRows(0, self.rowCount())

        return success

    def removeRows(self, position: int, rows: int,
                   parent: QModelIndex = QModelIndex()) -> bool:
        parent_item: TreeItem = self.get_item(parent)
        if not parent_item:
            return False

        self.beginRemoveRows(parent, position, position + rows - 1)
        success: bool = parent_item.remove_children(position, rows)
        self.endRemoveRows()

        return success

    def rowCount(self, parent: QModelIndex = QModelIndex()) -> int:
        if parent.isValid() and parent.column() > 0:
            return 0

        parent_item: TreeItem = self.get_item(parent)
        if not parent_item:
            return 0
        return parent_item.child_count()

    def setData(self, index: QModelIndex, value, role: int) -> bool:
        if role != Qt.EditRole:
            return False

        item: TreeItem = self.get_item(index)
        result: bool = item.set_data(index.column(), value)

        if result:
            self.dataChanged.emit(index, index, [Qt.DisplayRole, Qt.EditRole])

        return result
    
    def setData2(self, index: QModelIndex, value, role: int) -> bool:
        if role != Qt.EditRole:
            return False

        item: TreeItem = self.get_item(index)
        result: bool = item.set_data(index.column(), value)

        return result

    def setHeaderData(self, section: int, orientation: Qt.Orientation, value,
                      role: int = None) -> bool:
        if role != Qt.EditRole or orientation != Qt.Horizontal:
            return False

        result: bool = self.root_item.set_data(section, value)

        if result:
            self.headerDataChanged.emit(orientation, section, section)

        return result

    def setup_model_data(self, parent: TreeItem):
    

        parents = [parent] 
        parent : TreeItem = parents[0] 
        col_count = self.root_item.column_count()
        parent.insert_children(parent.child_count(), 1, col_count) 
        column_data = ['test 1','test 2','test 3','test 4','test 5'] 

        for column in range(len(column_data)):
            child = parent.last_child()
            child.set_data(column, column_data[column])

        for i in range(4) :
            child : TreeItem = parent.last_child()
            child.insert_children(child.child_count(), 1, col_count)
            column_data = ['test 4','test 9','test 4','test 0','test 6'] 

            for column in range(len(column_data)):
                child2 = child.last_child()
                child2.set_data(column, column_data[column])


    def _repr_recursion(self, item: TreeItem, indent: int = 0) -> str:
        result = " " * indent + repr(item) + "\n"
        for child in item.child_items:
            result += self._repr_recursion(child, indent + 2)
        return result

    def __repr__(self) -> str:
        return self._repr_recursion(self.root_item)
    

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

现在下面的代码显示了我所拥有的

setup_model_data method

def setup_model_data(self, parent: TreeItem, id_projet):

        parents = [parent]
        con : Connexion = Connexion()
        with con.pool.acquire() as connexion :
            with connexion.cursor() as cursor :
                for ensemble in cursor.execute("SELECT nom,id_sous_ensemble,id FROM Sous_Ensemble WHERE id_sous_ensemble = 0 AND id_projet = :1 ORDER BY id_sous_ensemble,nom",[id_projet]) : 
                    parent : TreeItem = parents[0] 
                    col_count = self.root_item.column_count() 
                    parent.insert_children(parent.child_count(), 1, col_count)
                    column_data = [ensemble[0],None,None,-2,ensemble[2]]

                    for column in range(len(column_data)):
                        child = parent.last_child()
                        child.set_data(column, column_data[column])

                    
                    self.recursiviter(ensemble[2],child)


            with connexion.cursor() as cursor1 :
                for piece in cursor1.execute("SELECT designation, id_sous_ensemble, id FROM piece WHERE id_sous_ensemble = 0") :
                    
                    with connexion.cursor() as cursor2 :
                        for rev_info in cursor2.execute("SELECT id_rev,id_mat FROM link_piece_rev WHERE id_piece = :1 ORDER BY id_rev DESC FETCH NEXT 1 ROWS ONLY",[piece[2]]) :
                            id_rev = rev_info[0]
                            id_mat = rev_info[1]
                            parent.insert_children(parent.child_count(), 1, col_count)
                            column_data =[piece[0],id_rev,id_mat,-1,piece[2]]

                            for column in range(len(column_data)):
                                child = parent.last_child()
                                child.set_data(column, column_data[column])
    
    def recursiviter(self,id, parent : TreeItem, id_projet = 1) :
        
        con1 : Connexion = Connexion()
        with con1.pool.acquire() as connexion1 :
            with connexion1.cursor() as cursor2 :
                for ensemble in (cursor2.execute("SELECT nom,id_sous_ensemble,id FROM SOUS_ENSEMBLE WHERE id_sous_ensemble = :1 AND id_projet = :2",[id,id_projet])) :
                    if ensemble :
                        col_count = self.root_item.column_count()
                        parent.insert_children(parent.child_count(),1,col_count)
                        column_data = [ensemble[0],None,None,-2,ensemble[2]]

                        for column in range(len(column_data)):
                            child = parent.last_child()
                            child.set_data(column, column_data[column])
                        
                        self.recursiviter(ensemble[2],parent.last_child())
                    else : 
                        return
                    
                with connexion1.cursor() as cursor3 :
                    for piece in cursor3.execute("SELECT designation, id_sous_ensemble, id FROM piece WHERE id_sous_ensemble = :1",[id]) :
                        if piece :
                            with connexion1.cursor() as cursor4 :
                                for rev_info in cursor4.execute("SELECT id_rev,id_mat FROM link_piece_rev WHERE id_piece = :1 ORDER BY id_rev DESC FETCH NEXT 1 ROWS ONLY",[piece[2]]) :
                                    id_rev = rev_info[0]
                                    id_mat = rev_info[1]
                                    col_count = self.root_item.column_count()
                                    parent.insert_children(parent.child_count(), 1, col_count)
                                    column_data =[piece[0],id_rev,id_mat,-1,piece[2]]
                                    for column in range(len(column_data)):
                                        child = parent.last_child()
                                        child.set_data(column, column_data[column])
                        else :
                            return

我不知道这是否会对您有帮助,特别是当我从数据库创建模型时使用

setup_model_data
recusititer
方法。我按照Qt官方网站给出的exemple来创建我的
QTreeView
。我不知道可以给你什么来解决我的问题。请随时在评论中询问任何信息,我会很乐意快速答复。

python database user-interface qtreeview qabstractitemmodel
1个回答
0
投票

这方面有什么更新吗? 我有完全相同的问题,并且没有返回码。 很奇怪

© www.soinside.com 2019 - 2024. All rights reserved.