Qt.ScrollBarAsNeeded не показывает полосу прокрутки, когда это действительно необходимо

Я реализую приложение Python с использованием PyQt5, и я столкнулся с некоторыми проблемами при использовании QScrollArea. Это макет моего приложения:

введите описание изображения здесь

Он состоит из 2 QScrollArea (левая и правая панели) и QMdiArea (центральный виджет) организован в QHBoxLayout, Когда я раскрываю виджеты на левой панели, нажимая на элементы управления и высоту QWidget из QScrollArea больше, чем высота QScrollArea Сама полоса прокрутки появляется (как и ожидалось), но она перекрывает содержимое QScrollArea, Чтобы исправить эту проблему, я переопределил resizeEvent добавив необходимое место для полосы прокрутки (до этого момента все работает.

введите описание изображения здесь

Теперь, когда я вручную изменяю размер главного окна, левая панель получает больше места, и полоса прокрутки должна исчезнуть (но не исчезает), и она перекрывает виджеты панели:

введите описание изображения здесь

Я также попытался вручную переключить видимость полосы прокрутки (когда получено resizeEvent): когда я делаю это, я могу успешно скрыть полосу прокрутки, но затем я не могу показать ее снова (независимо от того, вызову ли я setVisible(True) на полосе прокрутки). Это приводит к тому, что пространство для полосы прокрутки добавляется, но полоса прокрутки отсутствует, а содержимое панели не прокручивается:

введите описание изображения здесь

Вот реализация виджета панели:

class Pane(QScrollArea):

    MinWidth = 186

    def __init__(self, alignment=0, parent=None):
        super().__init__(parent)
        self.mainWidget = QWidget(self)
        self.mainLayout = QVBoxLayout(self.mainWidget)
        self.mainLayout.setAlignment(alignment)
        self.mainLayout.setContentsMargins(0, 0, 0, 0)
        self.mainLayout.setSpacing(0)
        self.setContentsMargins(0, 0, 0, 0)
        self.setFrameStyle(QFrame.NoFrame)
        self.setFixedWidth(Pane.MinWidth)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Ignored)
        self.setWidgetResizable(True)
        self.setWidget(self.mainWidget)

    def resizeEvent(self, resizeEvent):
        if self.viewport().height() < self.widget().height():
            self.setFixedWidth(Pane.MinWidth + 18)
            # THIS DOESN'T WORK
            #self.verticalScrollBar().show()
        else:
            self.setFixedWidth(Pane.MinWidth)
            #self.verticalScrollBar().hide()

    def addWidget(self, widget):
        self.mainLayout.addWidget(widget)

    def removeWidget(self, widget):
        self.mainLayout.removeWidget(widget)

    def update(self, *__args):
        for item in itemsInLayout(self.mainLayout):
            item.widget().update()
        super().update(*__args)

То, чего я хочу достичь, довольно просто (но практически кажется, что это не так просто): я хотел бы динамически показывать вертикальную полосу прокрутки на виджетах левой / правой панели только тогда, когда это необходимо, и добавить необходимое пространство для полосы прокрутки, чтобы она не не перекрывают виджеты в QScrollArea,

Прежде чем кто-то спросит, я уже пытался сделать что-то вроде этого:

def resizeEvent(self, resizeEvent):
    if self.viewport().height() < self.widget().height():
        self.setFixedWidth(Pane.MinWidth + 18) 
        scrollbar = self.verticalScrollbar()
        scrollbar.setVisible(True)
        self.setVerticalScrollBar(scrollbar) ## APP CRASH
    else:
        self.setFixedWidth(Pane.MinWidth)
        #self.verticalScrollBar().hide()

что приводит к сбою моего приложения. Я надеюсь, что кто-то уже сталкивался с этой проблемой и сможет мне помочь.

РЕДАКТИРОВАТЬ: я использую PyQt5.5 скомпилированный против Qt5.5 под OSX Yosemite 10.10.4 с использованием Clang.

1 ответ

Решение

Кажется, все работает, как и ожидалось, без необходимости обходных путей. Однако я сильно подозреваю, что в вашем реальном коде есть дополнительные ограничения, которые вы не указали в своем вопросе.

ОБНОВИТЬ

Ниже приведен простой пример изменения размера прокрутки, когда полосы прокрутки показаны / скрыты:

import sys
from PyQt5 import QtCore, QtGui, QtWidgets

class Window(QtWidgets.QMainWindow):
    def __init__(self):
        super(Window, self).__init__()
        widget = QtWidgets.QWidget(self)
        layout = QtWidgets.QHBoxLayout(widget)
        self.mdi = QtWidgets.QMdiArea(self)
        self.leftScroll = Pane(
            QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft, self)
        self.rightScroll = Pane(
            QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft, self)
        layout.addWidget(self.leftScroll)
        layout.addWidget(self.mdi)
        layout.addWidget(self.rightScroll)
        self.setCentralWidget(widget)
        for scroll in self.leftScroll, self.rightScroll:
            for index in range(4):
                widget = QtWidgets.QTextEdit()
                widget.setText('one two three four five')
                scroll.addWidget(widget)

class Pane(QtWidgets.QScrollArea):
    MinWidth = 186

    def __init__(self, alignment=0, parent=None):
        super().__init__(parent)
        self.mainWidget = QtWidgets.QWidget(self)
        self.mainLayout = QtWidgets.QVBoxLayout(self.mainWidget)
        self.mainLayout.setAlignment(alignment)
        self.mainLayout.setContentsMargins(0, 0, 0, 0)
        self.mainLayout.setSpacing(0)
        self.setContentsMargins(0, 0, 0, 0)
        self.setFrameStyle(QtWidgets.QFrame.NoFrame)
        self.setFixedWidth(Pane.MinWidth)
        self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
        self.setSizePolicy(QtWidgets.QSizePolicy.Maximum,
                           QtWidgets.QSizePolicy.Ignored)
        self.setWidgetResizable(True)
        self.setWidget(self.mainWidget)
        self.verticalScrollBar().installEventFilter(self)

    def addWidget(self, widget):
        self.mainLayout.addWidget(widget)

    def removeWidget(self, widget):
        self.mainLayout.removeWidget(widget)

    def eventFilter(self, source, event):
        if isinstance(source, QtWidgets.QScrollBar):
            if event.type() == QtCore.QEvent.Show:
                self.setFixedWidth(Pane.MinWidth + source.width())
            elif event.type() == QtCore.QEvent.Hide:
                self.setFixedWidth(Pane.MinWidth)
        return super(Pane, self).eventFilter(source, event)

if __name__ == '__main__':

    app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.setGeometry(500, 300, 800, 300)
    window.show()
    sys.exit(app.exec_())
Другие вопросы по тегам