pyQt imshow is not updating - python

I am trying to update a matrix and show it using Qt with imshow, but it is not updating and I get a warning that the variable room is shadowing another room.
This is my code in Qt using imshow:(After defining the required libraries, the matrix "room" initializes as zero. Then a form designed in Qt Designer, with a widget called "mplt_widget" and a push button called "start_PB")
from PyQt5 import uic
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout
from PyQt5 import QtCore
import os
import sys
import numpy as np
from time import sleep
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigCanv
# Initializing existence of wave in the room (0)
room = [[0.0 for mm in range(0, 100)] for nn in range(0, 100)]
plt.ion()
Form = uic.loadUiType(os.path.join(os.getcwd(), "FTDT.ui"))[0]
class PlotWin(QMainWindow, Form):
def __init__(self):
QMainWindow.__init__(self)
Form.__init__(self)
self.setupUi(self)
self.fig = plt.figure(frameon=True)
self.ax = Axes3D(self.fig)
self.canvas = FigCanv(self.fig)
self.line1 = []
self.line1.append(self.ax.imshow(room))
l = QVBoxLayout(self.mplt_widget)
l.addWidget(self.canvas)
self.PlotThread = PlotThread(0)
self.start_PB.clicked.connect(self.start)
def start(self):
num = np.random.random()
self.PlotThread = PlotThread(num)
self.PlotThread.update_trigger.connect(self.update_plot)
self.PlotThread.start()
def update_plot(self, room):
self.line1.set_data(room)
self.canvas.draw()
class PlotThread(QtCore.QThread):
update_trigger = QtCore.pyqtSignal(np.ndarray, np.ndarray)
finished_trigger = QtCore.pyqtSignal()
def __init__(self, decay):
QtCore.QThread.__init__(self)
self.decay = decay
def run(self):
for Time in range(10):
for mm in range(0, 100):
for nn in range(0, 100):
room[mm][nn] += self.decay*(room[mm][nn]/2 + 1/2)
self.update_trigger.emit(room)
sleep(1)
self.finished_trigger.emit()
app = QApplication(sys.argv)
app.setStyle("Fusion")
w = PlotWin()
w.show()
sys.exit(app.exec_())

Related

How to display images using PyQt5 on a widget?

I am making Wafer contour map program by PyQt5. I have some problems.
import pandas as pd
test = pd.DataFrame(index=range(0,101), columns=range(0,101)) # empty dataframe
for i in range(0,101): #
for j in range(0,101):
if ((50-i)**2 + (50-j)**2) < 50**2:
test.loc[i,j] = i*2+i+j
else:
test.loc[i,j] = 0
import numpy as np
import matplotlib as mpl
import matplotlib.pylab as plt
test[test == 0] = np.nan
plt.figure(1)
plt.imshow(test, interpolation = 'hanning', vmin = 0, cmap = 'gist_rainbow_r')
plt.colorbar()
I got a below image
and I would like to display this image instead of graph on PyQt5 window
My window is below like
from PyQt5.uic import loadUiType
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt5agg import (
FigureCanvasQTAgg as FigureCanvas,
NavigationToolbar2QT as NavigationToolbar)
Ui_MainWindow, QMainWindow = loadUiType(r'C:\Users\wtjang\.spyder-py3\window.ui')
import numpy as np
from PyQt5 import QtWidgets
class Main(QMainWindow, Ui_MainWindow):
def __init__(self, ):
super(Main, self).__init__()
self.setupUi(self)
def addmpl(self, fig):
self.canvas = FigureCanvas(fig)
self.mplvl.addWidget(self.canvas)
self.canvas.draw()
if __name__ == '__main__':
import sys
from PyQt5 import QtWidgets
fig1 = Figure()
ax1f1 = fig1.add_subplot(111)
ax1f1.plot(np.random.rand(5))
app = QtWidgets.QApplication(sys.argv)
main = Main()
main.addmpl(fig1)
main.show()
sys.exit(app.exec_())
how to change graph to wafer image?
I tried to
if __name__ == '__main__':
import sys
from PyQt5 import QtWidgets
fig1 = plt.imshow(test, interpolation = 'hanning', vmin = 0, cmap = 'gist_rainbow_r')
app = QtWidgets.QApplication(sys.argv)
main = Main()
main.addmpl(fig1)
main.show()
sys.exit(app.exec_())
but I failed.
I got a answer. Thank your helping :)
ax1f1.imshow(test, interpolation = 'hanning', vmin = 0, cmap = 'gist_rainbow_r')

