如何将NavigationToolbar所做的更改保留在定期更新的图形上

2024-10-02 08:22:57 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个pyqt5gui应用程序。这个应用程序显示了一个每秒更新一次的图(默认为xlimylim),基本上是一个实时图。我有这个功能,但我想添加一个NavigationToolbar,这样可以放大/缩小图形

我把工具栏添加到我的布局中,它就会显示出来。到现在为止,一直都还不错。现在我放大,图形被放大,但是一旦图形被周期性地更新,xlimylim再次被默认,缩放就消失了。我需要从工具栏调用哪些属性,以便保存它们并将它们传递给我的_update_canvas函数?我查看了https://matplotlib.org/3.1.0/api/axes_api.html,注意到函数get_ylim。所以我试着如下:

self._dynamic_ax.set_ylim(self._dynamic_ax.get_ylim())self._dynamic_ax.set_ylim(self._dynamic_ax2.get_ylim())

以及: self._dynamic_ax.set_navigate(True)

但是这些都不起作用。如何持久化NavigationToolbar设置?不仅是缩放,而且是平移

最小可运行代码示例:

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_()

我的实_update_canvas函数:

    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()

使用fill_between时,@DizietAsahi的解决方案不起作用。该区域会被覆盖而不被清除。所以它们被展示在一起

enter image description here


Tags: selfgetdynamicblueaxtabgraphcolor
1条回答
网友
1楼 · 发布于 2024-10-02 08:22:57

我的建议是不要在每次更新时都清除这个数字。相反,存储对plot()创建的Line2D艺术家的引用,并在update函数中更新{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_between()的代码。 fill_between()返回一个PolyCollection,这是很难更新的,因此最好的选择是删除PolyCollection并在每次更新时重新创建它(但不清除整个图)

相关问题 更多 >

    热门问题