首页 > 解决方案 > PyQt:如何拉伸 QGraphicsView 和 QGraphicsScene 小部件的宽度和高度?

问题描述

我正在尝试创建一个延伸到窗口宽度和高度的网格小部件。网格的大小(当前为 14x49)可能会发生变化,但我希望小部件的整体大小保持不变。

代码:

import sys

from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QLayout, 
QGridLayout, QSizePolicy, QGraphicsScene, QGraphicsView
from PyQt5.QtCore import QCoreApplication, QSize, Qt, QRectF, QPointF, QSizeF
from PyQt5.QtGui import QColor, QPen, QBrush, qRgb

class Map(QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.initUI()

    def initUI(self):
        self.setMinimumSize(500, 500) # min-size of the widget
        self.columns = 14 # num of columns in grid
        self.rows = 49 # num of rows in grid

    def resizeEvent(self, event):
        # compute the square size based on the aspect ratio, assuming that the
        reference = self.width() * self.rows / self.columns
        if reference > self.height():
            # the window is larger than the aspect ratio
            # use the height as a reference (minus 1 pixel)
            self.squareSize = (self.height() - 1) / self.rows
        else:
            # the opposite
            self.squareSize = (self.width() - 1) / self.columns

    def paintEvent(self, event):
        grid = QGridLayout()
        self.sceneWithPen(grid)
        self.setLayout(grid)

    # creates the grid of squares
    def sceneWithPen(self, grid):
        scene = QGraphicsScene()
        w = QGraphicsView()
        w.setScene(scene)
        side = self.squareSize
        brush = QBrush(QColor(qRgb(255, 255, 255))) # background color of square
        pen = QPen(Qt.black) # border color of square
        for i in range(self.rows):
            for j in range(self.columns):
                r = QRectF(QPointF(
                    i*side, j*side), QSizeF(side, side)) # each square 
                scene.addRect(r, pen, brush)
        grid.addWidget(w)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    view = Map()
    view.show()
    sys.exit(app.exec_())

我希望小部件看起来像什么:

在此处输入图像描述

标签: pythonpyqt5

解决方案


您的方法比它需要的要复杂得多,并且存在各种问题(其中一些非常重要)。

首先,不需要每次都计算正方形的大小,因为 QGraphicsView 可以通过使用 来做到这一点fitInView(),默认情况下会忽略纵横比。只需使用适合您需要的任意正方形大小。

然后,您永远不应该在 a 中创建和添加小部件paintEvent,因为它可能会导致递归。

一旦在小部件上设置了布局,除非绝对必要,否则不应设置另一个布局(无论如何,不​​应该那样做)。

使用 向 graphicsScene 添加 rect 时addRect,通常不需要先创建 QRectF,因为您可以使用接受坐标和大小的便捷函数(无论如何,Qt 都会在 C++ 端自动创建 QRectF,这可能会稍微快一些)。

另一方面,由于您要创建多个相同大小的矩形,您可以只创建一个 QRectF 然后用于translated()设置坐标。

当它们是现有变量或常量时,通常建议使用 QPointF 和 QSizeF 创建 QRectF,否则您可以只使用基本QRectF(x, y, width, height)构造函数。

class Map(QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.initUI()

    def initUI(self):
        self.setMinimumSize(500, 500) # min-size of the widget
        self.columns = 14 # num of columns in grid
        self.rows = 49 # num of rows in grid

        grid = QGridLayout(self)
        self.view = QGraphicsView()
        grid.addWidget(self.view)
        self.view.setRenderHints(QPainter.Antialiasing)

        self.scene = QGraphicsScene()
        self.view.setScene(self.scene)

        brush = QBrush(QColor(qRgb(255, 255, 255))) # background color of square
        pen = QPen(Qt.black) # border color of square
        side = 10
        rect = QRectF(0, 0, side, side)
        for i in range(self.rows):
            for j in range(self.columns):
                self.scene.addRect(rect.translated(i * side, j * side), pen, brush)

        # this is required to ensure that fitInView works on first shown too
        self.resizeScene()

    def resizeScene(self):
        self.view.fitInView(self.scene.sceneRect())

    def resizeEvent(self, event):
        # call fitInView each time the widget is resized
        self.resizeScene()

    def showEvent(self, event):
        # call fitInView each time the widget is shown
        self.resizeScene()

推荐阅读