Fatih Ünal
Fatih Ünal

Reputation: 37

PySide2: QListWidget Popup Code Completion and Focus Management Issues

I'm trying to build an IntelliSense-like code completion system inside a QPlainTextEdit using PySide2. However, I'm facing issues with focus management and hiding the QListWidget when it pops up. The goal is for the popup to appear when the user is typing, allowing them to continue typing into the text editor while the popup is visible, and then hide the popup when needed (e.g., pressing the Escape key or moving the cursor away).

Here are the main issues I'm facing:

Focus Issue: I'm using self.completer_list.setFocusPolicy(Qt.NoFocus) to prevent the popup from taking focus, but when using Qt.Popup, the focus still shifts to the QListWidget, and the rest of the program becomes unresponsive until I interact with the popup.

Popup Hiding Issue: I'm using self.completer_list.hide() to hide the popup, but it doesn't always hide properly or doesn't respond well when clicking outside or when the cursor moves.

What might I be doing wrong in my code?

This is part of my code:

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 TO POPUP"""
    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)

Upvotes: 0

Views: 38

Answers (1)

Fatih Ünal
Fatih Ünal

Reputation: 37

I did it with the using "Jedi" like tihs:

 # QCompleter oluştur
    self.completer = QCompleter(self)
    self.completer.setCompletionMode(QCompleter.PopupCompletion)
    self.completer.setWidget(self)

    # Dinamik olarak güncellenecek model
    self.model = QStringListModel()
    self.completer.setModel(self.model)

    # İmleç pozisyonuna göre tamamlama popup'ını göster
    self.textChanged.connect(self.show_completer)

    # Completer'den bir öneri seçildiğinde, insert_completion fonksiyonunu çağır.
    self.completer.activated.connect(self.insert_completion)

def show_completer(self):
    """Tamamlama popup'ını göster"""
    cursor = self.textCursor()
    cursor.select(cursor.WordUnderCursor)
    current_code = self.toPlainText()  # Şu anki metni al
    line, col = cursor.blockNumber() + 1, cursor.columnNumber()  # Jedi'ye göndermek için pozisyonu al

    # Jedi'yi kullanarak tamamlama önerileri al
    script = jedi.Script(current_code)
    completions = script.complete(line, col)  # Satır ve sütun numarasını burada kullanıyoruz

    # Eğer tamamlama önerileri varsa, popup'ı göster
    if completions:
        completion_list = [comp.name for comp in completions]  # Önerileri listeye dönüştür
        self.model.setStringList(completion_list)

        rect = self.cursorRect()
        rect.setX(rect.x() + 35)  # Popup'ı 2 birim sağa kaydırıyoruz
        rect.setWidth(self.completer.popup().sizeHintForColumn(0) + 100)  # Popup genişliği

        # Popup için opaklık ayarı yapıyoruz
        opacity_effect = QGraphicsOpacityEffect(self.completer.popup())
        opacity_effect.setOpacity(0.9)  # %90 opaklık
        self.completer.popup().setGraphicsEffect(opacity_effect)

        self.completer.complete(rect)  # Popup'ı göster
    else:
        self.completer.popup().hide()  # Eğer öneri yoksa popup'ı gizle

def keyPressEvent(self, event):
    cursor = self.textCursor()

    if self.completer.popup().isVisible():
        if event.key() in (Qt.Key_Enter, Qt.Key_Return):
            selected_item = self.completer.popup().currentIndex().data()
            if selected_item:
                self.insert_completion(selected_item)
            self.completer.popup().hide()
            return  # Varsayılan işleyiciyi engelle
        elif event.key() == Qt.Key_Space or event.key() == Qt.Key_Escape:
            self.completer.popup().hide()
            return

Upvotes: -1

Related Questions