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

QGraphicsScene未显示在窗口左上角的问题排查

问题描述

我参考Stack Overflow上的折线绘制示例编写了PyQt5绘图代码,但绘制的图形并未显示在窗口左上角。我尝试通过scene = QGraphicsScene(0,0, self.width, self.height)设置场景范围,但问题依旧。请指出需要修改的内容,让图形显示在正确位置。

原始代码
#!/usr/bin/python3
#
from PyQt5.QtGui import QColor,  QFont, QPainter, QPainterPath, QPainterPathStroker, QPen, QPolygonF ####NEW
from PyQt5.QtCore import Qt, QPointF
from PyQt5.QtWidgets import QApplication, QGraphicsScene, QGraphicsPathItem, QGraphicsView, QMainWindow
import sys
class plot(QMainWindow):
#
    def plot_parameters(self, data, width, height):
        len_data = len(data)
# pixel_ratio changes the raw data x-value to a screen x-value
        pixel_ratio_x = width/len_data # transform ratio for x-axis
        min_y = min(data)
        max_y = max(data)
# pixel_ratio changes the raw data y-value to a screen y-value
        pixel_ratio_y = height/(max_y-min_y)
        return pixel_ratio_x, pixel_ratio_y, min_y, max_y, len_data
#
    def y_axis_data(self, min_y, max_y):
# Y labels - a plotdata must be >= 0 - TODO
        y_labels = [max_y]
        yl_increment='1' # label increment
        lenlblvalue=len(str(int(min_y))) # label length
        if min_y>0.0:
            lenlblvalue=len(str(int(max_y)))
            min_y=0.0
        yunit=len(str(max(int(max_y), abs(int(min_y)))))-1 # maximum units of y values
        for i in range(0, yunit): # create increment
            yl_increment+='0'
        lblvalue=int((int(str(max_y)[0])+1)*int(yl_increment))# first lblvalue
        yl_increment=int(yl_increment)*.25
        while lblvalue>=max_y:
            lblvalue-=yl_increment
        while (lblvalue>min_y and lblvalue<max_y): #draw value labels and lines
            lblvalue -= yl_increment
            y_labels.append(lblvalue)
# y axis line tick marks
        return y_labels,yl_increment

    def plot_decoration(self, scene, data, min_y, max_y, width, height):
# create a y-axis with tick marks, labels and lines
        pen = QPen(Qt.gray, 0.5)
# calculate the y-axis line positions
        y_labels,yl_increment = self.y_axis_data(min_y, max_y)
# initial line position on y-axis
        y_position = 0
# add a tick mark and label at y_position
        for l in list(y_labels):
            y_position = l
            line = scene.addLine(0,height-(y_position*self.pixel_ratio_y), width, height-(y_position*self.pixel_ratio_y))
            line.setPen(pen)
            txt = scene.addText(str(l), QFont('sans',8,QFont.Light)) #/27612052/
            txt.setPos(0,height-(y_position*self.pixel_ratio_y)-12) # -12 to line up with tick mark
            y_position += yl_increment # move to next position
#
    def create_plot(self):
        self.width = 700
        self.height = 400
        poly_plot_data = []
# create scene and view
        scene = QGraphicsScene(0,0, self.width, self.height) #/10443894/
        view = QGraphicsView(scene)
        view.show()
# raw data
        data = [-10, -12, 0, -5, -10, -20]
# get parameters of data for plotting
        self.pixel_ratio_x, self.pixel_ratio_y, min_y, max_y, len_data = self.plot_parameters(data, self.width, self.height)
# create polyline data
        for d in range(0, len_data):
            poly_plot_data.append(QPointF(int(d*self.pixel_ratio_x), ((self.height)-(data[d])*self.pixel_ratio_y)))
# draw the axes - TODO x-axis
        self.plot_decoration(scene, data, min_y, max_y, self.width, self.height)
# draw polyline
        poly = QPolygonF(poly_plot_data) # /68704191/
        path = QPainterPath()
        path.addPolygon(poly)
        new_item = QGraphicsPathItem(path, None)
        new_item.setPen(QPen(QColor("red"), 1))
        scene.addItem(new_item)
        return view
#
    def __init__(self):
        super().__init__()
        plot = self.create_plot()
        self.setCentralWidget(plot)
        self.show()
###########################################
if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = plot()
    sys.exit(app.exec_())
修复方案

问题核心是Y轴坐标转换未考虑数据的最小值偏移,导致图形超出场景可见范围,同时存在视图重复显示的问题。需要修改以下几点:

