PySide QTreeWidget的clear()导致崩溃

4 投票
1 回答
1718 浏览
提问于 2025-04-18 01:50

首先,我对Python和PySide还很陌生。

我有三个不同的QTreeWidget,它们分别代表三种不同的文件夹结构。当我尝试一次性清空这三个时,应用程序就崩溃了。

widgets = [ self.Delete_treeWidget01, self.Delete_treeWidget02, self.Delete_treeWidget03 ]  
for widget in widgets:
    widget.clear()

我也尝试过其他方法来清空它,比如说:

widgets = [ self.treeWidget01, self.treeWidget02, self.treeWidget03 ]
for widget in widgets:
    aList = (range(widget.topLevelItemCount()))
    for i in aList[::-1]:
        widget.takeTopLevelItem(i)

但这样做也会导致崩溃。

我点击了“调试”,然后Visual Studio给了我这个信息:

在mayapy.exe中发生未处理的异常,地址为0x000000006E9D2C68(QtGui4.dll): 0xC0000005:访问冲突,读取位置0xFFFFFFFFFFFFFFFF。

(这里提到maya是因为我在用Eclipse和Maya的Python解释器,因为我主要是为Maya编程。不过这次是独立的,跟Maya没有关系。)

我在QTreeWidgets所表示的层级中删除了一个文件夹,为了让它显示准确,我想先清空它,然后再重新填充。

我在网上搜索这个问题时,有人说这可能跟线程有关,但因为我对Python和PySide还不太熟悉,所以我甚至不知道怎么处理线程。

有没有人知道该怎么解决这个问题?

编辑:

代码如下:

import os
import sys
from PySide import QtGui, QtCore
from Toolbox_Test_UI import Ui_MirrorFolderCreation
from PySide.QtGui import QMainWindow, QApplication


