Home > database >  Prevent QCompleter's popup from hiding itself on connected QLineEdit's text changes
Prevent QCompleter's popup from hiding itself on connected QLineEdit's text changes

Time:08-27

I'm trying to have a QCompleter that's updated with suggestions from a remote api that's being requested on every text change (e.g. like the google search bar). This is a bit problematic, because the completer hides its popup after text is added to the line edit, and then doesn't show the popup after the model is updated in the slot connected to the reply's finished signal.

The completer not showing after the update can be solved by a call to its complete method, but this causes flicker on the popup because of the hiding before a response is received.

I believe the popup is being hidden here https://code.qt.io/cgit/qt/qtbase.git/tree/src/widgets/util/qcompleter.cpp?h=6.3.1#n1392, but overriding the event filter to show the popup still causes some flicker, and the suggestions disappear until the model is updated in the update_callback.

from PySide6 import QtCore, QtWidgets
from __feature__ import snake_case, true_property  # noqa: F401

class Window(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.main_widget = QtWidgets.QWidget(self)
        self.set_central_widget(self.main_widget)
        self.layout_ = QtWidgets.QVBoxLayout(self.main_widget)

        self.line_edit = QtWidgets.QLineEdit(self)
        self.layout_.add_widget(self.line_edit)

        self.completer_model = QtCore.QStringListModel(self)
        self.completer = QtWidgets.QCompleter(self.completer_model, self)
        self.line_edit.textEdited.connect(self.update_model)
        self.line_edit.set_completer(self.completer)

    def update_model(self, query: str):
        """Simulate the network requests that calls self.update_callback when finished."""
        QtCore.QTimer.single_shot(0, lambda: self.update_callback(query))
        #self.completer_model.set_string_list([query   "completedtext"])

    def update_callback(self, query):
        self.completer_model.set_string_list([query   "completedtext"])

app = QtWidgets.QApplication()
window = Window()
window.show()
app.exec()

CodePudding user response:

By using setCompleter() the line edit automatically calls setCompletionPrefix() anytime its text is changed. This is done right after the text is updated, so the completer is always updated "too soon" with the previous text, and that can cause flickering.

A possible solution is to always trigger the completer manually, which is achieved by using setWidget() on the completer (instead of setCompleter()) and explicitly calling complete().

class Window(QtWidgets.QMainWindow):
    def __init__(self):
        # ...
        self.completer.setWidget(self.line_edit)

    # ...

    def update_callback(self, query):
        self.completer_model.setStringList([query   "completedtext"])
        self.completer.complete()
  • Related