1. 修正Y轴坐标转换逻辑

原始代码直接使用data[d]计算屏幕Y坐标,未处理数据中的负数偏移,导致图形被推到场景下方(超出视图可见区域)。需将数据相对于min_y做偏移:
在create_plot方法中,修改折线点的Y坐标计算:

poly_plot_data.append(QPointF(int(d*self.pixel_ratio_x), self.height - (data[d] - min_y) * self.pixel_ratio_y))

2. 修正Y轴刻度的坐标计算

同样在plot_decoration方法中,刻度线和标签的Y坐标也需要基于min_y偏移:

# 替换原line和txt的pos计算
y_screen_pos = height - (y_position - min_y) * self.pixel_ratio_y
line = scene.addLine(0, y_screen_pos, width, y_screen_pos)
txt.setPos(0, y_screen_pos - 12)

3. 移除重复的视图显示调用

create_plot中调用的view.show()会与主窗口的self.show()冲突,导致窗口显示异常,删除该行即可。

4. 确保视图与场景左上角对齐(可选)

为避免视图自动居中场景,添加对齐设置:

view = QGraphicsView(scene)
view.setAlignment(Qt.AlignTop | Qt.AlignLeft)
修改后的完整代码
#!/usr/bin/python3
#
from PyQt5.QtGui import QColor,  QFont, QPainter, QPainterPath, QPainterPathStroker, QPen, QPolygonF
from PyQt5.QtCore import Qt, QPointF
from PyQt5.QtWidgets import QApplication, QGraphicsScene, QGraphicsPathItem, QGraphicsView, QMainWindow
import sys
class plot(QMainWindow):
#
    def plot_parameters(self, data, width, height):
        len_data = len(data)
        pixel_ratio_x = width/len_data
        min_y = min(data)
        max_y = max(data)
        pixel_ratio_y = height/(max_y-min_y)
        return pixel_ratio_x, pixel_ratio_y, min_y, max_y, len_data
#
    def y_axis_data(self, min_y, max_y):
        y_labels = [max_y]
        yl_increment='1'
        lenlblvalue=len(str(int(min_y)))
        if min_y>0.0:
            lenlblvalue=len(str(int(max_y)))
            min_y=0.0
        yunit=len(str(max(int(max_y), abs(int(min_y)))))-1
        for i in range(0, yunit):
            yl_increment+='0'
        lblvalue=int((int(str(max_y)[0])+1)*int(yl_increment))
        yl_increment=int(yl_increment)*.25
        while lblvalue>=max_y:
            lblvalue-=yl_increment
        while (lblvalue>min_y and lblvalue<max_y):
            lblvalue -= yl_increment
            y_labels.append(lblvalue)
        return y_labels,yl_increment

    def plot_decoration(self, scene, data, min_y, max_y, width, height):
        pen = QPen(Qt.gray, 0.5)
        y_labels,yl_increment = self.y_axis_data(min_y, max_y)
        for l in y_labels:
            y_position = l
            y_screen_pos = height - (y_position - min_y) * self.pixel_ratio_y
            line = scene.addLine(0, y_screen_pos, width, y_screen_pos)
            line.setPen(pen)
            txt = scene.addText(str(l), QFont('sans',8,QFont.Light))
            txt.setPos(0, y_screen_pos - 12)
#
    def create_plot(self):
        self.width = 700
        self.height = 400
        poly_plot_data = []
        scene = QGraphicsScene(0,0, self.width, self.height)
        view = QGraphicsView(scene)
        view.setAlignment(Qt.AlignTop | Qt.AlignLeft)
        data = [-10, -12, 0, -5, -10, -20]
        self.pixel_ratio_x, self.pixel_ratio_y, min_y, max_y, len_data = self.plot_parameters(data, self.width, self.height)
        for d in range(0, len_data):
            poly_plot_data.append(QPointF(int(d*self.pixel_ratio_x), self.height - (data[d] - min_y) * self.pixel_ratio_y))
        self.plot_decoration(scene, data, min_y, max_y, self.width, self.height)
        poly = QPolygonF(poly_plot_data)
        path = QPainterPath()
        path.addPolygon(poly)
        new_item = QGraphicsPathItem(path, None)
        new_item.setPen(QPen(QColor("red"), 1))
        scene.addItem(new_item)
        return view
#
    def __init__(self):
        super().__init__()
        plot_view = self.create_plot()
        self.setCentralWidget(plot_view)
        self.show()
###########################################
if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = plot()
    sys.exit(app.exec_())

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 20:24:54