pythonpyqt5readonlyqlineeditqcompleter

PyQt5: make QCompleter popup readonly


I have a QCompleter for a QLineEdit and it's working fine.

All I want is for this completer to be only read-only. So the user can only see the matches but cannot select any one of them. No highlighting, no selected item. Only a visible list of matches should be shown.

I tried for so long, but I'm still stuck and could not complete the task.

This is the code:

from PyQt5.QtWidgets import QApplication, QWidget, QCompleter, QLineEdit, QVBoxLayout
from PyQt5.QtCore import Qt, QStringListModel
import sys

class MainWindow(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.testthislineEdit = QLineEdit()

        temp_list = ['alpha', 'beta', 'boota', 'beita']

        model = QStringListModel()
        model.setStringList(temp_list)

        completer = QCompleter()
        completer.setFilterMode(Qt.MatchContains)
        completer.setCaseSensitivity(Qt.CaseSensitivity(0))
        completer.setModel(model)
        layout = QVBoxLayout()
        layout.addWidget(self.testthislineEdit)
        self.setLayout(layout)

        self.testthislineEdit.setCompleter(completer)

if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()

try:
    sys.exit(app.exec_())
except SystemExit:
    print('Closing Window...')

Solution

  • One way to do this would be to use a custom item-view and selection-model with reimplemented methods that disable all the default selection, mouse and keyboard handling. It will also be necessary to ensure the custom selection-model is linked to the completer's internal proxy model.

    Here is a working demo based on your example:

    from PyQt5.QtWidgets import QApplication, QWidget, QCompleter, QLineEdit, QVBoxLayout, QListView
    from PyQt5.QtCore import Qt, QStringListModel, QItemSelectionModel, QAbstractProxyModel
    import sys
    
    class SelectionModel(QItemSelectionModel):
        def select(self, *args, **kwargs):
            # disable selection
            pass
    
        def setCurrentIndex(self, *args, **kwargs):
            # disable current index
            pass
    
    class ListView(QListView):
        def setSelectionModel(self, model):
            # link custom selection model to completer proxy model
            super().setSelectionModel(SelectionModel(model.model(), self))
    
        def mousePressEvent(self, event):
            # ignore mouse events
            self.close()
    
    class MainWindow(QWidget):
        def __init__(self, parent=None):
            super().__init__(parent)
            self.testthislineEdit = QLineEdit()
    
            temp_list = ['alpha', 'beta', 'boota', 'beita']
    
            model = QStringListModel()
            model.setStringList(temp_list)
    
            completer = QCompleter()
            completer.setFilterMode(Qt.MatchContains)
            completer.setCaseSensitivity(Qt.CaseSensitivity(0))
    
            # set custom popup
            completer.setPopup(ListView(self))
    
            completer.setModel(model)
            layout = QVBoxLayout()
            layout.addWidget(self.testthislineEdit)
            self.setLayout(layout)
    
            self.testthislineEdit.setCompleter(completer)
    
    if __name__ == "__main__":
    
        app = QApplication(sys.argv)
        window = MainWindow()
        window.show()
    
        try:
            sys.exit(app.exec_())
        except SystemExit:
            print('Closing Window...')