Efficient way to write a QListWidget with editable QListWidgetItem

Hi,
Am writing a renaming tool for maya. Its almost done but Im stuck on a issue.

I’m following Rob’s book “Practical Maya Programming with Python”. So I’m keeping the Qt(PySide) code seperate from my maya code.

Here’s a quick laydown:

#The window contains two list widgets (old names, new names) and two buttons (load objects, rename objects). Old names list is not editable.

The UI is made with Qt Designer and converted to python using pysideuic.

The way its supposed to work:

  1. Click “Load Objects” button to load selected objects(names) on both list widgets.
  2. User edits the text on the “new names” list widget.
  3. Click the “Rename Objects” button to rename objects on the “old names” widget with the naming from the “new names” widget.

It works the first time. After that, I get this error:

RuntimeError: Internal C++ object (PySide.QtGui.QListWidgetItem) already deleted.

I create the QListWidgetItem in the loop as I want the “new names” list to be editable. I can
understand qt/python clearing it as garbage collection. How can I create the QListWidgetItem properly so it doesn’t get cleared on refresh.

Can someone help me understand and write this efficiently so I can keep the list editable and
refresh/recreate the QListWidgetItem on each object load. What I’ve written is quite crude.

The ui file(converted to python) is attached with this post.renameTest.zip (1.1 KB)
Here’s the extracted code: (It also contains a test function so you can test-run it with mayapy on a console)



from qtshim import QtGui, QtCore, Signal
from renameTest import Ui_MainWindow as rePy


class RenamerController(QtCore.QObject):
    objList = Signal(list)


class RenamerWindow(QtGui.QMainWindow):
    customRenameClicked = Signal(list, list)
    loadObjsClicked = Signal(list)


class Renamer(RenamerWindow):
    def __init__(self, controller, parent=None, ):
        super(Renamer, self).__init__(parent)
        self.ui = rePy()
        self.ui.setupUi(self)

        self.ui.load_objs_btn.clicked.connect(self.oncommit)
        self.ui.custom_rename_btn.clicked.connect(self.oncommit)

        controller.objList.connect(self.update_loaded_objs)
        
        self.newList = []

    def oncommit(self):
        sender = self.sender().objectName()

        if sender == 'load_objs_btn':
            self.loadObjsClicked.emit('')

        elif sender == 'custom_rename_btn':
            old_list = []
            for index in xrange(self.ui.loaded_objs_list.count()):
                text = self.ui.loaded_objs_list.item(index).text()
                old_list.append(text)
            new_list = []
            for item in self.newList:
                new_list.append(item.text())
            self.customRenameClicked.emit(old_list, new_list)

    def update_loaded_objs(self, load_objs):

        self.ui.loaded_objs_list.clear()
        self.ui.loaded_objs_list.addItems(load_objs)
        self.ui.preview_new_names_list.clear()

        for obj in load_objs:
            obj = QtGui.QListWidgetItem(obj)
            obj.setFlags(QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsSelectable |
                         QtCore.Qt.ItemIsEnabled)
            self.ui.preview_new_names_list.addItem(obj)
            self.newList.append(obj)


def _pytest():

    controller = RenamerController()

    def objs_list():
        selected_objs = (
            'pSphere1',
            'pCube1',
            'polyCube1',
            'nurbsCircle'
        )
        return selected_objs

    def load_objs():
        controller.objList.emit(objs_list())

    def custom_rename(old_list, new_list):
        for i in range(len(old_list)):
            print '%s renamed to %s' % (old_list[i], new_list[i])

    app = QtGui.QApplication([])
    win = Renamer(controller)

    win.loadObjsClicked.connect(load_objs)
    win.customRenameClicked.connect(custom_rename)

    win.show()
    app.exec_()


if __name__ == '__main__':
    _pytest()

I’ve tried to be as clear as possible. :slight_smile:

Had posted the same question on the Python_inside_maya google group.
Almost fixed it. :nod:
https://groups.google.com/forum/#!topic/python_inside_maya/boCHB8cGbas

you arent resetting self.newList. So it contains references to QListWidgetItems that get deleted in the .clear() calls. So your python list has a reference to a pyside object, but the OWNERSHIP is to the QListWidget ui.preview_new_names_list, so when that gets cleared the pyside objects get deleted and your python list is SOL. in the update_loaded_objs function, just set self.newList = [] and you should be good

[QUOTE=TheMaxx;27882]you arent resetting self.newList. So it contains references to QListWidgetItems that get deleted in the .clear() calls. So your python list has a reference to a pyside object, but the OWNERSHIP is to the QListWidget ui.preview_new_names_list, so when that gets cleared the pyside objects get deleted and your python list is SOL. in the update_loaded_objs function, just set self.newList = and you should be good[/QUOTE]

Thanks for the find! I ended up making a custom item type for my purposes as is discussed in the google group linked above. But I’m glad i found out the error in my earlier code. Will surely help next time.