【问题标题】:Fixed size QGraphicsView and QGraphicsScene固定大小的 QGraphicsView 和 QGraphicsScene
【发布时间】:2020-04-27 07:05:09
【问题描述】:

我有一个 QMdiSubWindow,它包含一个 QTabWidget,它在一个带有 QHBoxLayout 的小部件内包含 QGraphicsView。子窗口位于我的主窗口的 mdi 区域中。我的目标是为 QGraphicScene 设置一个固定大小,具体取决于我使用的纸张大小。 然后,视图应该与场景的大小一致,如果该区域可以适合总可用 mdi 区域,则不显示滚动条,也调整子窗口的大小,或者应该调整大小以填充可用区域,也调整子窗口的大小,并且应该显示滚动条以导航到剩余空间。

我目前的方法是首先调整 mdi 子窗口的大小,然后调用调整窗口 QTabWidget 中当前活动小部件的大小。 这一切都是按照musicamante的实现来完成的。 当前小部件选择父(QStackedWidget)的父(QTabWidget)的父(QMdiSubWindow)的矩形(首先调整大小)和QGraphicsScene的尺寸之间的最小值,该尺寸在我设置尺寸时更新。

-> 根据用户输入设置 QGraphicsScene 尺寸

-> 调整子窗口大小

-> 调整 QGraphicsView 的大小

我在我的程序上使用相同的逻辑,它根本无法显示滚动条,并且视图扩展以适应 QGraphicsScene 尽管被告知要改为子窗口的大小(子窗口的大小正确),以及下面的示例,它们确实根据需要显示,但被部分剪掉。

from PyQt5 import QtWidgets, QtCore, QtGui
paperSizes = {
    "A0": {
        "72": [2384, 3370],
        "96": [3179, 4494],
        "150": [4967, 7022],
        "300": [9933, 14043]
    },
    "A1": {
        "72": [1684, 2384],
        "96": [2245, 3179],
        "150": [3508, 4967],
        "300": [7016, 9933]
    },
    "A2": {
        "72": [1191, 1684],
        "96": [1587, 2245],
        "150": [2480, 3508],
        "300": [4960, 7016]
    },
    "A3": {
        "72": [842, 1191],
        "96": [1123, 1587],
        "150": [1754, 2480],
        "300": [3508, 4960]
    },
    "A4": {
        "72": [595, 842],
        "96": [794, 1123],
        "150": [1240, 1754],
        "300": [2480, 3508]
    }
}
class canvas(QtWidgets.QWidget):

    def __init__(self, parent=None, size= 'A4', ppi= '72'):
        super(canvas, self).__init__(parent)
        self._ppi = ppi
        self._canvasSize = size 
        self.painter = QtWidgets.QGraphicsScene()        
        self.painter.setBackgroundBrush(QtGui.QBrush(QtCore.Qt.white))

        self.view = QtWidgets.QGraphicsView(self.painter)
        # self.view.setMinimumSize(595, 842)

        self.layout = QtWidgets.QHBoxLayout(self)
        self.layout.addWidget(self.view, stretch = 1, alignment= QtCore.Qt.AlignCenter)
        self.setLayout(self.layout)

        self.painter.setSceneRect(0, 0, *paperSizes[self.canvasSize][self.ppi])

    def resizeView(self, w, h):
        self.painter.setSceneRect(0, 0, w, h)
        self.adjustView()

    def adjustView(self):
        self.view.setSceneRect(0, 0, self.painter.sceneRect().width() - self.view.frameWidth() * 2, 
            self.painter.sceneRect().height())
        # give the view some time to adjust itself
        QtWidgets.QApplication.processEvents()
        width = self.painter.sceneRect().width() + self.view.frameWidth()*2
        if self.view.verticalScrollBar().isVisible():
            width += self.style().pixelMetric(QtWidgets.QStyle.PM_ScrollBarExtent)
        height = self.painter.sceneRect().height() + self.view.frameWidth()*2
        if self.view.verticalScrollBar().isVisible():
            height += self.style().pixelMetric(QtWidgets.QStyle.PM_ScrollBarExtent)

        self.view.setFixedWidth(min(self.parent().rect().width() - self.view.frameWidth()*2, width))
        self.view.setFixedHeight(min(self.parent().rect().height()- self.view.frameWidth()*2, height))
    def resizeEvent(self, event):
        self.adjustView()

    def setCanvasSize(self, size):
        self.canvasSize = size

    def setCanvasPPI(self, ppi):
        self.ppi = ppi

    @property
    def canvasSize(self):
        return self._canvasSize
    @property
    def ppi(self):
        return self._ppi

    @canvasSize.setter
    def canvasSize(self, size):
        self._canvasSize = size
        if self.painter:
            self.resizeView(*paperSizes[self.canvasSize][self.ppi])

    @ppi.setter
    def ppi(self, ppi):
        self._ppi = ppi
        if self.painter:
            self.resizeView(*paperSizes[self.canvasSize][self.ppi])

    @property
    def dimensions(self):
        #returns the dimension of the current scene
        return self.painter.sceneRect().width(), self.painter.sceneRect().height()

