当有空间时,将 QGraphicsView 滚动条放在旁边而不是放在 QGraphicsView 内

问题描述 投票:0回答:1

我用 PyQt6 编写的 Qt 应用程序包含一个

QGraphicsView
。根据应用程序窗口的大小,该视图下面的
QGraphicsScene
可能太大而无法适应视口,因此
QGraphicsView
会根据需要自动添加水平和垂直滚动条。

我发现当我将窗口大小调整到足够宽以至于不再需要水平滚动条时,

QGraphicsView
不会将垂直滚动条移动到多余的空间中,因此我继续有一个水平滚动条存在,其滚动量仅等于垂直滚动条的宽度。

当水平方向有足够的空间时,我希望

QGraphicsView
的垂直滚动条出现在视图的右侧,而不是在视图内部,这样就不需要水平滚动条了。

(我认为这就是

QScrollArea.setWidgetResizable(True)
应该做的,但不幸的是,
QGraphicsView
QAbstractScrollArea
而不是
QScrollArea
的子类,因此
widgetResizable
属性不可用。)

如何让水平滚动条在不需要的情况下停止出现?

MWE:

from PyQt6 import QtCore, QtGui, QtWidgets

class MyBottomWidget(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()

        scene = QtWidgets.QGraphicsScene()
        rect = QtCore.QRect(0, 0, 700, 700)
        shape = scene.addEllipse(rect)
        shape.setBrush(QtGui.QColor(QtGui.QColorConstants.Blue))
        view = QtWidgets.QGraphicsView(scene)
        view.setSceneRect(rect)
        view.setFrameShape(QtWidgets.QFrame.Shape.NoFrame)

        layout = QtWidgets.QGridLayout(self)
        self.setLayout(layout)
        layout.addWidget(view, 0, 0, QtCore.Qt.AlignmentFlag.AlignLeft | QtCore.Qt.AlignmentFlag.AlignTop)

app = QtWidgets.QApplication([])

window = QtWidgets.QMainWindow()
window.resize(800, 600)
window.setCentralWidget(MyBottomWidget())
window.show()

app.exec()

qt qgraphicsview pyqt6 qscrollarea
1个回答
0
投票

我想出的解决方案是子类化

QGraphicsView
并重写
sizeHint
方法,以始终将滚动条的范围包含在宽度和高度中。

class MyGraphicsView(QtWidgets.QGraphicsView):
    def __init__(self, scene):
        super().__init__(scene)
        self.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft | QtCore.Qt.AlignmentFlag.AlignTop)
    
    def sizeHint(self):
        w, h, s = self.full_width(), self.full_height(), self.scrollbar_extent()
        return QtCore.QSize(w + s, h + s)
        
    def full_width(self):
        return self.mapFromScene(self.sceneRect()).boundingRect().width()

    def full_height(self):
        return self.mapFromScene(self.sceneRect()).boundingRect().height()
    
    @staticmethod
    def scrollbar_extent():    # Returns 19 on my system. See https://stackoverflow.com/q/16515646/1187304
        extent = QtWidgets.QApplication.instance().style().pixelMetric(QtWidgets.QStyle.PixelMetric.PM_ScrollBarExtent)
        return extent + 3

在我的代码中使用

MyGraphicsView
而不是
QtWidgets.QGraphicsView
,我现在得到以下行为:

  1. 当窗口大小足够大时,我会看到一个 719px × 719px 的画布,我的图像绘制在左上角 700px × 700px 中。
  2. 如果窗口宽度和/或高度缩小,以致有空间仅显示前 700px ≤ x < 719px, the excess is not shown but no scrollbar appears.
  3. 如果窗口宽度或高度缩小,只有小于 700px 的显示空间,则会出现滚动条。
  4. 如果窗口宽度和窗口高度都很小,以致两个滚动条都出现,则每个滚动条都有额外的 19px 范围来容纳另一个滚动条。

由于第 1 点,这不是一个完美的解决方案 - 理想情况下,如果有足够的空间,不需要滚动条,那么画布就不会包含额外的 19px - 但完全满足我的需求。如果需要,可以将默认背景颜色的矩形添加到场景的右边缘和下边缘,以掩盖包含这些额外像素的事实:

        w, h, s = self.full_width(), self.full_height(), self.scrollbar_extent()
        for rect in (QtCore.QRectF(w + 1, 0, s, h + s), QtCore.QRectF(0, h + 1, w + s, s)):
            shape = scene.addRect(rect)
            shape.setBrush(self.palette().window())
            shape.setPen(QtGui.QPen(QtCore.Qt.PenStyle.NoPen))

我决定暂时不这样做,但这是一个可以玩的东西。

© www.soinside.com 2019 - 2024. All rights reserved.