У меня, надеюсь, относительно простая ситуация с использованием панели инструментов навигации Matplotlib. Я хочу иметь возможность сохранять предыдущее значение масштабирования, панорамирование камеры и т. д. Между обновлениями рисунка. Я сохранил здесь встраивание PyQt5 (которое я использую в своем проекте) на случай, если между ними потребуется дополнительная связь. Большое спасибо за то, что заглянули!
import sys
import os
import random
import matplotlib
matplotlib.use('Qt5Agg')
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QGridLayout, QFileDialog, QPushButton
from numpy import arange
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
class MyMplCanvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi)
self.fig = fig ###
self.axes = fig.add_subplot(111)
self.axes.hold(False)
self.compute_initial_figure()
FigureCanvas.__init__(self, fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self,
QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
def compute_initial_figure(self):
pass
class MyDynamicMplCanvas(MyMplCanvas):
def __init__(self, *args, **kwargs):
MyMplCanvas.__init__(self, *args, **kwargs)
timer = QtCore.QTimer(self)
timer.timeout.connect(self.update_figure)
timer.start(1000)
def compute_initial_figure(self):
self.axes.plot([0, 1, 2, 3], [1, 2, 0, 4], 'b')
def update_figure(self):
l = [random.randint(0, 10) for i in range(4)]
self.axes.cla()
self.axes.plot([0, 1, 2, 3], l, 'b')
self.draw()
class P1(QtWidgets.QWidget):
def __init__(self, parent=None):
super(P1, self).__init__(parent)
layout = QGridLayout(self)
self.plot_canvas = MyDynamicMplCanvas(self, width=5, height=4, dpi=100)
self.navi_toolbar = NavigationToolbar(self.plot_canvas, self)
layout.addWidget(self.plot_canvas, 1, 1, 1, 1)
layout.addWidget(self.navi_toolbar, 2, 1, 1, 1)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.stack = QtWidgets.QStackedWidget(self)
P1f = P1(self)
self.stack.addWidget(P1f)
self.setCentralWidget(self.stack)
if __name__ == '__main__':
qApp = QtWidgets.QApplication(sys.argv)
aw = MainWindow()
aw.show()
sys.exit(qApp.exec_())






Главными злодеями здесь являются axes.hold(False) и axes.cla(). Они отвечают за очистку осей и фигуры, что (как правило) сбрасывает вид графика.
Убрав их, вы можете правильно использовать self.axes.autoscale(enable=False), который я рекомендую вам поместить после вашего первого графика в compute_initial_figure(), чтобы график был, по крайней мере, несколько масштабирован в начале.
Затем, чтобы иметь возможность очистить ваши предыдущие графики, вы можете создать другой атрибут для класса MyMplCanvas, возможно, self.plotted_line или что-то в этом роде, инициализированный с помощью None. Каждый раз, когда вы вызываете self.axes.plot(...), присваивайте возвращаемое значение self.plotted_line, например: self.plotted_line, = self.axes.plot(...). Обратите внимание на запятую после self.plotted_line, которая является одним из способов присвоить только первое возвращаемое значение, которое вас интересует.
Наконец, прямо перед каждым новым сюжетом проверяйте и звоните
if self.plotted_line is not None:
self.plot.remove()
который эффективно удалит предыдущий сюжет.
Классы холста будут выглядеть примерно так (изменения были незначительными).
class MyMplCanvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi)
self.fig = fig ###
self.axes = fig.add_subplot(111)
self.plotted_line = None
self.compute_initial_figure()
FigureCanvas.__init__(self, fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self,
QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
def compute_initial_figure(self):
pass
class MyDynamicMplCanvas(MyMplCanvas):
def __init__(self, *args, **kwargs):
MyMplCanvas.__init__(self, *args, **kwargs)
timer = QtCore.QTimer(self)
timer.timeout.connect(self.update_figure)
timer.start(1000)
def compute_initial_figure(self):
self.plotted_line, = self.axes.plot([0, 1, 2, 3], [1, 2, 0, 4], 'b')
self.axes.autoscale(enable=False)
def update_figure(self):
l = [random.randint(0, 10) for i in range(4)]
if self.plotted_line is not None:
self.plotted_line.remove()
self.plotted_line, = self.axes.plot([0, 1, 2, 3], l, 'b')
self.draw()
Возможно, вы действительно сможете использовать это, возможно, пометив соответствующие графики соответствующим образом, так что пока не теряйте надежды на этот метод. Обязательно подумайте о создании еще одного поста.
Значения масштабирования можно сохранить напрямую, подключив mpl 'draw_event' с:
def __init__(self, parent=None, width=5, height=4, dpi=100):
...
self.mpl_connect('draw_event', self.on_draw)
def on_draw(self, event):
self.xlim = self.axes.get_xlim()
self.ylim = self.axes.get_ylim()
а затем восстанавливается после вызова функции plot ():
def update_figure(self):
...
self.axes.plot([0, 1, 2, 3], l, 'b')
self.axes.set_xlim(self.xlim)
self.axes.set_ylim(self.ylim)
...
https://matplotlib.org/users/event_handling.html
Полный код:
import random
import sys
import matplotlib
matplotlib.use('Qt5Agg')
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import QGridLayout
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
class MyMplCanvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi)
self.fig = fig ###
self.axes = fig.add_subplot(111)
self.axes.hold(False)
self.compute_initial_figure()
FigureCanvas.__init__(self, fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self,
QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
self.xlim = self.axes.get_xlim()
self.ylim = self.axes.get_ylim()
self.mpl_connect('draw_event', self.on_draw)
def on_draw(self, event):
self.xlim = self.axes.get_xlim()
self.ylim = self.axes.get_ylim()
def compute_initial_figure(self):
pass
class MyDynamicMplCanvas(MyMplCanvas):
def __init__(self, *args, **kwargs):
MyMplCanvas.__init__(self, *args, **kwargs)
timer = QtCore.QTimer(self)
timer.timeout.connect(self.update_figure)
timer.start(1000)
def compute_initial_figure(self):
self.axes.plot([0, 1, 2, 3], [1, 2, 0, 4], 'b')
def update_figure(self):
l = [random.randint(0, 10) for i in range(4)]
self.axes.cla()
self.axes.plot([0, 1, 2, 3], l, 'b')
self.axes.set_xlim(self.xlim)
self.axes.set_ylim(self.ylim)
self.draw()
class P1(QtWidgets.QWidget):
def __init__(self, parent=None):
super(P1, self).__init__(parent)
layout = QGridLayout(self)
self.plot_canvas = MyDynamicMplCanvas(self, width=5, height=4, dpi=100)
self.navi_toolbar = NavigationToolbar(self.plot_canvas, self)
layout.addWidget(self.plot_canvas, 1, 1, 1, 1)
layout.addWidget(self.navi_toolbar, 2, 1, 1, 1)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.stack = QtWidgets.QStackedWidget(self)
P1f = P1(self)
self.stack.addWidget(P1f)
self.setCentralWidget(self.stack)
if __name__ == '__main__':
qApp = QtWidgets.QApplication(sys.argv)
aw = MainWindow()
aw.show()
sys.exit(qApp.exec_())
Большое спасибо, @Gabriel! Прошу прощения за задержку ответа ... Мне очень плохо, но, хотя это исправляет мой пример, я не могу заставить это работать с моей реальной программой (которая использует imshow с дополнительными точками разброса, нанесенными наверх). Я могу создать новую тему только для более детального подхода, но большое спасибо за ваше время и помощь; ваша техника была очень яркой в большем масштабе!