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

PyQtGraph双Y轴绘图曲线不显示及ViewBox无法清除问题

PyQtGraph绘图曲线问题排查与修复

问题梳理

  1. 初始问题:双Y轴图表元素显示正常,但数据曲线不可见,确认曲线已添加但无法显示。
  2. 更新后问题:曲线可显示,但切换盆栽时,光强曲线会叠加显示所有盆栽的数据;尝试清除ViewBox后无法重新添加曲线,元素被彻底删除。

代码中的核心问题

  • 重复创建ViewBox未清理:每次调用sensor_graph都新建v2,但旧的v2没有从场景中移除,导致旧的光强曲线残留,切换盆栽时叠加显示。
  • 清除操作冗余且逻辑混乱:多次重复调用p.clear()、p.clearPlots()等清除方法,不仅无效还可能打乱元素管理;而且在创建曲线前再次执行清除,导致之前的配置被重置。
  • ViewBox的移除逻辑错误:p.getViewBox().removeItem(v2)这行代码中,v2是刚创建的新ViewBox,根本没加入到p.vb中,所以这行完全无效,无法移除旧的ViewBox。
  • 信号重复绑定:每次调用函数都绑定p.vb.sigResized信号,导致多次触发回调,引发不必要的重绘和状态异常。

修正后的代码

def sensor_graph(self, pot_id):
    # 从同步控件获取正确的盆栽ID
    pot_id = self.sync_pot.property("id")
    print(pot_id)

    # 获取PlotItem,避免重复调用getPlotItem()
    p = self.plot_widget.plotItem
    # 一次性清除PlotItem中的所有元素
    p.clear()
    
    # 设置边距(可移到类初始化方法中,避免重复执行)
    p.setContentsMargins(0, 10, 0, 10)
    p.setTitle("Sensor readings")

    # 处理右侧ViewBox:先移除旧的ViewBox(如果存在)
    if hasattr(self, 'v2'):
        self.v2.scene().removeItem(self.v2)
        del self.v2
    
    # 创建新的右侧ViewBox
    self.v2 = pg.ViewBox()
    p.scene().addItem(self.v2)

    # 轴链接配置
    p.getAxis('left').linkToView(p.vb)
    p.getAxis('right').linkToView(self.v2)
    # 设置日期X轴
    x_axis = pg.DateAxisItem(orientation='bottom')
    p.setAxisItems({'bottom': x_axis})
    self.v2.setXLink(p)

    # 从数据库读取当前盆栽的传感器数据
    with Session(bind=db_engine) as local_session:
        readings = local_session.query(SensorReadings).filter(SensorReadings.pot_id == pot_id).all()
        for reading in readings:
            print(f"Reading - Timestamp: {reading.datetime}, Temperature: {reading.temperature_celsius}, Light Intensity: {reading.light_intensity_lux}, Soil Moisture: {reading.soil_moisture}, Soil pH: {reading.soil_ph}")

    # 转换数据格式
    x_data = [reading.datetime.timestamp() for reading in readings]
    y1_data = [reading.temperature_celsius for reading in readings]
    y2_data = [reading.light_intensity_lux for reading in readings]
    y3_data = [reading.soil_moisture for reading in readings]
    y4_data = [reading.soil_ph for reading in readings]

    # 创建曲线
    pen1 = pg.mkPen(color='#E07D54')
    pen2 = pg.mkPen(color='#DB9600')
    pen3 = pg.mkPen(color='#5E9299')
    pen4 = pg.mkPen(color='#523D35')

    curve1 = pg.PlotCurveItem(x_data, y1_data, pen=pen1, name='Temperature')
    curve2 = pg.PlotCurveItem(x_data, y2_data, pen=pen2, name='Light Intensity')
    curve3 = pg.PlotCurveItem(x_data, y3_data, pen=pen3, name='Soil Moisture')
    curve4 = pg.PlotCurveItem(x_data, y4_data, pen=pen4, name='Soil pH')
   
    # 添加曲线到对应容器
    p.addItem(curve1)
    p.addItem(curve3)
    p.addItem(curve4)
    self.v2.addItem(curve2)
    
    # 设置轴标签
    self.plot_widget.setLabel('left', '<span style="color:#E07D54">Temperature(°C),</span> <span style="color:#5E9299">Soil Moisture,</span> <span style="color:#523D35">Soil pH</span>')
    self.plot_widget.setLabel('right', '<span style="color:#DB9600">Light Intensity (LUX)</span>')
    self.plot_widget.setLabel('bottom', 'Time')

    # 同步ViewBox几何位置
    self.v2.setGeometry(p.vb.sceneBoundingRect())
    self.v2.linkedViewChanged(p.vb, self.v2.XAxis)

    # 先断开旧的信号连接(如果存在),再绑定新的
    if hasattr(self, 'resize_conn'):
        self.resize_conn.disconnect()
    self.resize_conn = p.vb.sigResized.connect(lambda: self.v2.setGeometry(p.vb.sceneBoundingRect()))

    # 强制更新绘图
    self.plot_widget.update()

关键改动说明

  1. ViewBox生命周期管理:用类属性self.v2存储右侧ViewBox,每次调用函数时先检查并移除旧的ViewBox,再创建新的,彻底避免残留曲线。
  2. 简化清除操作:只调用一次p.clear()清除PlotItem中的所有元素,避免重复清除导致的逻辑混乱。
  3. 信号绑定优化:用类属性存储信号连接对象,每次调用前先断开旧连接,再绑定新的,防止重复触发回调。
  4. 移除冗余代码:删除了无效的p.getViewBox().removeItem(v2)和重复的清除操作,让代码逻辑更清晰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 09:34:59