Python - how do you embed matplotlib animation in a Qwidget created in Qt designer?

Starting of with embedding matplotlib into a Qwidget I followed the following tutorial:
https://yapayzekalabs.blogspot.com/2018/11/pyqt5-gui-qt-designer-matplotlib.html
Files can be downloaded here:
https://drive.google.com/drive/folders/10V1Nh1xAVAfwydBtsJvoVGaYiR_HI15X?usp=sharing
The tutorial includes a mplwidget.py file:
from PyQt5.QtWidgets import *
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure
class MplWidget(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.canvas = FigureCanvas(Figure())
vertical_layout = QVBoxLayout()
vertical_layout.addWidget(self.canvas)
self.canvas.axes = self.canvas.figure.add_subplot(111)
self.setLayout(vertical_layout)
main.py:
from PyQt5.QtWidgets import*
from PyQt5.uic import loadUi
from matplotlib.backends.backend_qt5agg import (NavigationToolbar2QT as NavigationToolbar)
import numpy as np
import random
class MatplotlibWidget(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
loadUi("qt_designer.ui",self)
self.setWindowTitle("PyQt5 & Matplotlib Example GUI")
self.pushButton_generate_random_signal.clicked.connect(self.update_graph)
self.addToolBar(NavigationToolbar(self.MplWidget.canvas, self))
def update_graph(self):
fs = 500
f = random.randint(1, 100)
ts = 1/fs
length_of_signal = 100
t = np.linspace(0,1,length_of_signal)
cosinus_signal = np.cos(2*np.pi*f*t)
sinus_signal = np.sin(2*np.pi*f*t)
self.MplWidget.canvas.axes.clear()
self.MplWidget.canvas.axes.plot(t, cosinus_signal)
self.MplWidget.canvas.axes.plot(t, sinus_signal)
self.MplWidget.canvas.axes.legend(('cosinus', 'sinus'),loc='upper right')
self.MplWidget.canvas.axes.set_title('Cosinus - Sinus Signal')
self.MplWidget.canvas.draw()
app = QApplication([])
window = MatplotlibWidget()
window.show()
app.exec_()
and the qt_designer.ui file which has a button and a Qwidget (Promoted to MplWidget within QT Designer).
This works great for a static matplotlib graph, but can't seem to make an animation work. I think I don't understand what is seen as the figure in this case. What do I need to send to
FuncAnimation(fig, func, frames=None, init_func=None, fargs=None, save_count=None, *, cache_frame_data=True, **kwargs)[source]
to display the graph as an animation, of lets say 10 random graphs being displayed one after the other?
I have tried changing the code to something like this:
#In the init function
self.pushButton_generate_random_signal.clicked.connect(self.update_animation)
def update_animation(self):
self.ani = animation.FuncAnimation(self.MplWidget.canvas, self.update_graph, interval=1000)
self.MplWidget.canvas.draw()
def update_graph(self):
for k in range(1, 10):
fs = 500
f = random.randint(1, 100)
ts = 1/fs
length_of_signal = 100
t = np.linspace(0,1,length_of_signal)
cosinus_signal = np.cos(2*np.pi*f*t)
sinus_signal = np.sin(2*np.pi*f*t)
self.MplWidget.canvas.axes.clear()
self.MplWidget.canvas.axes.plot(t, cosinus_signal)
self.MplWidget.canvas.axes.plot(t, sinus_signal)
But that breaks python completely. Most examples research uses matplotlib.pyplot as plt with fig = plt.plot. So I think I don't understand how to set the MplWidget as a plot. Any help will be appreciated. Thanks
Thanks to eyllanesc in the comments I realised that ani should be self.ani and from there that I had to change self.MplWidget.canvas to just self.MplWidget. I also updated the structure and added the 'repeat = False' statement to stop the animation after 10 loops. The following main.py works:
from PyQt5.QtWidgets import *
from PyQt5.uic import loadUi
from matplotlib.backends.backend_qt5agg import (NavigationToolbar2QT as
NavigationToolbar)
import matplotlib.animation as animation
import matplotlib.pyplot as plt
import numpy as np
import random
class MatplotlibWidget(QMainWindow):
def __init__(self): # Create matplotlib interface
super(MatplotlibWidget, self).__init__()
loadUi("qt_designer.ui", self)
self.setWindowTitle("PyQt5 & Matplotlib Example GUI")
self.addToolBar(NavigationToolbar(self.MplWidget.canvas, self))
self.pushButton_generate_random_signal.clicked.connect(self.update_animation)
def update_animation(self):
self.ani = animation.FuncAnimation(self.MplWidget, self.update_axes,
self.update_graph, interval=1000, repeat=False)
self.MplWidget.canvas.draw()
def update_graph(self):
for k in range(1, 10):
fs = 500
f = random.randint(1, 100)
ts = 1 / fs
length_of_signal = 100
t = np.linspace(0, 1, length_of_signal)
cosinus_signal = np.cos(2 * np.pi * f * t)
sinus_signal = np.sin(2 * np.pi * f * t)
yield t, cosinus_signal, sinus_signal
def update_axes(self, update):
t, cosinus_signal, sinus_signal = update[0], update[1], update[2]
self.MplWidget.canvas.axes.clear()
self.MplWidget.canvas.axes.plot(t, cosinus_signal)
self.MplWidget.canvas.axes.plot(t, sinus_signal)
app = QApplication([])
window = MatplotlibWidget()
window.show()
app.exec_()

Add/Delete plots independently on a Matplotlib figure

I want to generate a scatterplot (up to half a million points) and on top of that, add different statistics (e.g. Q1, median, Q3). The idea is to add/delete those statistics without replotting the scatterplot in order to speed up the process. So far I can add plots independently on the figure but I can't delete a specific plot.
When I uncheck the checkbox, I get the following error:
AttributeError: 'Graphics' object has no attribute 'vline1'
I understand that when I create the plot, I need to store/return the plot in order to call it later when I want to delete it but I don't know how to do that.
Here my current code:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.pyplot import Figure
class Mainwindow(QMainWindow):
def __init__(self, parent=None):
super(Mainwindow, self).__init__(parent)
centralWidget = QWidget()
self.setCentralWidget(centralWidget)
self.fig = Figure()
self.axes = self.fig.add_subplot(111)
self.canvas = FigureCanvas(self.fig)
self.gridLayout = QGridLayout(centralWidget)
self.gridLayout.addWidget(self.canvas)
self.btn_plot = QCheckBox("Plot")
self.btn_line = QCheckBox("Line")
self.gridLayout.addWidget(self.btn_plot, 1,0,1,1)
self.gridLayout.addWidget(self.btn_line, 2,0,1,1)
self.btn_plot.clicked.connect(self.btnPlot)
self.btn_line.clicked.connect(self.btnLine)
def btnPlot(self):
self.checked = self.btn_plot.isChecked()
self.Graphics = Graphics('plot', self.checked, self.axes)
def btnLine(self):
self.checked = self.btn_line.isChecked()
self.Graphics = Graphics('line', self.checked, self.axes)
class Graphics:
def __init__(self, typeGraph, checked, axes):
self.typeGraph = typeGraph
self.checked = checked
self.axes = axes
if self.typeGraph == 'plot': self.drawPlot()
if self.typeGraph == 'line': self.drawLine()
def drawPlot(self):
if self.checked == True:
self.plot = self.axes.plot([10,20,30], [5,10,2], 'o')
else:
self.plot.remove()
self.axes.figure.canvas.draw()
def drawLine(self):
if self.checked == True:
self.vline1 = self.axes.axvline(x=15, linestyle="dashed", color="#595959")
self.vline2 = self.axes.axvline(x=25, linestyle="dashed", color="#595959")
else:
self.vline1.remove()
self.vline2.remove()
self.axes.figure.canvas.draw()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
prog = Mainwindow()
prog.show()
sys.exit(app.exec_())
Problem is be because when you click it then it creates always new Graphics (in btnPlot/btnLine) which doesn't have previous values - plot, vline1, vline2. You have to create Graphics only once and later run only drawPlot(checked), drawLine(checked) to add or remove item.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.pyplot import Figure
class Mainwindow(QMainWindow):
def __init__(self, parent=None):
super(Mainwindow, self).__init__(parent)
centralWidget = QWidget()
self.setCentralWidget(centralWidget)
self.fig = Figure()
self.axes = self.fig.add_subplot(111)
self.canvas = FigureCanvas(self.fig)
self.gridLayout = QGridLayout(centralWidget)
self.gridLayout.addWidget(self.canvas)
self.btn_plot = QCheckBox("Plot")
self.btn_line = QCheckBox("Line")
self.gridLayout.addWidget(self.btn_plot, 1,0,1,1)
self.gridLayout.addWidget(self.btn_line, 2,0,1,1)
self.btn_plot.clicked.connect(self.btnPlot)
self.btn_line.clicked.connect(self.btnLine)
# create only once
self.Graphics = Graphics(self.axes)
def btnPlot(self):
# add or remove
self.Graphics.drawPlot(self.btn_plot.isChecked())
def btnLine(self):
# add or remove
self.Graphics.drawLine(self.btn_line.isChecked())
class Graphics:
def __init__(self, axes):
self.axes = axes
# create at start with default values (but frankly, now I don't need it)
self.plot = None
self.vline1 = None
self.vline2 = None
def drawPlot(self, checked):
if checked:
self.plot = self.axes.plot([10,20,30], [5,10,2], 'o')
else:
for item in self.plot:
item.remove()
self.axes.figure.canvas.draw()
def drawLine(self, checked):
if checked:
self.vline1 = self.axes.axvline(x=15, linestyle="dashed", color="#595959")
self.vline2 = self.axes.axvline(x=25, linestyle="dashed", color="#595959")
else:
self.vline1.remove()
self.vline2.remove()
self.axes.figure.canvas.draw()
if __name__ == "__main__":
app = QtWidgets.QApplication([])
prog = Mainwindow()
prog.show()
sys.exit(app.exec())

How to draw a circle on a FigureCanvasQTAgg on mouse click

I am new to Qt and I am trying to do a program, that I've previously done in tkinter, to learn how to use it. I have embedded a FigureCanvasQtAgg in a Qt window. I have ploted thing on it. Now I want to plot on this canvas a circle on user's mouse click.
What I have done to do it on Tkinter is to use :
self.canvas.get_tk_widget().create_oval()
Is there a simple way to have the same results in PySide2?
Here is a simpler code with what I've tried :
from PySide2.QtWidgets import *
from PySide2.QtCore import *
from PySide2.QtGui import *
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib.pyplot as plt
import numpy as np
import sys
class MyPaintWidget(QWidget):
def __init__(self):
super().__init__()
layout_canvas = QVBoxLayout()
self.fig = plt.gcf()
plt.plot(np.cos([i for i in np.arange(0, 10, 0.1)]))
self.canvas = FigureCanvas(self.fig)
self.canvas.mpl_connect('button_press_event', self._on_left_click)
layout_canvas.addWidget(self.canvas)
self.setLayout(layout_canvas)
def _on_left_click(self, event):
print(event.xdata, event.ydata)
qp = QPainter()
qp.drawEllipse(QPointF(event.x, event.y), 10, 10)
qp.end()
self.canvas.draw()
if __name__=="__main__":
app = QApplication(sys.argv)
w = MyPaintWidget()
w.show()
app.exec_()
What I have done in tkinter (when I click on the canvas a green point does appear) :
import tkinter as tk
import tkinter.ttk as ttk
import numpy as np
import sys
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.backend_bases import key_press_handler
import matplotlib.pyplot as plt
class MainFrame(ttk.Frame):
def __init__(self, master):
ttk.Frame.__init__(self, master)
self.master = master
self.fig = plt.gcf()
plt.plot(np.cos([i for i in np.arange(0, 10, 0.1)]))
self.canvas = FigureCanvasTkAgg(self.fig, master=self.master) # A tk.DrawingArea.
self.canvas.get_tk_widget().grid(row=0, column=0)
self.canvas.draw()
self.canvas.mpl_connect('button_press_event', self._on_left_click)
def _on_left_click(self, event):
self._add_point(event.x, event.y)
def _add_point(self, x, y):
self.canvas.get_tk_widget().create_oval(x - 4, self.canvas.get_tk_widget().winfo_height() - (y - 4), x + 4,
self.canvas.get_tk_widget().winfo_height() - (y + 4), fill='green')
if __name__=="__main__":
window = tk.Tk()
main_frame = MainFrame(window)
window.mainloop()
Any ideas to get this result in QT ? Thanks !
Unlike tkinter Qt does not implement a function like create_oval() to make the circles so an alternative is to use the tools of matplotlib.
from PySide2 import QtCore, QtGui, QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib.pyplot as plt
import numpy as np
class MyPaintWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.figure = plt.gcf()
self.canvas = FigureCanvas(self.figure)
self.canvas.mpl_connect("button_press_event", self._on_left_click)
self.axes = self.figure.add_subplot(111)
x = np.arange(0, 10, 0.1)
y = np.cos(x)
self.axes.plot(x, y)
layout_canvas = QtWidgets.QVBoxLayout(self)
layout_canvas.addWidget(self.canvas)
def _on_left_click(self, event):
self.axes.scatter(event.xdata, event.ydata)
self.figure.canvas.draw()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MyPaintWidget()
w.show()
sys.exit(app.exec_())
Another possible solution is to implement the method create_oval() inheriting from the class FigureCanvas:
from PySide2 import QtCore, QtGui, QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import numpy as np
class PainterCanvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = Figure(figsize=(width, height), dpi=dpi)
FigureCanvas.__init__(self, fig)
self.setParent(parent)
self._instructions = []
self.axes = self.figure.add_subplot(111)
def paintEvent(self, event):
super().paintEvent(event)
painter = QtGui.QPainter(self)
painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
width, height = self.get_width_height()
for x, y, rx, ry, br_color in self._instructions:
x_pixel, y_pixel_m = self.axes.transData.transform((x, y))
# In matplotlib, 0,0 is the lower left corner,
# whereas it's usually the upper right
# for most image software, so we'll flip the y-coor
y_pixel = height - y_pixel_m
painter.setBrush(QtGui.QColor(br_color))
painter.drawEllipse( QtCore.QPoint(x_pixel, y_pixel), rx, ry)
def create_oval(self, x, y, radius_x=5, radius_y=5, brush_color="red"):
self._instructions.append([x, y, radius_x, radius_y, brush_color])
self.update()
class MyPaintWidget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.canvas = PainterCanvas()
self.canvas.mpl_connect("button_press_event", self._on_left_click)
x = np.arange(0, 10, 0.1)
y = np.cos(x)
self.canvas.axes.plot(x, y)
layout_canvas = QtWidgets.QVBoxLayout(self)
layout_canvas.addWidget(self.canvas)
def _on_left_click(self, event):
self.canvas.create_oval(event.xdata, event.ydata, brush_color="green")
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MyPaintWidget()
w.show()
sys.exit(app.exec_())

Embedding second animated graph to PyQt5 GUI

I want to add a second animated graph to the GUI holding my first animated graph with both graphs animating at the same time, but I'm not sure how.
Here is my code :
import sys
import numpy as np
from matplotlib.backends.qt_compat import QtWidgets
from matplotlib.backends.backend_qt5agg import (
FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure
from matplotlib import animation
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
layout = QtWidgets.QVBoxLayout(self._main)
self.fig = Figure(figsize=(5, 3))
self.canvas = FigureCanvas(self.fig)
layout.addWidget(self.canvas)
self.addToolBar(NavigationToolbar(self.canvas, self))
self.setup()
def setup(self):
self.ax = self.fig.subplots()
self.ax.set_aspect('equal')
self.ax.grid(True, linestyle = '-', color = '0.10')
self.ax.set_xlim([-15, 15])
self.ax.set_ylim([-15, 15])
self.scat = self.ax.scatter([], [], c=(0.9, 0.1, 0.5), zorder=3)
self.scat.set_alpha(0.8)
self.anim = animation.FuncAnimation(self.fig, self.update,
frames = 720, interval = 10)
def update(self, i):
self.scat.set_offsets(([np.cos(np.radians(i))*7.5, np.sin(np.radians(i))*7.5], [0,0]))
if __name__ == "__main__":
qapp = QtWidgets.QApplication(sys.argv)
app = ApplicationWindow()
app.show()
qapp.exec_()
And here is some sample code which has two graphs inside the same window (like how I want to)
Here they use an _update_canvas function for the animated graph and the other graph (which is just a static graph) they plot it in the application window class.
I'm using an update plot function to animate my graph, do I need a second update plot function? How?
Sample code:
import sys
import time
import numpy as np
from matplotlib.backends.qt_compat import QtCore, QtWidgets, is_pyqt5
if is_pyqt5():
from matplotlib.backends.backend_qt5agg import (
FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
else:
from matplotlib.backends.backend_qt4agg import (
FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
layout = QtWidgets.QVBoxLayout(self._main)
static_canvas = FigureCanvas(Figure(figsize=(5, 3)))
layout.addWidget(static_canvas)
self.addToolBar(NavigationToolbar(static_canvas, self))
dynamic_canvas = FigureCanvas(Figure(figsize=(5, 3)))
layout.addWidget(dynamic_canvas)
self.addToolBar(QtCore.Qt.BottomToolBarArea,
NavigationToolbar(dynamic_canvas, self))
self._static_ax = static_canvas.figure.subplots()
t = np.linspace(0, 10, 501)
self._static_ax.plot(t, np.tan(t), ".")
self._dynamic_ax = dynamic_canvas.figure.subplots()
self._timer = dynamic_canvas.new_timer(
100, [(self._update_canvas, (), {})])
self._timer.start()
def _update_canvas(self):
self._dynamic_ax.clear()
t = np.linspace(0, 10, 101)
# Shift the sinusoid as a function of time.
self._dynamic_ax.plot(t, np.sin(t + time.time()))
self._dynamic_ax.figure.canvas.draw()
if __name__ == "__main__":
qapp = QtWidgets.QApplication(sys.argv)
app = ApplicationWindow()
app.show()
qapp.exec_()
To embed multiple animated graphs, you need to create multiple plot objects(Figure and FigureCanvas) then add each object to the QVBoxLayout. If you wanted to display it horizontally, you can use a QHBoxLayout. Each plot object will have its own subplot, grid, and data. To update each plot's data, you will need each plot to have its individual plot update function where you can pass this to the animation.FuncAnimation handler. So in your case, to have two animated graphs, you will need two update plot functions.
from matplotlib.figure import Figure
from matplotlib import animation
import numpy as np
import sys, matplotlib
from PyQt5 import QtWidgets, QtCore
from matplotlib.backends.backend_qt5agg import (FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
layout = QtWidgets.QVBoxLayout(self._main)
# Configure figure 1
self.fig1 = Figure(figsize=(5, 3))
self.canvas1 = FigureCanvas(self.fig1)
# Configure figure 2
self.fig2 = Figure(figsize=(5, 3))
self.canvas2 = FigureCanvas(self.fig2)
layout.addWidget(self.canvas1)
layout.addWidget(self.canvas2)
self.addToolBar(NavigationToolbar(self.canvas1, self))
self.addToolBar(QtCore.Qt.BottomToolBarArea, NavigationToolbar(self.canvas2, self))
self.setup()
def setup(self):
# Plot 1 (top)
self.ax1 = self.fig1.subplots()
self.ax1.set_aspect('equal')
self.ax1.grid(True, linestyle = '-', color = '0.10')
self.ax1.set_xlim([-15, 15])
self.ax1.set_ylim([-15, 15])
# Plot 2 (bottom)
self.ax2 = self.fig2.subplots()
self.ax2.set_aspect('equal')
self.ax2.grid(True, linestyle = '-', color = '0.10')
self.ax2.set_xlim([-15, 15])
self.ax2.set_ylim([-15, 15])
self.scat1 = self.ax1.scatter([], [], c=(0.9, 0.1, 0.5), zorder=3)
self.scat1.set_alpha(0.8)
self.scat2 = self.ax2.scatter([], [], c=(0.9, 0.1, 0.5), zorder=3)
self.scat2.set_alpha(0.8)
self.anim1 = animation.FuncAnimation(self.fig1, self.update1,frames = 720, interval = 10)
self.anim2 = animation.FuncAnimation(self.fig2, self.update2,frames = 720, interval = 10)
# Update data for plot 1
def update1(self, i):
self.scat1.set_offsets(([np.cos(np.radians(i))*7.5, np.sin(np.radians(i))*7.5], [0,0]))
# Update data for plot 2
def update2(self, i):
self.scat2.set_offsets(([np.cos(np.radians(i))*7.5, np.sin(np.radians(i))*7.5], [0,0]))
if __name__ == "__main__":
qapp = QtWidgets.QApplication(sys.argv)
app = ApplicationWindow()
app.show()
qapp.exec_()
Is there a specific reason you need two separate canvas and two separate figures? If not, then I agree with ImportanceOfBeingErnest's comment and that you should create only one figure/canvas with 2 subplots, and call a single update function that takes care of updating the content of both axes.
In essence, your question would be a duplicate of this one, except for the fact that you are embedding the animation in a Qt app.
import sys
import time
import numpy as np
from matplotlib.backends.qt_compat import QtCore, QtWidgets, is_pyqt5
if is_pyqt5():
from matplotlib.backends.backend_qt5agg import (
FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
else:
from matplotlib.backends.backend_qt4agg import (
FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
layout = QtWidgets.QVBoxLayout(self._main)
self.fig = Figure(figsize=(5, 6))
self.canvas = FigureCanvas(self.fig)
layout.addWidget(self.canvas)
self.addToolBar(QtCore.Qt.BottomToolBarArea,
NavigationToolbar(self.canvas, self))
self._axs = self.fig.subplots(2, 1)
self._timer = self.canvas.new_timer(
100, [(self._update_canvas, (), {})])
self._timer.start()
def _update_canvas(self):
[ax.clear() for ax in self._axs]
t = np.linspace(0, 10, 501)
self._axs[0].plot(t, np.tan(t + time.time()), ".")
t = np.linspace(0, 10, 101)
self._axs[1].plot(t, np.sin(t + time.time()))
self.canvas.draw()
if __name__ == "__main__":
qapp = QtWidgets.QApplication(sys.argv)
app = ApplicationWindow()
app.show()
qapp.exec_()

Categories

Resources