class AppDemo(QtWidgets.QMainWindow):
    def __init__(self):
        super(AppDemo, self).__init__()
        self.centralwidget = canvas(self)
        width, height = paperSizes['A4']['72']
        wsetter = min(1800, width - self.centralwidget.view.frameWidth()*2)
        hsetter = min(900, height - self.centralwidget.view.frameWidth()*2)
        self.setFixedSize(wsetter, hsetter)
        self.centralwidget.resizeView(width, height)
        self.setCentralWidget(self.centralwidget)
        self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self.resizer)
        self.show()

    def resizer(self, point):
        width, height = paperSizes['A0']['300']
        wsetter = min(1800, width - self.centralwidget.view.frameWidth()*2)
        hsetter = min(900, height - self.centralwidget.view.frameWidth()*2)
        self.setFixedSize(wsetter, hsetter)
        self.centralWidget().resizeView(width, height)
def main():
    import sys
    app = QtWidgets.QApplication(sys.argv)

    w = AppDemo()
    w.setWindowTitle('AppDemo')
    w.centralwidget.resizeView(*paperSizes['A0']['72'])
    w.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

【问题讨论】:

  • 拉伸总是根据可用大小调整大小,这是使用其他同级小部件大小策略计算的。现在,根据您的代码,您只是更改视图的场景矩形,您不能相应地为图形视图设置固定宽度吗?
  • 所以在设置场景矩形的同时设置一个固定宽度?
  • 是的,完全正确。设置视图的场景矩形只会改变视口上可见的场景区域,但不会影响视图的大小。请注意,如果您需要根据场景设置精确的宽度,则必须考虑视口的边距(包括 QFrame lineWidth 以及由于场景内容和翻译管理可能至少额外的像素),以便显示滚动条仅在真正需要时。
  • 这种部分解决了我的问题,但由于某种原因,当我将视图的高度设置为父级的高度时,当有宽度可以扩展时,仍然会出现一些实心滚动条滚动条没有空间移动。此外,当小部件初始化时,宽度很奇怪,我必须在小部件添加到窗口后再次调用我的调整大小函数来修复它....
  • 我建议你编辑你的代码并提供一个完整的minimal, reproducible example,这样我们就可以试着了解那里到底发生了什么。

标签: python-3.x pyqt5


【解决方案1】:

如果您要始终保持默认转换(无缩放),则使用实际场景矩形作为参考来设置视图 sceneRect 可能是有意义的。

请注意,由于窗口可以调整大小,通常最好使用通用函数来执行此操作,并在resizeEvent() 中调用它。这也确保了在垂直滚动条可见时调整视图的大小。

class canvas(QtWidgets.QWidget):
    def __init__(self, parent=None, size= 'A4', ppi= '72'):
        # ...
        w, h = paperSizes[self.canvasSize][self.ppi]
        self.painter.setSceneRect(0, 0, w, h)
        # ensure that the scene is always aligned on the left, instead of being
        # centered (the default)
        self.view.setAlignment(QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop)
        # no need for this here
        # self.view.setSceneRect(0, 0, w, h) 

    def resizeView(self, w, h):
        self.painter.setSceneRect(0, 0, w, h)
        self.adjustView()

    def adjustView(self):
        self.view.setSceneRect(0, 0, self.painter.sceneRect().width() - self.view.frameWidth() * 2, 
            self.painter.sceneRect().height())
        # give the view some time to adjust itself
        QtWidgets.QApplication.processEvents()
        width = self.painter.sceneRect().width() + self.view.frameWidth() * 2
        if self.view.verticalScrollBar().isVisible():
            width += self.style().pixelMetric(QtWidgets.QStyle.PM_ScrollBarExtent)
        self.view.setFixedWidth(width)

    def resizeEvent(self, event):
        self.adjustView()

显然,如果您确定视图将始终显示场景的全宽,您还可以通过将其 horizontalScrollBarPolicy 设置为 Qt.ScrollBarAlwaysOff 来完全禁用水平滚动条。

【讨论】:

  • 但是,如果我想缩放或者可能实际实现类似缩放的功能,该怎么做呢?
  • 另外,当宽度大于视口大小时,您没有处理这种情况......对我来说,考虑到 min(self.parentWidget().width(), self.painter.sceneRect ().width()) + #frame width ,对我没有帮助,因为窗口会扩展以适应视图本身的宽度......这很奇怪。并且没有出现水平滚动条。
猜你喜欢
  • 1970-01-01
  • 2013-10-26
  • 2012-10-03
  • 1970-01-01
  • 2017-02-16
  • 2013-11-19
  • 2014-02-18
  • 1970-01-01
  • 2011-06-26
相关资源
最近更新 更多