1. Anuncie Aqui ! Entre em contato fdantas@4each.com.br

[Python] Debounced Completer autoselects highlighted option when I stop arrowing through...

Discussão em 'Python' iniciado por Stack, Outubro 25, 2024 às 13:52.

  1. Stack

    Stack Membro Participativo

    I'm trying to create a debounced Typeahead widget that populates with data from my database. When I try to down arrow through the query results, the highlighted result autoselects when I stop pressing the down arrow key.

    I looked at the docs for the QCompleter and the return type of popup, QAbstractItemView. QAbstractItemView, says Ctrl+Arrow keys, "Changes the current item but does not select it.". However, when I try navigating with the Ctrl key held, the behavior is no different.

    The code for my widget is below:

    # Typeahead.h
    from PyQt5.QtWidgets import QLineEdit
    from PyQt5.QtCore import QTimer
    from PyQt5.QtWidgets import QCompleter
    from utk_apis.sql_engine import SQLEngine
    from PyQt5.QtCore import QStringListModel


    from PyQt5.QtCore import QTimer, QStringListModel
    from PyQt5.QtWidgets import QLineEdit, QCompleter, QAbstractItemView


    class Typeahead(QLineEdit):
    def __init__(self, parent=None):
    super(Typeahead, self).__init__(parent)
    self.results = []
    self.selected_result = None
    self.engine = None
    self.current_index = -1
    self.ignore_text_change = False
    self.completer = QCompleter(self)

    self.completer.setWidget(self)
    self.completer.setCompletionMode(QCompleter.PopupCompletion)
    self.completer.popup().setSelectionMode(
    QAbstractItemView.SingleSelection
    ) # Single item selection in popup

    self.setCompleter(self.completer)
    self.completer.popup().setAlternatingRowColors(True)

    # Create a timer for debouncing
    self.debounce_timer = QTimer(self)
    self.debounce_timer.setSingleShot(True) # Only trigger once after interval
    self.debounce_timer.setInterval(300) # 300 ms debounce interval

    # Connect the timeout of the timer to the emit_text_changed method
    self.debounce_timer.timeout.connect(self.emit_text_changed)

    # Connect the textChanged signal to start the debounce timer
    self.textChanged.connect(self.start_debounce)

    # Connect the completer's activated signal to set the selected item only when Enter is pressed
    self.completer.activated.connect(self.on_completer_activated)


    def start_debounce(self):
    """Start the debounce timer when the text changes."""
    self.debounce_timer.start() # This starts or restarts the debounce timer

    def emit_text_changed(self):
    """Fetch results and update completer when text changes."""
    print(f'Emitting text changed event: {self.text()} Ignore text change: {self.ignore_text_change}')

    if self.ignore_text_change is True:
    self.ignore_text_change = False
    return
    if self.engine is None:
    self.engine = SQLEngine(env=self.property("env"))

    # Run the query to fetch data from the database
    data = self.engine.run_query(self._build_query(), results_as_dict=True)

    # Convert data to the list of results. Store for sharing with other Typeahead instances
    self.results = [
    {
    "text": f"{row['primary_key_1']} ({row['primary_key_2']}, {row['primary_key_3']})",
    "primary_key_1": row["primary_key_1"],
    "primary_key_2": row["primary_key_2"],
    "primary_key_3": row["primary_key_3"],
    }
    for row in data
    ]

    # Update the completer with the new results
    self.update_completer()

    def update_completer(self):
    """Update the QCompleter with the new results."""
    completer_model = QStringListModel([result["text"] for result in self.results])

    #self.completer.model().setStringList(completer_model)
    self.completer.setModel(completer_model)

    # Set the width of the popup based on the longest string to avoid truncation
    longest_string = max(
    [len(result["text"]) for result in self.results], default=0
    )

    self.completer.popup().setMinimumWidth(
    longest_string * 15
    ) # Adjust 7 to fit font size

    # Manually open the completer dropdown
    if self.results:
    self.completer.complete() # Force the dropdown to show up again

    def on_completer_activated(self, text):
    """Handle what happens when an item in the dropdown is selected."""
    # Only set the text when the user selects an item by pressing Enter
    selected_item = next(
    (result for result in self.results if result["text"] == text), None
    )
    if selected_item:
    self.setText(selected_item["text"])

    def _build_query(self):
    """Build the SQL query for fetching suggestions."""
    query = f"SELECT {','.join(self.property('primary_keys'))} FROM {self.property('targetTable')} WHERE {self.property('targetField')} LIKE '%{self.text()}%'"
    return query

    Continue reading...

Compartilhe esta Página