Как добавить ползунок к анимации FuncAnimation?
Мне нужен способ, который позволяет с помощью слайдера/ползунка QSlider
управлять анимацией.
Анимация идёт непрерывно и за текущим кадром движется и ползунок. В любой момент я должен иметь возможность перемотать анимацию с помощью слайдера на нужный мне кадр. Код, в который требуется внедрить слайдер.
import matplotlib
import numpy as np
from PyQt5 import QtCore, QtWidgets
# Make sure that we are using QT5
matplotlib.use('Qt5Agg')
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.animation as animation
class MyMplCanvas(FigureCanvas):
def __init__(self, slider, parent=None, width=5, height=4, dpi=100):
self.slider = slider # Save reference to the slider
self.slider_is_manual = False # Flag to track if slider change is manual
fig = Figure(figsize=(width, height), dpi=dpi)
ax = fig.add_subplot(111) # The big subplot
self.ax = fig.add_subplot(211)
self.bx = fig.add_subplot(212)
FigureCanvas.__init__(self, fig)
self.setParent(parent)
FigureCanvas.setSizePolicy(self,
QtWidgets.QSizePolicy.Expanding,
QtWidgets.QSizePolicy.Expanding)
FigureCanvas.updateGeometry(self)
ax.set_title('TMP102 Temperature over Time')
ax.spines['top'].set_color('none')
ax.spines['bottom'].set_color('none')
ax.spines['left'].set_color('none')
ax.spines['right'].set_color('none')
ax.tick_params(labelcolor='w', top=False, bottom=False, left=False, right=False)
ax.set_xlabel('Samples')
ax.set_ylabel('Temperature (deg C)')
self.x_len = 200 # Number of points to display
self.y_range = [10, 40] # Range of possible Y values to display
self.xs = list(range(0, 200))
self.ys = [0 for _ in range(self.x_len)]
frames = np.arange(0, 200)
self.anim = animation.FuncAnimation(fig, self.animate, frames=frames, init_func=self.init, interval=50, blit=True, repeat=False)
# Connect the slider to the update_frame method
self.slider.valueChanged.connect(self.update_frame)
def init(self):
y_range = [10, 40]
self.ax.set_ylim(*y_range)
self.bx.set_ylim(*y_range)
self.line, = self.ax.plot(self.xs, self.ys)
self.line2, = self.bx.plot(self.xs, self.ys)
return self.line, self.line2
def animate(self, i):
temp_c = random.randint(15, 35)
self.ys.append(temp_c)
self.ys = self.ys[-self.x_len:]
self.line.set_ydata(self.ys)
self.line2.set_ydata(self.ys)
if not self.slider_is_manual:
self.slider.blockSignals(True)
self.slider.setValue(i) # Update slider value
self.slider.blockSignals(False)
return self.line, self.line2
def update_frame(self, i):
if self.slider_is_manual:
temp_c = random.randint(15, 35)
self.ys.append(temp_c)
self.ys = self.ys[-self.x_len:]
self.line.set_ydata(self.ys)
self.line2.set_ydata(self.ys)
self.draw()
def start_manual_update(self):
self.slider_is_manual = True
def end_manual_update(self):
self.slider_is_manual = False
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(ApplicationWindow, self).__init__(parent)
main_widget = QtWidgets.QWidget()
l = QtWidgets.QVBoxLayout(main_widget)
self.slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
self.slider.setRange(0, 199)
l.addWidget(self.slider)
sc = MyMplCanvas(self.slider, main_widget, width=5, height=4, dpi=100)
l.addWidget(sc)
self.setCentralWidget(main_widget)
# Connect signals to handle manual slider updates
self.slider.sliderPressed.connect(sc.start_manual_update)
self.slider.sliderReleased.connect(sc.end_manual_update)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = ApplicationWindow()
w.show()
sys.exit(app.exec_())```