Переопределение позиции всплывающего окна QCompleter

Были похожие вопросы о переопределении позиции всплывающего окна QCompleter, но я все еще не нашел рабочего решения. Я просто хочу переместить всплывающее окно примерно на 5 пикселей (у меня есть определенные требования к стилю).

Я пробовал создать подкласс QListView и использовать его как всплывающее окно с помощью setPopup(). Затем я переопределяю showEvent и перемещаю всплывающее окно вниз по оси Y. Я также делаю это для resizeEvent, так как считаю, что это срабатывает, когда элементы фильтруются и размер всплывающего окна изменяется. Однако это не работает. Затем я использовал однократный таймер, чтобы запустить движение через 1 мс. Это работает, но кажется довольно непоследовательным - в первый раз он отображается иначе, чем в последующие разы или изменение размера.

Ниже моя последняя попытка (попытка взломать ее, подсчитав количество всплывающих окон..), надеюсь, кто-то может показать мне, что я делаю неправильно, или лучшее решение

import sys
import os
from PySide2 import QtCore, QtWidgets, QtGui

class QPopup(QtWidgets.QListView):
    def __init__(self, parent=None):
        super(QPopup, self).__init__(parent)

        self.popups = 0

    def offset(self):
        y = 3 if self.popups < 2 else 7
        print('y: {}'.format(y))
        self.move(self.pos().x(), self.pos().y() + y)

        self.popups += 1

    def showEvent(self, event):
        print('show')
        # self.offset()
        QtCore.QTimer.singleShot(1, self.offset)

    def resizeEvent(self, event):
        print('resize')
        # self.offset()
        QtCore.QTimer.singleShot(1, self.offset)

class MyDialog(QtWidgets.QDialog):
    def __init__(self, parent=None):
        super(MyDialog, self).__init__(parent)

        self.create_widgets()
        self.create_layout()
        self.create_connections()

    def create_widgets(self):
        self.le = QtWidgets.QLineEdit('')

        self.completer = QtWidgets.QCompleter(self)
        self.completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
        self.completer.setCompletionMode(QtWidgets.QCompleter.PopupCompletion)
        self.completer.setMaxVisibleItems(10)
        self.completer.setFilterMode(QtCore.Qt.MatchContains)
        self.completer.setPopup(QPopup())

        popup = QPopup(self)
        self.completer.setPopup(popup)

        self.model = QtCore.QStringListModel()
        self.completer.setModel(self.model)

        self.le.setCompleter(self.completer)

        self.completer.model().setStringList(['one','two','three'])

    def create_layout(self):
        main_layout = QtWidgets.QVBoxLayout(self)

        main_layout.addWidget(self.le)

    def create_connections(self):
        pass

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)

    my_dialog = MyDialog()

    my_dialog.show()  # Show the UI
    sys.exit(app.exec_())

person Michael B    schedule 12.10.2020    source источник


Ответы (1)


Одним из решений может быть создание подкласса QLineEdit и переопределение keyPressEvent для отображения всплывающего окна со смещением:

PySide2.QtWidgets.QCompleter.complete([rect=QRect()])

Для режимов PopupCompletion и QCompletion::UnfilteredPopupCompletion вызов этой функции отображает всплывающее окно с текущими завершениями. По умолчанию, если rect не указан, всплывающее окно отображается в нижней части widget() . Если указан прямоугольник, всплывающее окно отображается на левом краю прямоугольника.

см. doc.qt.io -› QCompleter.complete.

Полный автономный пример

Прямоугольник вычисляется на основе Y-положения прямоугольника курсора. Высота всплывающего окна не изменяется. Ширина подгоняется под ширину виджета ZLineEdit.

rect = QtCore.QRect(0,
                    self.cursorRect().y() + 4,
                    self.width(),
                    self.completer().widget().height())

Ваш код, слегка измененный с использованием упомянутых выше моментов, может выглядеть так:

import sys
from PySide2 import QtCore, QtWidgets
from PySide2.QtWidgets import QLineEdit, QDialog, QCompleter


class ZLineEdit(QLineEdit):
    def __init__(self, string, parent=None):
        super().__init__(string, parent)

    def keyPressEvent(self, event):
        super().keyPressEvent(event)
        if len(self.text()) > 0:
            rect = QtCore.QRect(0,
                                self.cursorRect().y() + 4,
                                self.width(),
                                self.completer().widget().height())
            self.completer().complete(rect)


class MyDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.le = ZLineEdit('')
        autoList = ['one', 'two', 'three']
        self.completer = QCompleter(autoList, self)

        self.setup_widgets()
        self.create_layout()
        self.create_connections()

    def setup_widgets(self):
        self.completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
        self.completer.setCompletionMode(QtWidgets.QCompleter.PopupCompletion)
        self.completer.setMaxVisibleItems(10)
        self.completer.setFilterMode(QtCore.Qt.MatchContains)
        self.le.setCompleter(self.completer)

    def create_layout(self):
        main_layout = QtWidgets.QVBoxLayout(self)
        main_layout.addWidget(self.le)

    def create_connections(self):
        pass


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    my_dialog = MyDialog()
    my_dialog.show()
    sys.exit(app.exec_())

Тест

С левой стороны вы видите поведение по умолчанию. С правой стороны всплывающее окно сдвинуто вниз на 4 пикселя:

смещение

person Stephan Schlecht    schedule 17.10.2020