Question

First of I am new to python and pyside.

I have three different QTreeWidgets, representing three different folder structures. When I try to clear them all three in a row the application crashes.

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

I have also tried to clear it other ways, for instance

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

That also makes it crash the same way.

I clicked "debug" and visual studios gave me this message:

Unhandled exception at 0x000000006E9D2C68 (QtGui4.dll) in mayapy.exe: 0xC0000005: Access violation reading location 0xFFFFFFFFFFFFFFFF.

(It says maya because I am using eclipse with mayas python interpreter, because I mostly code for maya. This however is standalone, no maya involved.)

I remove a folder in the hierarchy that the QTreeWidgets are representing, to get it to show accurate again I want to clear it so I can populate it again.

When I googled the problem I got some people saying it had something to do with threading, but as I am new to python and pyside I don't even know how to thread things.

Anyone got any ideas how this can be done?

Edit:

The code

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_()

The window:

# -*- 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))

After some further testing it is probably because I have something selected in the widgets, (found that it works if I don't run findMirroredItem()) but it doesn't help to deselect to deselect everything first.

If I click the first widget, it tries to select a similar object in the other two. If I don't click in the first widget I can reload/populate it how many times I want and it will not crash. If I have had something selected it crashes eventually if I try to reload/populate it.

Was it helpful?

Solution

This seems to be caused by a PySide bug in QTreeWidgetItemIterator when passing a QTreeWidgetItem as the first argument. When a QTreeWidget is passed as the first argument, the problem goes away. This bug does not occur with equivalent PyQt4 code.

The example code can be fixed by amending the following line in selectMirroredItems:

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

It seems that simply iterating over the QTreeWidgetItemIterator is enough to cause the segfault (although I did not go to the trouble of attempting to create a minimal test case).

PySide bugs can be reported here.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top