PySide crash on exit (using QCompleter)

297 Views Asked by At

I have reproduced the "Custom Completer Example" from the Qt documentation using PySide (Python 2.7.3, PySide 1.1.2, Qt 4.8.1).

I have an issue where a win32 exception is thrown on exit (or on Mac OS X a access violation exception). On the Mac I can see a stack trace and the issue occurs during garbage collection, where references to QObjects are apparently not consistent, such that things go bad.

I can see this crash with the following self-contained script, only if a completer insertion was accepted. I.e. type the first few letters, then accept the completion.

On the other hand, if I have seen the completion list popup, but not accepted the completion, no crash occurs on exit.

################################################################################
# Completer.py
#
# A PySide port of the Qt 4.8 "Custom Completer Example"
# http://qt-project.org/doc/qt-4.8/tools-customcompleter.html
#
################################################################################

from PySide.QtCore import *
from PySide.QtGui import *

class TextEdit(QPlainTextEdit):

    def __init__(self, parent=None):
        super(TextEdit, self).__init__(parent)
        self.c = None

    def completer(self):
        return self.c

    def setCompleter(self, completer):
        if self.c:
            QObject.disconnect(self.c, 0, self, 0)

        self.c = completer

        if not self.c:
            return

        self.c.setWidget(self)
        self.c.setCompletionMode(QCompleter.PopupCompletion)
        self.c.setCaseSensitivity(Qt.CaseInsensitive)
        self.c.activated.connect(self.insertCompletion)

    def insertCompletion(self, completion):
        if self.c.widget() is not self:
            return
        tc = self.textCursor()
        extra = len(completion) - len(self.c.completionPrefix())
        tc.movePosition(QTextCursor.Left)
        tc.movePosition(QTextCursor.EndOfWord)
        tc.insertText(completion[-extra:])
        self.setTextCursor(tc)

    def textUnderCursor(self):
        tc = self.textCursor()
        tc.select(QTextCursor.WordUnderCursor)
        return tc.selectedText()

    def focusInEvent(self, event):
        if self.c:
            self.c.setWidget(self)
        super(TextEdit, self).focusInEvent(event)

    def keyPressEvent(self, e):
        if self.c and self.c.popup().isVisible():
            if e.key() in (Qt.Key_Enter, 
                           Qt.Key_Return,
                           Qt.Key_Escape,
                           Qt.Key_Tab,
                           Qt.Key_Backtab):
                e.ignore()
                return

        # Check for the shortcut combination Ctrl+E
        isShortcut = (e.modifiers() & Qt.ControlModifier) and e.key() == Qt.Key_E
        # Do not process the shortcut when we have a completion
        if not self.c or not isShortcut:
            super(TextEdit, self).keyPressEvent(e)

        noText = not e.text()
        ctrlOrShift = e.modifiers() & (Qt.ControlModifier | Qt.ShiftModifier)
        if not self.c or (ctrlOrShift and noText):
            return

        eow = "~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-=" # End of word
        hasModifier = (e.modifiers() != Qt.NoModifier) and not ctrlOrShift
        completionPrefix = self.textUnderCursor()

        if not isShortcut and \
           (hasModifier or noText or len(completionPrefix) < 1 or e.text()[-1:] in eow):
            self.c.popup().hide()
            return

        if completionPrefix != self.c.completionPrefix():
            self.c.setCompletionPrefix(completionPrefix)
            self.c.popup().setCurrentIndex( self.c.completionModel().index(0,0) )

        cr = self.cursorRect()
        cr.setWidth(self.c.popup().sizeHintForColumn(0) + \
                    self.c.popup().verticalScrollBar().sizeHint().width())
        self.c.complete(cr)


class Completer(QMainWindow):

    words = ("one",
             "two",
             "three",
             "four")

    def __init__(self, parent=None):
        super(Completer, self).__init__(parent)


        self.setWindowTitle("Completer")
        self.textEdit = TextEdit()
        self.completer = QCompleter(self)
        self.completer.setModelSorting(QCompleter.CaseInsensitivelySortedModel)
        self.completer.setCaseSensitivity(Qt.CaseInsensitive)
        self.completer.setWrapAround(False)
        self.completer.setModel(QStringListModel(Completer.words, self.completer))
        self.textEdit.setCompleter(self.completer)

        self.setCentralWidget(self.textEdit)
        self.resize(500, 300)
        self.setWindowTitle("Completer")


if __name__ == '__main__':
    import sys
    from PySide.QtGui import QApplication

    app = QApplication(sys.argv)
    window = Completer()
    window.show()    
    sys.exit(app.exec_())
0

There are 0 best solutions below