PyQtGraph双Y轴绘图曲线不显示及ViewBox无法清除问题
PyQtGraph绘图曲线问题排查与修复
问题梳理
- 初始问题:双Y轴图表元素显示正常,但数据曲线不可见,确认曲线已添加但无法显示。
- 更新后问题:曲线可显示,但切换盆栽时,光强曲线会叠加显示所有盆栽的数据;尝试清除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()
关键改动说明
- ViewBox生命周期管理:用类属性
self.v2存储右侧ViewBox,每次调用函数时先检查并移除旧的ViewBox,再创建新的,彻底避免残留曲线。 - 简化清除操作:只调用一次
p.clear()清除PlotItem中的所有元素,避免重复清除导致的逻辑混乱。 - 信号绑定优化:用类属性存储信号连接对象,每次调用前先断开旧连接,再绑定新的,防止重复触发回调。
- 移除冗余代码:删除了无效的
p.getViewBox().removeItem(v2)和重复的清除操作,让代码逻辑更清晰。
内容的提问来源于stack exchange,提问作者Muserica
相关产品推荐
相关产品推荐

