PyQt5 QComboBox элементы списка, меняющие положение - PullRequest
0 голосов
/ 04 марта 2020

У меня возникла проблема со стилем отображения элементов Qcombobox. В настоящее время я жестко кодирую данные, которые будут показаны в выпадающем списке.

вот код:

self.Dummy = QComboBox(self)
self.Dummy.setGeometry(200,600, 350, 50)

self.Dummy.setStyleSheet("QComboBox {background-color: white;border-style: outset;" border-width: 2px;border-radius: 5px;border-color: #448aff; font:  12px; min-width: 10em; padding: 3px;}")

self.Dummy.addItems(["-Select-", "2", "3","4","5","6","7","8","9","0","11",])

Проблема в том, что позиция выпадающего списка остается меняется после каждого выбора. Вот изображение проблемы, с которой я сталкиваюсь.

Ниже находится мой комбинированный список

enter image description here

Список содержит элементы <- Выбрать ->, 2,3,4, 5,6,7,8,9,0,11 , где <-Select-> будет первым показанным элементом.

Теперь, когда я нажимаю на поле, список ящиков «вниз» элементов и предположим, что я выбрал «2». Затем, если я попытаюсь выбрать другой элемент, список будет сброшен в направлении «вниз». см. ниже

dropped

Теперь, скажем, если выбран последний элемент из элементов, '11'. Теперь, если я попытаюсь выбрать новый элемент, нажав на поле, список будет отображаться "вверх", а не вниз. см. ниже

dropped Up

Что нужно сделать, чтобы это исправить? Я не думаю, что это проблема с таблицей стилей, без этого также эта проблема происходит. Причина, по которой мне нужно это исправить, заключается в том, что, когда список всплывает, он закрывает метку над ним

1 Ответ

0 голосов
/ 04 марта 2020

То, что вы видите, - это поведение, зависящее от ОС и стиля.

Чтобы избежать этого, лучше всего создать подкласс QComboBox и переписать showPopup(), затем мы вызываем базу реализация класса (которая отвечает за отображение, изменение размера и позиционирование всплывающего представления) и перемещение его при необходимости.

class Combo(QtWidgets.QComboBox):
    def showPopup(self):
        super().showPopup()
        # find the widget that contains the list; note that this is *not* the view
        # that QComboBox.view() returns, but what is used to show it.
        popup = self.findChild(QtWidgets.QFrame)
        rect = popup.geometry()
        if not rect.contains(self.mapToGlobal(self.rect().center())):
            # the popup is not over the combo, there's no need to move it
            return
        # move the popup at the bottom left of the combo
        rect.moveTopLeft(self.mapToGlobal(self.rect().bottomLeft()))
        # ensure that the popup is always inside the edges of the screen
        # we use the center of the popup as a reference, since with multiple
        # screens the combo might be between two screens, but that position
        # could also be completely outside the screen, so the cursor position
        # is used as a fallback to decide on what screen we'll show it
        done = False
        for i, pos in enumerate((rect.center(), QtGui.QCursor.pos())):
            for screen in QtWidgets.QApplication.screens():
                if pos in screen.geometry():
                    screen = screen.geometry()
                    if rect.x() < screen.x():
                        rect.moveLeft(screen.x())
                    elif rect.right() > screen.right():
                        rect.moveRight(screen.right())
                    if rect.y() < screen.y():
                        rect.moveTop(screen.y())
                    elif rect.bottom() > screen.bottom():
                        # if the popup goes below the screen, move its bottom
                        # *over* the combo, so that the its current selected
                        # item will always be visible
                        rect.moveBottom(self.mapToGlobal(QtCore.QPoint()).y())
                    done = True
                    break
            if done:
                break
        popup.move(rect.topLeft())

Это также можно сделать без создания подклассов (например, если у вас много комбинаций, вы создали Пользовательский интерфейс от дизайнера, и вы не хотите использовать продвигаемые виджеты), но вы должны помнить, чтобы изменить все ссылки на комбо.

class MyWindow(QtWidgets.QWidget):
    def __init__(self):
        # ...
        self.combo = QtWidgets.QComboBox()
        self.combo.showPopup = self.showPopupAndCheck

    def showPopupAndCheck(self):
        QtWidgets.QComboBox.showPopup(self.combo)
        popup = self.combo.findChild(QtWidgets.QFrame)
        rect = popup.geometry()
        if not rect.contains(self.combo.mapToGlobal(self.combo.rect().center())):
            # the popup is not over the combo, there's no need to move it
            return
        # change everything from self to self.combo

В качестве альтернативы, если вы хотите, чтобы это поведение было последовательным во всех ваша программа, не всегда используя подкласс, вы можете использовать какой-то хакерский патч.
Преимущество заключается в том, что любой QComboBox, который вы создаете (даже при загрузке файлов пользовательского интерфейса или создании комбо во время выполнения), всегда будет используйте новое поведение.

Важно : это ДОЛЖНО находиться в самом начале main fi файла вашей программы, возможно сразу после раздела импорта.

from PyQt5 import QtCore, QtGui, QtWidgets

def customShowPopup(self):
    # we can't use super(), because we're not in the class definition, but
    # calling the class method with "self" as first argument is practically the 
    # same thing; note the underscore!
    QtWidgets.QComboBox._showPopup(self)
    popup = self.findChild(QtWidgets.QFrame)
    # ... go on, exactly as above

# create a new reference to the showPopup method, which is the one we've used
# in the function above
QtWidgets.QComboBox._showPopup = QtWidgets.QComboBox.showPopup
# overwrite the original reference with the new function
QtWidgets.QComboBox.showPopup = customShowPopup
...