class MainWindow( QMainWindow, Ui_MirrorFolderCreation ):
    def __init__( self ):
        super( MainWindow, self ).__init__()
        self.setupUi( self )

        # Connects the buttons
        self.Delete_pushButton01.clicked.connect( lambda: self.folderDialog( 'delete1_dir' ) )
        self.Delete_pushButton02.clicked.connect( lambda: self.folderDialog( 'delete2_dir' ) )
        self.Delete_pushButton03.clicked.connect( lambda: self.folderDialog( 'delete3_dir' ) )

        self.Delete_deletePushButton.clicked.connect( lambda: self.deleteFolders() )
        self.Delete_populatePushButton.clicked.connect( lambda: self.populateTreeViews() )

        self.Delete_treeWidget01.itemClicked.connect( lambda: self.findMirroredItem() )

        # Change resize mode
        headers = [self.Delete_treeWidget01.header(), self.Delete_treeWidget02.header(), self.Delete_treeWidget03.header()]
        for head in headers:
            head.setResizeMode( 0, QtGui.QHeaderView.ResizeToContents )

    def findMirroredItem( self ):
        ''' find a item mirrored in widget 2 and 3 '''
        #self.deselectTreeWidgetsItem()
        widgets = [self.Delete_treeWidget02, self.Delete_treeWidget03]

        for eachWidget in widgets:
            self.selectMirroredItems(eachWidget)

    def folderDialog( self, arg ):
        ''' Set lineEdits'''
        dialog = QtGui.QFileDialog()

        path = dialog.getExistingDirectory( self, "Select Folder", os.getcwd() )

        if path:

            if arg == 'delete1_dir':
                self.Delete_lineEdit01.setText( path )

            elif arg == 'delete2_dir':
                self.Delete_lineEdit02.setText( path )

            elif arg == 'delete3_dir':
                self.Delete_lineEdit03.setText( path )


    def populateTreeViews(self):
        ''' poplute the widgets'''
        widgets = { self.Delete_treeWidget01:self.Delete_lineEdit01, self.Delete_treeWidget02:self.Delete_lineEdit02, self.Delete_treeWidget03:self.Delete_lineEdit03 }

        for treeWidget, lineEdit in widgets.iteritems():
            folderPath = lineEdit.text()

            if folderPath:
                treeWidget.clear()
                self.populateTreeWidgetSearch( treeWidget, folderPath )


    def populateTreeWidgetSearch( self, treeWidget, folderPath ):
        rootTreeView = None
        def searchFolder( folder, rootTreeView ):
            folders = os.listdir( folder )
            for eachSubFolder in folders:
                childFolder = os.path.join( folder, eachSubFolder )
                if os.path.isdir( childFolder ):
                    if rootTreeView is None:
                        rootTreeView = treeWidget.invisibleRootItem()

                    item = QtGui.QTreeWidgetItem( rootTreeView )
                    item.setText( 0, eachSubFolder )
                    item.setExpanded( True )
                    item.parent()

                    treeWidget.addTopLevelItem( item )

                    if os.listdir( childFolder ):
                        searchFolder( childFolder, item )

        if folderPath:
            searchFolder( folderPath, rootTreeView )


    def deleteFolders( self ):
        def countFolder( folder):
            folders = os.listdir( folder )
            for eachSubFolder in folders:
                eachSubFolder = os.path.join( folder, eachSubFolder )
                if os.path.isdir( eachSubFolder ):
                    self.foIndex += 1
                    if os.listdir( eachSubFolder ):
                        countFolder( eachSubFolder)
                else:
                    self.fiIndex += 1


        def findFolders():
            folderPath = []
            widgets = {self.Delete_treeWidget01:self.Delete_lineEdit01, self.Delete_treeWidget02:self.Delete_lineEdit02, self.Delete_treeWidget03:self.Delete_lineEdit03}
            for treeWidget, lineEdit in widgets.iteritems():
                selectedItems = treeWidget.selectedItems()
                for item in selectedItems:
                    basePath = lineEdit.text()
                    if basePath:
                        treeWidgetHierarchy = self.getTreePath( item )
                        folderPath.append( os.path.join( basePath, treeWidgetHierarchy ) )

            return folderPath

        infoDict = []
        for eachFolder in findFolders():
            self.fiIndex = 0
            self.foIndex = 0     
            countFolder( eachFolder)
            infoDict.append({'deletionFolder':eachFolder, 'files':self.fiIndex, 'folders':self.foIndex})

        self.populateTreeViews()


    def deselectTreeWidgetsItem(self):
        # Deselect everything from previews selection
        widgets = [self.Delete_treeWidget02, self.Delete_treeWidget03]
        for eachWidget in widgets:
            for eachSel in eachWidget.selectedItems():
                eachSel.setSelected( False )

    def selectMirroredItems(self, widget):
        # Get selected item
        item = self.Delete_treeWidget01.selectedItems()[0]

        # Path to the selected item
        itemChilds = self.getTreePath( item )

        # Find all top items to iterate through
        topItems = widget.findItems( itemChilds.split( '/' )[0],QtCore.Qt.MatchExactly,0 )

        # Default state of variables 
        itemFound = None
        listCheck = []
        index = 1

        if topItems:
            TreeWidgetItems = QtGui.QTreeWidgetItemIterator( topItems[0] )
            TreeWidgetItemList = self.remakeList( TreeWidgetItems )

            for child in itemChilds.split( '/' ):
                index = index + 1
                for item in TreeWidgetItemList:
                    itemlist = self.getTreePath( item )
                    if child == item.text( 0 ):
                        listCheck.append( item.text( 0 ))
                        if ( itemlist in itemChilds ) and ( len( itemChilds.split('/')) < index ):
                            itemFound = item

            if itemFound:
                widget.scrollToItem(itemFound)
                itemFound.setSelected( True )

    def remakeList(self, treeWidgetList):
        itemList = []
        for it in treeWidgetList:
            itemList.append(it.value())

        return itemList

    def getTreePath( self, item ):
        path = []
        while item is not None:
            path.append( str( item.text( 0 )))
            item = item.parent()
        return '/'.join( reversed( path ))


