pythonpyqt5qcompleter

PyQt5 QCompleter for QLineEdit crashing with no exception visible


I'm trying to use the QCompleter class for the QLineEdit to give auto completion suggestions while typing, and update the suggestions after the user enters a new text. But when I try to update the Completer with text that starts with something that is already at the completer list, it just crashes the entire app with no visible exception! Even try-except doesn't catch this error, and I can't understand what I am doing wrong...

Below is a simpler example of my code: it is a simple "echo" console application that gets commands from QLineEdit (input text box) and writing it to QTextBrowser (output text box). When entering entirely new "command" (text) it works fine, and being added to the completer, so next time I can see it. But if the new text starts similar to other words in the completer list, choosing it crashes the entire GUI app with no exception visible, not even when I'm running in debug mode...

Please see my example below, and try writing at the upper text box options like: a, aa, aaa (that start similar to completer already word: aaa1)

What am I doing wrong??

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QVBoxLayout, QLineEdit, QTextBrowser, QCompleter

class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)

        self.setWindowTitle('console')
        self.setGeometry(10, 50, 500, 800)

        # Create text box for input
        self.consoleCommandLineEdit = QLineEdit(self)
        self.consoleCommandLineEdit.setFixedHeight(25)
        self.consoleCommandLineEdit.editingFinished.connect(self.gotConsoleCommand)
        self.completerCommands = ['aaa1','aaa2','aaa3'] # initial completer list
        completer = QCompleter(self.completerCommands)
        self.consoleCommandLineEdit.setCompleter(completer)

        # Create text box for output
        self.consoleViewer = QTextBrowser(self)
        self.consoleViewer.setLineWrapMode(QTextBrowser.NoWrap)

        widget = QWidget(self)
        self.setCentralWidget(widget)
        self.vlay = QVBoxLayout(widget)
        self.vlay.addWidget(self.consoleCommandLineEdit)
        self.vlay.addWidget(self.consoleViewer)

    def gotConsoleCommand(self):
        cmd = self.consoleCommandLineEdit.text()
        self.consoleCommandLineEdit.setText('')
        self.sendCommandToConsole(cmd)

    def sendCommandToConsole(self,cmd):
        self.consoleViewer.append(cmd) # add cmd to output box
        if cmd not in self.completerCommands: # if the command is new, add it to the completer
            self.completerCommands.append(cmd)                  # 1. add the new text to the list we have
            completer = QCompleter(self.completerCommands)      # 2. create a new completer object
            self.consoleCommandLineEdit.setCompleter(completer) # 3. set the new completer as the LineEdit completer


if __name__ == "__main__":
    app = QApplication(sys.argv)
    mainWin = MainWindow()
    mainWin.show()
    sys.exit(app.exec_())


Solution

  • I have not yet found the cause of the problem but a better solution than creating a new QCompleter every time you need to add a new text. In this case it is better to use a model to store the texts that is the basis of the QCompleter information.

    import sys
    from PyQt5.QtGui import QStandardItem, QStandardItemModel
    from PyQt5.QtWidgets import (
        QApplication,
        QWidget,
        QMainWindow,
        QVBoxLayout,
        QLineEdit,
        QTextBrowser,
        QCompleter,
    )
    
    
    class MainWindow(QMainWindow):
        def __init__(self, parent=None):
            super(MainWindow, self).__init__(parent)
    
            self.setWindowTitle("console")
            self.setGeometry(10, 50, 500, 800)
    
            # Create text box for input
            self.consoleCommandLineEdit = QLineEdit()
            self.consoleCommandLineEdit.setFixedHeight(25)
            self.consoleCommandLineEdit.editingFinished.connect(self.gotConsoleCommand)
    
            self.model = QStandardItemModel()
            self.model.appendRow([QStandardItem(text) for text in ("aaa1", "aaa2", "aaa3")])
            completer = QCompleter(self.model, self)
            self.consoleCommandLineEdit.setCompleter(completer)
    
            # Create text box for output
            self.consoleViewer = QTextBrowser(lineWrapMode=QTextBrowser.NoWrap)
    
            widget = QWidget()
            self.setCentralWidget(widget)
            vlay = QVBoxLayout(widget)
            vlay.addWidget(self.consoleCommandLineEdit)
            vlay.addWidget(self.consoleViewer)
    
        def gotConsoleCommand(self):
            cmd = self.consoleCommandLineEdit.text()
            self.consoleCommandLineEdit.clear()
            self.sendCommandToConsole(cmd)
    
        def sendCommandToConsole(self, cmd):
            self.consoleViewer.append(cmd)  # add cmd to output box
            if not self.model.findItems(cmd):
                self.model.appendRow(QStandardItem(cmd))
    
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        mainWin = MainWindow()
        mainWin.show()
        sys.exit(app.exec_())
    

    Perhaps something that helps us understand the problem is that if you add a parent to QCompleter the problem does not happen:

    completer = QCompleter(self.completerCommands, self)

    but I stress: the best solution is to use a model.