我用 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()
我想出的解决方案是子类化
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 点,这不是一个完美的解决方案 - 理想情况下,如果有足够的空间,不需要滚动条,那么画布就不会包含额外的 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))
我决定暂时不这样做,但这是一个可以玩的东西。