PySide2: Проблемы с автозавершением кода и управлением фокусом в QListWidget

Вопрос или проблема

Я пытаюсь создать систему автозавершения кода, подобную IntelliSense, внутри QPlainTextEdit с использованием PySide2. Однако у меня возникают проблемы с управлением фокусом и скрытием QListWidget, когда он появляется. Цель состоит в том, чтобы всплывающее окно появлялось, когда пользователь вводит текст, позволяя ему продолжать ввод в текстовый редактор, пока всплывающее окно видно, и скрывать его по мере необходимости (например, при нажатии клавиши Escape или перемещении курсора).

Вот основные проблемы, с которыми я сталкиваюсь:

Проблема с фокусом: я использую self.completer_list.setFocusPolicy(Qt.NoFocus), чтобы предотвратить получение фокуса всплывающим окном, но при использовании Qt.Popup фокус все равно сдвигается на QListWidget, и остальная часть программы становится неответственной, пока я не взаимодействую с всплывающим окном.

Проблема скрытия всплывающего окна: я использую self.completer_list.hide(), чтобы скрыть всплывающее окно, но оно не всегда скрывается должным образом или не реагирует хорошо, когда щелкаешь вне или когда курсор перемещается.

Что может быть не так в моем коде?

Это часть моего кода:

self.temp_completer_list = ["for", "while", "if", "else", "elif", "def",
                           "class", "import", "from", "return", "try", "except", "finally"]
    self.completer_list = QListWidget()
    self.completer_list.addItems(self.temp_completer_list)
    self.completer_list.setWindowFlag(Qt.Popup)
    self.completer_list.hide()
    self.textChanged.connect(self.show_completer_list)

def show_completer_list(self):
    """КУРСОР ДЛЯ ВСПЛЫВАЮЩЕГО ОКНА"""
    cursor = self.textCursor()
    pos = self.cursorRect(cursor).bottomRight()
    global_pos = self.mapToGlobal(pos)
    self.completer_list.move(global_pos)
    self.completer_list.show()
    self.completer_list.setFocusPolicy(Qt.NoFocus)

Ответ или решение

Ваша задача состоит в том, чтобы создать систему автозаполнения кода в QPlainTextEdit с использованием PySide2, но вы сталкиваетесь с проблемами управления фокусом и скрытия виджета QListWidget. Давайте рассмотрим, как можно исправить эти проблемы.

Проблема управления фокусом

Ваша проблема с управлением фокусом может быть связана с тем, как QWidget обрабатывает всплывающие окна (Popup). Установка setFocusPolicy(Qt.NoFocus) не всегда предотвращает переход фокуса на виджет. Вместо этого, можно использовать метод setWindowFlags с Qt.Tool или Qt.Window | Qt.FramelessWindowHint, чтобы избежать проблем с фокусом.

Проблема скрытия всплывающего окна

Для корректного скрытия списка можно использовать сигналы. Например, добавьте обработчики для событий фокуса и кликов мыши. Вот пример того, как можно доработать ваш код.

Полный код с решением

from PySide2.QtWidgets import QApplication, QPlainTextEdit, QListWidget
from PySide2.QtCore import Qt, QPoint

class Editor(QPlainTextEdit):
    def __init__(self):
        super().__init__()

        self.temp_completer_list = ["for", "while", "if", "else", "elif", "def",
                                    "class", "import", "from", "return", "try", 
                                    "except", "finally"]
        self.completer_list = QListWidget()
        self.completer_list.addItems(self.temp_completer_list)
        self.completer_list.setWindowFlags(Qt.Tool | Qt.FramelessWindowHint)  # Используйте эти флаги
        self.completer_list.hide()

        self.textChanged.connect(self.show_completer_list)

        # Обработчики событий
        self.completer_list.installEventFilter(self)
        self.completer_list.mousePressEvent = self.completer_mouse_press_event
        self.completer_list.focusOutEvent = self.completer_focus_out_event

    def show_completer_list(self):
        """Показать список автозаполнения."""
        cursor = self.textCursor()
        if cursor.hasSelection():
            return

        pos = self.cursorRect(cursor).bottomRight()
        global_pos = self.mapToGlobal(pos)

        if self.completer_list.isHidden():
            self.completer_list.move(global_pos)
            self.completer_list.show()
        else:
            self.completer_list.hide()

    def completer_mouse_press_event(self, event):
        """Обработчик события нажатия мыши для списка автозаполнения."""
        super().mousePressEvent(event)
        self.completer_list.hide()  # Скрыть список при клике на текстовое поле

    def completer_focus_out_event(self, event):
        """Обработчик события потери фокуса для списка автозаполнения."""
        self.completer_list.hide()  # Скрыть список при потере фокуса
        super().focusOutEvent(event)

    def keyPressEvent(self, event):
        """Обработка нажатия клавиш для скрытия списка при Escape."""
        if event.key() == Qt.Key_Escape:
            self.completer_list.hide()
        else:
            super().keyPressEvent(event)

if __name__ == "__main__":
    app = QApplication([])
    editor = Editor()
    editor.show()
    app.exec_()

Объяснение изменений:

  1. Флаги окна: Установка Qt.Tool | Qt.FramelessWindowHint позволяет избежать проблем с фокусом во время работы с всплывающим окном.

  2. Обработчики событий: Мы добавили обработчики для обработки кликов мышью и потери фокуса. Это позволяет скрывать popup, когда взаимодействие происходит вне него или когда он теряет фокус.

  3. Скрытие при Escape: Добавлен обработчик keyPressEvent, который скрывает всплывающее окно при нажатии клавиши Escape.

Теперь ваше приложение должно корректно отображать popup с автозаполнением и управлять им без проблем с фокусом или скрытием.

Оцените материал
Добавить комментарий

Капча загружается...