if __name__ == '__main__':
    app = QApplication( sys.argv )
    frame = MainWindow()
    frame.show()    
    app.exec_()

窗口:

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

# Form implementation generated from reading ui file 'C:/Users/stmahe1/GoogleDrive_Group3/TA_SpaceShooter/scripts/WIP/mattias/PipelineToolbox/Mirror/ui/Toolbox_Test.ui'
#
# Created: Tue Apr 08 13:19:23 2014
#      by: pyside-uic 0.2.15 running on PySide 1.2.1
#
# WARNING! All changes made in this file will be lost!

from PySide import QtCore, QtGui

class Ui_MirrorFolderCreation(object):
    def setupUi(self, MirrorFolderCreation):
        MirrorFolderCreation.setObjectName("MirrorFolderCreation")
        MirrorFolderCreation.resize(807, 626)
        self.centralwidget = QtGui.QWidget(MirrorFolderCreation)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout_8 = QtGui.QVBoxLayout(self.centralwidget)
        self.verticalLayout_8.setObjectName("verticalLayout_8")
        self.verticalLayout_4 = QtGui.QVBoxLayout()
        self.verticalLayout_4.setObjectName("verticalLayout_4")
        self.label_6 = QtGui.QLabel(self.centralwidget)
        font = QtGui.QFont()
        font.setFamily("Orator Std")
        font.setPointSize(11)
        font.setWeight(75)
        font.setBold(True)
        self.label_6.setFont(font)
        self.label_6.setObjectName("label_6")
        self.verticalLayout_4.addWidget(self.label_6)
        self.horizontalLayout_4 = QtGui.QHBoxLayout()
        self.horizontalLayout_4.setObjectName("horizontalLayout_4")
        self.verticalLayout_6 = QtGui.QVBoxLayout()
        self.verticalLayout_6.setObjectName("verticalLayout_6")
        self.horizontalLayout_20 = QtGui.QHBoxLayout()
        self.horizontalLayout_20.setObjectName("horizontalLayout_20")
        self.Delete_lineEdit01 = QtGui.QLineEdit(self.centralwidget)
        self.Delete_lineEdit01.setObjectName("Delete_lineEdit01")
        self.horizontalLayout_20.addWidget(self.Delete_lineEdit01)
        self.Delete_pushButton01 = QtGui.QPushButton(self.centralwidget)
        self.Delete_pushButton01.setMaximumSize(QtCore.QSize(30, 16777215))
        self.Delete_pushButton01.setObjectName("Delete_pushButton01")
        self.horizontalLayout_20.addWidget(self.Delete_pushButton01)
        self.verticalLayout_6.addLayout(self.horizontalLayout_20)
        self.Delete_treeWidget01 = QtGui.QTreeWidget(self.centralwidget)
        self.Delete_treeWidget01.setEnabled(True)
        self.Delete_treeWidget01.setObjectName("Delete_treeWidget01")
        self.Delete_treeWidget01.header().setVisible(False)
        self.Delete_treeWidget01.header().setStretchLastSection(False)
        self.verticalLayout_6.addWidget(self.Delete_treeWidget01)
        self.horizontalLayout_4.addLayout(self.verticalLayout_6)
        self.verticalLayout_5 = QtGui.QVBoxLayout()
        self.verticalLayout_5.setObjectName("verticalLayout_5")
        self.horizontalLayout_19 = QtGui.QHBoxLayout()
        self.horizontalLayout_19.setObjectName("horizontalLayout_19")
        self.Delete_lineEdit02 = QtGui.QLineEdit(self.centralwidget)
        self.Delete_lineEdit02.setObjectName("Delete_lineEdit02")
        self.horizontalLayout_19.addWidget(self.Delete_lineEdit02)
        self.Delete_pushButton02 = QtGui.QPushButton(self.centralwidget)
        self.Delete_pushButton02.setMaximumSize(QtCore.QSize(30, 16777215))
        self.Delete_pushButton02.setObjectName("Delete_pushButton02")
        self.horizontalLayout_19.addWidget(self.Delete_pushButton02)
        self.verticalLayout_5.addLayout(self.horizontalLayout_19)
        self.Delete_treeWidget02 = QtGui.QTreeWidget(self.centralwidget)
        self.Delete_treeWidget02.setObjectName("Delete_treeWidget02")
        self.Delete_treeWidget02.headerItem().setText(0, "1")
        self.Delete_treeWidget02.header().setVisible(False)
        self.Delete_treeWidget02.header().setStretchLastSection(False)
        self.verticalLayout_5.addWidget(self.Delete_treeWidget02)
        self.horizontalLayout_4.addLayout(self.verticalLayout_5)
        self.verticalLayout_7 = QtGui.QVBoxLayout()
        self.verticalLayout_7.setObjectName("verticalLayout_7")
        self.horizontalLayout_21 = QtGui.QHBoxLayout()
        self.horizontalLayout_21.setObjectName("horizontalLayout_21")
        self.Delete_lineEdit03 = QtGui.QLineEdit(self.centralwidget)
        self.Delete_lineEdit03.setObjectName("Delete_lineEdit03")
        self.horizontalLayout_21.addWidget(self.Delete_lineEdit03)
        self.Delete_pushButton03 = QtGui.QPushButton(self.centralwidget)
        self.Delete_pushButton03.setMaximumSize(QtCore.QSize(30, 16777215))
        self.Delete_pushButton03.setObjectName("Delete_pushButton03")
        self.horizontalLayout_21.addWidget(self.Delete_pushButton03)
        self.verticalLayout_7.addLayout(self.horizontalLayout_21)
        self.Delete_treeWidget03 = QtGui.QTreeWidget(self.centralwidget)
        self.Delete_treeWidget03.setObjectName("Delete_treeWidget03")
        self.Delete_treeWidget03.headerItem().setText(0, "1")
        self.Delete_treeWidget03.header().setVisible(False)
        self.verticalLayout_7.addWidget(self.Delete_treeWidget03)
        self.horizontalLayout_4.addLayout(self.verticalLayout_7)
        self.verticalLayout_4.addLayout(self.horizontalLayout_4)
        self.horizontalLayout_5 = QtGui.QHBoxLayout()
        self.horizontalLayout_5.setObjectName("horizontalLayout_5")
        self.Delete_populatePushButton = QtGui.QPushButton(self.centralwidget)
        self.Delete_populatePushButton.setObjectName("Delete_populatePushButton")
        self.horizontalLayout_5.addWidget(self.Delete_populatePushButton)
        spacerItem = QtGui.QSpacerItem(20, 20, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum)
        self.horizontalLayout_5.addItem(spacerItem)
        self.Delete_deletePushButton = QtGui.QPushButton(self.centralwidget)
        self.Delete_deletePushButton.setObjectName("Delete_deletePushButton")
        self.horizontalLayout_5.addWidget(self.Delete_deletePushButton)
        self.verticalLayout_4.addLayout(self.horizontalLayout_5)
        self.verticalLayout_8.addLayout(self.verticalLayout_4)
        self.gridLayout_4 = QtGui.QGridLayout()
        self.gridLayout_4.setObjectName("gridLayout_4")
        self.verticalLayout_8.addLayout(self.gridLayout_4)
        MirrorFolderCreation.setCentralWidget(self.centralwidget)
        self.actionExit = QtGui.QAction(MirrorFolderCreation)
        self.actionExit.setObjectName("actionExit")
        self.actionLoad_preset = QtGui.QAction(MirrorFolderCreation)
        self.actionLoad_preset.setObjectName("actionLoad_preset")
        self.actionSave_Preset = QtGui.QAction(MirrorFolderCreation)
        self.actionSave_Preset.setObjectName("actionSave_Preset")

        self.retranslateUi(MirrorFolderCreation)
        QtCore.QMetaObject.connectSlotsByName(MirrorFolderCreation)
        MirrorFolderCreation.setTabOrder(self.Delete_lineEdit01, self.Delete_lineEdit02)
        MirrorFolderCreation.setTabOrder(self.Delete_lineEdit02, self.Delete_lineEdit03)
        MirrorFolderCreation.setTabOrder(self.Delete_lineEdit03, self.Delete_pushButton01)
        MirrorFolderCreation.setTabOrder(self.Delete_pushButton01, self.Delete_pushButton02)
        MirrorFolderCreation.setTabOrder(self.Delete_pushButton02, self.Delete_pushButton03)
        MirrorFolderCreation.setTabOrder(self.Delete_pushButton03, self.Delete_populatePushButton)
        MirrorFolderCreation.setTabOrder(self.Delete_populatePushButton, self.Delete_deletePushButton)
        MirrorFolderCreation.setTabOrder(self.Delete_deletePushButton, self.Delete_treeWidget01)
        MirrorFolderCreation.setTabOrder(self.Delete_treeWidget01, self.Delete_treeWidget02)
        MirrorFolderCreation.setTabOrder(self.Delete_treeWidget02, self.Delete_treeWidget03)

    def retranslateUi(self, MirrorFolderCreation):
        MirrorFolderCreation.setWindowTitle(QtGui.QApplication.translate("MirrorFolderCreation", "Technical Artist Toolbox", None, QtGui.QApplication.UnicodeUTF8))
        self.label_6.setText(QtGui.QApplication.translate("MirrorFolderCreation", "Mirrored Deletion of Folders", None, QtGui.QApplication.UnicodeUTF8))
        self.Delete_pushButton01.setText(QtGui.QApplication.translate("MirrorFolderCreation", "...", None, QtGui.QApplication.UnicodeUTF8))
        self.Delete_pushButton02.setText(QtGui.QApplication.translate("MirrorFolderCreation", "...", None, QtGui.QApplication.UnicodeUTF8))
        self.Delete_pushButton03.setText(QtGui.QApplication.translate("MirrorFolderCreation", "...", None, QtGui.QApplication.UnicodeUTF8))
        self.Delete_populatePushButton.setText(QtGui.QApplication.translate("MirrorFolderCreation", "Update", None, QtGui.QApplication.UnicodeUTF8))
        self.Delete_deletePushButton.setText(QtGui.QApplication.translate("MirrorFolderCreation", "Delete", None, QtGui.QApplication.UnicodeUTF8))
        self.actionExit.setText(QtGui.QApplication.translate("MirrorFolderCreation", "Exit", None, QtGui.QApplication.UnicodeUTF8))
        self.actionLoad_preset.setText(QtGui.QApplication.translate("MirrorFolderCreation", "Load Preset", None, QtGui.QApplication.UnicodeUTF8))
        self.actionSave_Preset.setText(QtGui.QApplication.translate("MirrorFolderCreation", "Save Preset", None, QtGui.QApplication.UnicodeUTF8))

经过进一步测试,可能是因为我在这些控件中选中了某些东西,(我发现如果不运行findMirroredItem(),它就能正常工作),但即使我先取消选择所有内容,这也没有帮助。

如果我点击第一个控件,它会尝试在其他两个控件中选择一个类似的对象。如果我不点击第一个控件,我可以随意重新加载/填充它,程序不会崩溃。如果我之前选中了某个东西,尝试重新加载/填充时最终会崩溃。

1 个回答

3

这个问题似乎是因为在使用 QTreeWidgetItemIterator 时,传入 QTreeWidgetItem 作为第一个参数导致的一个 PySide 的 bug。当把 QTreeWidget 作为第一个参数时,这个问题就消失了。这个 bug 在相应的 PyQt4 代码中不会出现。

可以通过修改 selectMirroredItems 中的以下一行来修复示例代码:

    # TreeWidgetItems = QtGui.QTreeWidgetItemIterator( topItems[0] )
    TreeWidgetItems = QtGui.QTreeWidgetItemIterator(widget)

似乎仅仅遍历 QTreeWidgetItemIterator 就足以导致程序崩溃(虽然我没有费心去创建一个最小的测试案例)。

如果发现 PySide 的 bug,可以在 这里 报告。

撰写回答