Qt.ScrollBarAsNeeded在需要时未显示滚动条

9

我正在使用PyQt5实现一个应用程序,在使用QScrollArea时遇到了一些问题。这是我的应用程序布局:

enter image description here

它由两个QScrollArea(左侧和右侧窗格)和一个QMdiArea(中心小部件)组成,排列成QHBoxLayout。当我通过单击控件展开左侧窗格上的小部件,并且QScrollAreaQWidget的高度大于QScrollArea本身的高度时,滚动条会出现(如预期),但它重叠在QScrollArea的内容上。为了解决这个问题,我重新实现了resizeEvent,增加了必要的滚动条空间(到此为止一切正常)。

enter image description here

现在,当我手动调整主窗口大小时,左侧面板会获得更多的空间,滚动条应该消失(但它没有),并且会重叠面板的小部件:

enter image description here

我还尝试手动切换滚动条的可见性(当接收到resizeEvent时):这样做时,我可以成功地隐藏滚动条,但是我无法再次显示它(无论我是否在滚动条上调用setVisible(True))。这导致添加了滚动条的空间,但滚动条缺失且面板内容无法滚动:

enter image description here

这里是面板小部件的实现:

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()

这导致我的应用程序崩溃。

我希望已经有人遇到过这个问题并能够帮助我。

编辑:我正在使用针对Qt5.5编译的PyQt5.5,在OSX Yosemite 10.10.4下使用clang。


首先尝试将 setWidgetResizable 设置为 true,以适应您的滚动区域。 - Pie_Jesu
它已经设置为True了,所以我猜问题出在其他地方。 - Daniele Pantaleone
1个回答

3

在我的情况下,一切似乎都按预期工作,没有任何需要解决的问题。然而,我强烈怀疑你在真实代码中有额外的限制,并且这些限制在你的问题中并未透露出来。

更新

以下是一个简单的示例,在滚动条显示/隐藏时调整滚动区域的大小:

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_())

我尝试了您的代码,但问题仍存在:http://i.imgur.com/O2o0iil.png。您在哪个平台上尝试了代码?也许这是只有OSX才会出现的问题。 - Daniele Pantaleone
@DanielePantaleone。是的,我怀疑还有其他因素参与其中。我使用Linux,无法在OSX上进行测试。您应该编辑您的问题,明确指出您正在使用哪个特定版本的OSX和Qt。如果您在OSX上添加了最简单的可能重现问题的示例,例如仅包含一个小部件的单个QScrollArea,那将非常有帮助。 - ekhumoro
我修改了你的代码,在每个Pane中添加了1个小部件(比滚动区域大),但问题仍然存在。接下来几天我会在Linux下尝试一下。 - Daniele Pantaleone
哪种是正确的行为。然而,在我的系统上并没有发生这种情况(这相当奇怪)。我使用Python 3.4和使用mingw32编译的PyQt5测试了您的代码。 - Daniele Pantaleone
@DanielePantaleone。这真是让人完全迷惑-根本没有意义。肯定还有其他因素在起作用。你能在另一台机器上进行测试吗?除非有其他人能够重现这个问题,否则很难看到任何进展的可能性。 - ekhumoro
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接