Python 如何在定期更新的图表上保留NavigationToolbar所做的更改
我有一个PyQt5 GUI应用程序。这个应用程序显示了一个每秒更新一次的图形(使用默认的Python 如何在定期更新的图表上保留NavigationToolbar所做的更改,python,matplotlib,graph,pyqt5,Python,Matplotlib,Graph,Pyqt5,我有一个PyQt5 GUI应用程序。这个应用程序显示了一个每秒更新一次的图形(使用默认的xlim和ylim),基本上是一个实时图形。我有这个功能,但我想添加一个NavigationToolbar,这样可以放大/缩小图表 我将工具栏添加到我的布局中,并显示出来。到现在为止,一直都还不错。现在我放大了,图形被放大了,但是一旦图形被定期更新,xlim和ylim再次被默认,并且缩放消失了。我需要从工具栏调用哪些属性,以便保存它们并将它们传递给我的\u update\u canvas函数?我看了一下,注意
xlim
和ylim
),基本上是一个实时图形。我有这个功能,但我想添加一个NavigationToolbar
,这样可以放大/缩小图表
我将工具栏添加到我的布局中,并显示出来。到现在为止,一直都还不错。现在我放大了,图形被放大了,但是一旦图形被定期更新,xlim
和ylim
再次被默认,并且缩放消失了。我需要从工具栏调用哪些属性,以便保存它们并将它们传递给我的\u update\u canvas
函数?我看了一下,注意到函数get_ylim
。所以我试着如下:
self.\u dynamic\u ax.set\u ylim(self.\u dynamic\u ax.get\u ylim())
和self.\u dynamic\u ax.set\u ylim(self.\u dynamic\u ax2.get\u ylim())
以及:
self.\u动态\u ax.set\u导航(True)
然而,这些都不起作用。如何保持由导航工具栏设置的设置?不仅是缩放,还有平移
最小可运行代码示例:
import sys
from matplotlib.backends.qt_compat import QtCore, QtWidgets, QtGui
from matplotlib.backends.backend_qt5agg import (FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
buffer_size = 120
t = [t for t in range(buffer_size)]
bitthrough = [t for t in range(buffer_size)]
errors = bitthrough[::-1]
class App(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("PCANbus sniffer")
self.table_widget = MyTableWidget(self)
self.setCentralWidget(self.table_widget)
self.setMinimumSize(QtCore.QSize(640, 400))
self.show()
class MyTableWidget(QtWidgets.QWidget):
def __init__(self, parent):
super(QtWidgets.QWidget, self).__init__(parent)
self.layout = QtWidgets.QVBoxLayout(self)
self.tabs = QtWidgets.QTabWidget()
self.tab_graph = QtWidgets.QWidget()
self.tab_info = QtWidgets.QWidget()
self.tabs.addTab(self.tab_graph, "PCANbus occupation")
self.tabs.addTab(self.tab_info, "PCANbus information")
self.tab_graph.layout = QtWidgets.QVBoxLayout(self)
self.dynamic_canvas = FigureCanvas(Figure(figsize=(6, 4)))
self.tab_graph.layout.addWidget(self.dynamic_canvas)
self.toolbar = NavigationToolbar(self.dynamic_canvas, self)
self.tab_graph.layout.addWidget(self.toolbar)
self._dynamic_ax = self.dynamic_canvas.figure.subplots()
self._dynamic_ax.set_xlabel("time (s)")
self._dynamic_ax.set_xlim(-5, 125)
self._dynamic_ax.set_ylabel("Throughput (%)", color="black")
self._dynamic_ax.set_ylim(-5, 120)
self._dynamic_ax.tick_params(axis="y", labelcolor="black")
# self._dynamic_ax.set_navigate(True)
self._dynamic_ax2 = self._dynamic_ax.twinx()
self._dynamic_ax2.set_ylabel("Errors (%)", color="blue")
self._dynamic_ax2.set_ylim(-4, 100)
self._dynamic_ax2.tick_params(axis="y", labelcolor="blue")
#self._dynamic_ax2.set_navigate(True)
self.tab_graph.setLayout(self.tab_graph.layout)
self.layout.addWidget(self.tabs)
self.setLayout(self.layout)
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self._update_canvas)
self.timer.start(1000)
def _update_canvas(self):
self._dynamic_ax.clear()
self._dynamic_ax.plot(t, bitthrough, color="black")
self._dynamic_ax.set_xlabel("time (s)")
self._dynamic_ax.set_ylabel("Throughput (%)", color="black")
self._dynamic_ax.set_ylim(-5, 120) #self._dynamic_ax.get_ylim())
self._dynamic_ax.tick_params(axis="y", labelcolor="black")
self._dynamic_ax2.clear()
self._dynamic_ax2.plot(t, errors, color="blue")
self._dynamic_ax2.set_ylabel("Errors", color="blue")
self._dynamic_ax2.set_ylim(-4, 100) #self._dynamic_ax2.get_ylim())
self._dynamic_ax2.tick_params(axis="y", labelcolor="blue")
self._dynamic_ax.figure.canvas.draw_idle()
if __name__ == "__main__":
qapp = QtWidgets.QApplication(sys.argv)
app = App()
app.show()
qapp.exec_()
我的真实\u更新\u画布
功能:
def _update_canvas(self):
wh_green = [a <= b for a, b in zip(bitthrough, llvl)]
wh_orange = [a > b and a <= c
for a, b, c in zip(bitthrough, llvl, lvl)]
wh_red = [a > b for a, b, in zip(bitthrough, lvl)]
# self._dynamic_ax.clear()
# self._dynamic_ax2.clear()
self._dynamic_ax.fill_between(
t, 0, bitthrough, where=wh_red, color="red", interpolate=True
)
self._dynamic_ax.fill_between(
t, 0, bitthrough, where=wh_orange, color="orange", interpolate=True
)
self._dynamic_ax.fill_between(
t, 0, bitthrough, where=wh_green, color="green", interpolate=True
)
# self._dynamic_ax.plot(t, bitthrough, color="black")
# self._dynamic_ax.set_xlabel("time (s)")
# self._dynamic_ax.set_ylabel("Throughput (%)", color="black")
# #self._dynamic_ax.set_ylim(self._dynamic_ax.get_ylim())
# self._dynamic_ax.tick_params(axis="y", labelcolor="black")
# self._dynamic_ax2.plot(t, errors, color="blue")
# self._dynamic_ax2.set_ylabel("Errors", color="blue")
# #self._dynamic_ax2.set_ylim(self._dynamic_ax2.get_ylim())
# self._dynamic_ax2.tick_params(axis="y", labelcolor="blue")
self._plot1.set_ydata(bitthrough)
self._plot2.set_ydata(errors)
# logging.debug("redrawing graph!!")
self._dynamic_ax.figure.canvas.draw_idle()
def\u更新\u画布(自):
wh_green=[a b和a b表示a,b,在zip中(比特通过,lvl)]
#self.\u dynamic\u ax.clear()
#self.\u dynamic\u ax2.clear()
自。\ u动态\u ax.fill\u之间(
t、 0,位通过,其中=wh\u red,color=“red”,插值=True
)
自。\ u动态\u ax.fill\u之间(
t、 0,位通过,其中=wh_orange,color=“orange”,插值=True
)
自。\ u动态\u ax.fill\u之间(
t、 0,位通过,其中=wh\u绿色,color=“绿色”,插值=True
)
#自动态轴测图(t、bitthrough、color=“黑色”)
#self.\u dynamic\u ax.set\u xlabel(“时间”)
#self.\u dynamic\u ax.set\u ylabel(“吞吐量(%)”,color=“黑色”)
##self._dynamic_ax.set_ylim(self._dynamic_ax.get_ylim())
#自身动态勾选参数(轴=“y”,标签颜色=“黑色”)
#自.\u动态\u ax2.绘图(t、错误、颜色=“蓝色”)
#self.\u dynamic\u ax2.set\u ylabel(“错误”,color=“蓝色”)
##self._dynamic_ax2.set_ylim(self._dynamic_ax2.get_ylim())
#self.\u dynamic\u ax2.勾选参数(axis=“y”,labelcolor=“blue”)
自绘图1.设置数据(位通过)
自绘图2.设置数据(错误)
#debug(“重画图形!!”)
self.\u dynamic\u ax.figure.canvas.draw\u idle()
在
之间使用fill\u时,@DizietAsahi的解决方案不起作用。该区域被覆盖而未被清除。因此,它们显示在彼此的顶部
我的建议是不要在每次更新时清除该图。相反,存储对由plot()
创建的Line2D美工师的引用,并在更新函数中更新{x | y}数据(使用set_data()
或set_ydata()
)
import sys
from matplotlib.backends.qt_compat import QtCore, QtWidgets, QtGui
from matplotlib.backends.backend_qt5agg import (FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import numpy as np
buffer_size = 120
t = np.linspace(0, 100, buffer_size)
bitthrough = 120*np.random.random(size=(buffer_size,))
errors = bitthrough[::-1]
class App(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("PCANbus sniffer")
self.table_widget = MyTableWidget(self)
self.setCentralWidget(self.table_widget)
self.setMinimumSize(QtCore.QSize(640, 400))
self.show()
class MyTableWidget(QtWidgets.QWidget):
def __init__(self, parent):
super(QtWidgets.QWidget, self).__init__(parent)
self.layout = QtWidgets.QVBoxLayout(self)
self.tabs = QtWidgets.QTabWidget()
self.tab_graph = QtWidgets.QWidget()
self.tab_info = QtWidgets.QWidget()
self.tabs.addTab(self.tab_graph, "PCANbus occupation")
self.tabs.addTab(self.tab_info, "PCANbus information")
self.tab_graph.layout = QtWidgets.QVBoxLayout(self)
self.dynamic_canvas = FigureCanvas(Figure(figsize=(6, 4)))
self.tab_graph.layout.addWidget(self.dynamic_canvas)
self.toolbar = NavigationToolbar(self.dynamic_canvas, self)
self.tab_graph.layout.addWidget(self.toolbar)
self._dynamic_ax = self.dynamic_canvas.figure.subplots()
self._dynamic_ax.set_xlabel("time (s)")
self._dynamic_ax.set_xlim(-5, 125)
self._dynamic_ax.set_ylabel("Throughput (%)", color="black")
self._dynamic_ax.set_ylim(-5, 120)
self._dynamic_ax.tick_params(axis="y", labelcolor="black")
# self._dynamic_ax.set_navigate(True)
self._dynamic_ax2 = self._dynamic_ax.twinx()
self._dynamic_ax2.set_ylabel("Errors (%)", color="blue")
self._dynamic_ax2.set_ylim(-4, 100)
self._dynamic_ax2.tick_params(axis="y", labelcolor="blue")
#self._dynamic_ax2.set_navigate(True)
##
## Create plots here (initially empty)
##
self._plot1, = self._dynamic_ax.plot(t, np.empty(shape=(buffer_size,)), color="black")
self._plot2, = self._dynamic_ax2.plot(t, np.empty(shape=(buffer_size,)), color="blue")
self._fill1 = self._dynamic_ax.fill_between(t, 0, bitthrough, color="orange")
self.tab_graph.setLayout(self.tab_graph.layout)
self.layout.addWidget(self.tabs)
self.setLayout(self.layout)
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self._update_canvas)
self.timer.start(1000)
def _update_canvas(self):
bitthrough = 120*np.random.random(size=(buffer_size, ))
errors = bitthrough[::-1]
##
## update the content of the plots here, without clearing the figure
##
self._plot1.set_ydata(bitthrough)
self._plot2.set_ydata(errors)
self._fill1.remove()
self._fill1 = self._dynamic_ax.fill_between(t, 0, bitthrough, color="orange")
self._dynamic_ax.figure.canvas.draw_idle()
if __name__ == "__main__":
qapp = QtWidgets.QApplication(sys.argv)
app = App()
app.show()
qapp.exec_()
编辑
我添加了一些代码来处理fill\u between()
。
fill\u between()
返回一个PolyCollection
,这是一个很难更新的问题,因此最好的选择是删除PolyCollection
,并在每次更新时重新创建它(但不清除整个图) 谢谢!非常喜欢这个解决方案,优雅而简单。这不是我真正的update
功能。我在我的作品中添加了我现在在评论中使用的旧代码。我真的认为fill\u-between
不起作用。原来它是由magicOk交织在一起的,所以我想我没有做足够的测试。这在使用在之间填充时不起作用。有什么建议吗?我添加了更多处理fill\u between()
的代码,感谢您的快速回复。这很好用。添加了我自己的\u fill\u red
和\u fill\u green
。