PyQt中如何离线使用Plotly无需重绘整张图即可更新数据?
优化方案
核心思路
原有实现每次触发更新都会重新生成完整HTML并让QWebEngineView全量重加载,相当于重新渲染整个页面,自然流畅度差。我们可以利用Plotly原生的JS更新接口,仅更新折线数据,无需重绘整个图表,保留原有坐标轴等全部配置。
关键修改点
- 初始化阶段仅生成一次HTML结构,同时将Plotly图表实例绑定到JS全局变量方便后续调用
- 数据更新阶段通过
QWebEngineView.runJavaScript()调用Plotly的restyle接口,仅替换目标折线的x/y数据,不会触发全页面重绘
完整优化代码
from PyQt5.QtWebEngineWidgets import QWebEngineView from PyQt5.QtWidgets import * import plotly.graph_objects as go import plotly import numpy as np import json class Window(QWidget): def __init__(self): super(Window, self).__init__() grid_layout = QGridLayout() self.pb = QPushButton('plot') # 初始化示例数据 x = np.arange(0, 2*np.pi, 0.001) y = np.sin(x) fig = go.Figure(go.Scatter(x=x, y=y)) # 生成HTML时保留Plotly实例引用到全局变量 plot_div = plotly.offline.plot(fig, output_type='div', include_plotlyjs='cdn') # 加一段JS把生成的图表实例存到window下,方便后续调用 html = f""" <html> <body> {plot_div} <script> // 把页面里的第一个Plotly实例存为全局变量 window.plotlyFig = document.getElementsByClassName('plotly-graph-div')[0]; </script> </body> </html> """ self.plot_widget = QWebEngineView() self.plot_widget.setHtml(html) grid_layout.addWidget(self.plot_widget, 0, 0) grid_layout.addWidget(self.pb, 1, 0) self.setLayout(grid_layout) self.pb.clicked.connect(self.newplot) def newplot(self): # 生成新数据 x = np.arange(0, 2 * np.pi, 0.001) y = np.sin(x+np.random.uniform(low=0, high=2*np.pi)) # 把数据序列化为JSON格式传给JS x_json = json.dumps(x.tolist()) y_json = json.dumps(y.tolist()) # 调用Plotly.restyle仅更新第一条折线的x和y数据 self.plot_widget.page().runJavaScript(f""" Plotly.restyle(window.plotlyFig, {{ x: [{x_json}], y: [{y_json}] }}, [0]); """) if __name__ == '__main__': app = QApplication([]) window = Window() window.show() app.exec_()
效果说明
该方案仅更新数据部分,不会重新加载整个页面,更新延迟大幅降低,原有坐标轴的缩放、自定义配置等都会完整保留,不会被重置。
内容的提问来源于stack exchange,提问作者Victor Y.
相关产品推荐
相关产品推荐

