You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyQt6中QGraphicsScene绘制256×256网格的尺寸异常问题求助

QGraphicsScene网格绘制异常与坐标系疑问

问题描述

  • 需求:绘制256×256的网格,每个单元格32×32像素,所有单元格(含整个网格边界)均有1像素厚边框,计算场景宽高应为 256×32+257=8449 像素
  • 异常:使用QGraphicsLineItem绘制后,截图测量单元格宽高在40-41像素之间,部分并非正方形,推测与item、scene、view三种坐标系有关
  • 疑问:Qt文档称视图坐标系左上角为(0, 0),右下角为(view.width, view.height),按0索引逻辑会导致尺寸为 (view.width + 1)×(view.height + 1),这似乎不合理

尝试代码

from PyQt6.QtWidgets import QApplication, QGraphicsScene, QGraphicsView, QGraphicsLineItem, QMainWindow, QVBoxLayout, QFrame
from PyQt6.QtGui import QColor, QPen

app = QApplication([])

scene = QGraphicsScene(0, 0, 8448,8448)
scene.setBackgroundBrush(QColor(0, 255, 0))

for i in range(0, 8449, 33):
    pen = QPen(QColor(0, 0, 255))
    pen.setWidth(0)
    horizontal_line = QGraphicsLineItem(0, 0, 8448, 0)
    horizontal_line.setPen(pen)
    horizontal_line.setPos(0, i)
    scene.addItem(horizontal_line)
    horizontal_line.setOpacity(0.5)
    vertical_line = QGraphicsLineItem(0, 0, 0, 8448)
    vertical_line.setPen(pen)
    vertical_line.setPos(i, 0)
    vertical_line.setOpacity(0.5)
    scene.addItem(vertical_line)

window = QMainWindow()
window.show()

container = QFrame(parent=window)
vlayout = QVBoxLayout()

view = QGraphicsView(scene, parent=container)
vlayout.addWidget(view)

container.setLayout(vlayout)
window.setCentralWidget(container)

view.show()
app.exec()

问题解决

1. 网格尺寸异常的修复

核心问题在于视图默认的缩放/适配逻辑会自动拉伸场景,加上场景尺寸和线条绘制的小误差,导致单元格变形:

  • 修正场景尺寸:256个32px单元格加257条1px边框,总长度应为256*32 + 257 = 8449像素,创建场景时要对应设置为QGraphicsScene(0, 0, 8449, 8449)(之前的8448少了1px)
  • 强制1:1视图映射:禁用视图的自动缩放锚点,确保场景和视图像素一一对应:
    view.setTransformationAnchor(QGraphicsView.ViewportAnchor.NoAnchor)
    view.setResizeAnchor(QGraphicsView.ViewportAnchor.NoAnchor)
    view.setSceneRect(scene.sceneRect())
    
  • 简化线条绘制:直接在场景坐标系中定义线条位置,无需额外调用setPos,避免坐标偏移

修改后的完整代码:

from PyQt6.QtWidgets import QApplication, QGraphicsScene, QGraphicsView, QGraphicsLineItem, QMainWindow, QVBoxLayout, QFrame
from PyQt6.QtGui import QColor, QPen
from PyQt6.QtCore import Qt

app = QApplication([])

# 修正场景尺寸为8449x8449
scene = QGraphicsScene(0, 0, 8449, 8449)
scene.setBackgroundBrush(QColor(0, 255, 0))

pen = QPen(QColor(0, 0, 255))
pen.setWidth(0)  # 0表示1px固定宽度,不受变换影响

# 绘制257条水平线(间隔33px:32px单元格+1px边框)
for y in range(0, 8449, 33):
    line = QGraphicsLineItem(0, y, 8449, y)
    line.setPen(pen)
    line.setOpacity(0.5)
    scene.addItem(line)

# 绘制257条垂直线
for x in range(0, 8449, 33):
    line = QGraphicsLineItem(x, 0, x, 8449)
    line.setPen(pen)
    line.setOpacity(0.5)
    scene.addItem(line)

window = QMainWindow()
container = QFrame(parent=window)
vlayout = QVBoxLayout(container)

view = QGraphicsView(scene, parent=container)
# 强制1:1映射,禁用自动缩放
view.setTransformationAnchor(QGraphicsView.ViewportAnchor.NoAnchor)
view.setResizeAnchor(QGraphicsView.ViewportAnchor.NoAnchor)
view.setSceneRect(scene.sceneRect())
# 可选:关闭滚动条,避免视图超出场景范围
view.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
view.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)

vlayout.addWidget(view)
window.setCentralWidget(container)
window.show()

app.exec()

2. 视图坐标系疑问解答

Qt的视图坐标系是连续浮点坐标系,并非离散的像素索引:

  • (0, 0)对应视图左上角像素的左上角,(view.width(), view.height())对应右下角像素的右下角
  • 单个像素占据的坐标区间是[n, n+1](n为整数),比如宽100px的视图,x坐标范围0到100,刚好覆盖100个像素
  • 文档描述是图形系统的标准定义,和离散像素索引逻辑不冲突,两者是连续坐标与离散采样的关系

内容的提问来源于stack exchange,提问作者Ethan Alwaise

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 13:35:16