I'm using matplotlib with pyqt5 to draw data into 3 axes, and than user can make selection in one plot that will be shown in other two plots too. Since I'm working with big data (up to 10 millions of points), drawing selection could be slow, especially when I need to draw to scatterplot.
I am trying to use matplotlib blit function, but have some issues with result. Here is minimum simple example.
import matplotlib
matplotlib.use('Qt5Agg')
import numpy as np
import sys
from matplotlib.backends.qt_compat import QtCore, QtWidgets
from matplotlib.backends.backend_qt5agg 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.static_canvas = FigureCanvas(Figure(figsize=(10, 10)))
layout.addWidget(self.static_canvas)
layout.addWidget(NavigationToolbar(self.static_canvas, self))
axes = self.static_canvas.figure.subplots(2, 1)
self.ax1 = axes[0]
self.ax2 = axes[1]
self.ax1.cla()
self.ax2.cla()
button = QtWidgets.QPushButton('Click me!')
button.clicked.connect(self.update_canvas_blit)
layout.addWidget(button)
# Fixing random state for reproducibility
np.random.seed(19680801)
# Create random data
N = 50000
x = np.random.rand(N)
y = np.random.rand(N)
self.ax1.scatter(x, y)
self.points = self.ax1.scatter([],[], s=5, color='red')
x = np.linspace(0, 1000, 100000)
self.ax2.plot(x, np.sin(x))
self.lines, = self.ax2.plot([],[], color='red')
self.static_canvas.draw()
self.background1 = self.static_canvas.copy_from_bbox(self.ax1.bbox)
self.background2 = self.static_canvas.copy_from_bbox(self.ax2.bbox)
def update_canvas_blit(self):
N = 50
x = np.random.rand(N)
y = np.random.rand(N)
self.static_canvas.restore_region(self.background1)
self.points.set_offsets(np.c_[x,y])
self.ax1.draw_artist(self.points)
self.ax1.figure.canvas.blit(self.ax1.bbox)
self.static_canvas.restore_region(self.background2)
x = np.linspace(0, np.random.randint(500,1000), 1000)
self.lines.set_data(x, np.sin(x))
self.ax2.draw_artist(self.lines)
self.ax2.figure.canvas.blit(self.ax2.bbox)
if __name__ == "__main__":
qapp = QtWidgets.QApplication(sys.argv)
app = ApplicationWindow()
app.show()
qapp.exec_()
When clicking button, expected output should be still same background with random points/lines redrawing. In a way it is happening but there are some strange artifacts that looks like somehow axes are drawn to each other. But when I try to save it to .png, it will restore to good state.
The problem is that the snapshot of the background is taken at a moment in time where the figure has not yet been shown on screen. At that point the figure is 10 by 10 inches large. Later, it is shown inside the QMainWindow and resized to fit into the widget.
Only once that has happened, it makes sense to take the background snapshot.
One option is to use a timer of 1 second and only then copy the background. This would look as follows.
import numpy as np
import sys
from matplotlib.backends.qt_compat import QtCore, QtWidgets
from matplotlib.backends.backend_qt5agg 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.static_canvas = FigureCanvas(Figure(figsize=(10, 10)))
layout.addWidget(self.static_canvas)
layout.addWidget(NavigationToolbar(self.static_canvas, self))
axes = self.static_canvas.figure.subplots(2, 1)
self.ax1 = axes[0]
self.ax2 = axes[1]
self.ax1.cla()
self.ax2.cla()
button = QtWidgets.QPushButton('Click me!')
button.clicked.connect(self.update_canvas_blit)
layout.addWidget(button)
# Fixing random state for reproducibility
np.random.seed(19680801)
# Create random data
N = 50000
x = np.random.rand(N)
y = np.random.rand(N)
self.ax1.scatter(x, y)
self.points = self.ax1.scatter([],[], s=5, color='red')
x = np.linspace(0, 1000, 100000)
self.ax2.plot(x, np.sin(x))
self.lines, = self.ax2.plot([],[], color='red')
self.static_canvas.draw()
self._later()
def _later(self, evt=None):
self.timer = self.static_canvas.new_timer(interval=1000)
self.timer.single_shot = True
self.timer.add_callback(self.update_background)
self.timer.start()
def update_background(self, evt=None):
self.background1 = self.static_canvas.copy_from_bbox(self.ax1.bbox)
self.background2 = self.static_canvas.copy_from_bbox(self.ax2.bbox)
def update_canvas_blit(self):
N = 50
x = np.random.rand(N)
y = np.random.rand(N)
self.static_canvas.restore_region(self.background1)
self.points.set_offsets(np.c_[x,y])
self.ax1.draw_artist(self.points)
self.ax1.figure.canvas.blit(self.ax1.bbox)
self.static_canvas.restore_region(self.background2)
x = np.linspace(0, np.random.randint(500,1000), 1000)
self.lines.set_data(x, np.sin(x))
self.ax2.draw_artist(self.lines)
self.ax2.figure.canvas.blit(self.ax2.bbox)
if __name__ == "__main__":
qapp = QtWidgets.QApplication(sys.argv)
app = ApplicationWindow()
app.show()
qapp.exec_()
Related
I am trying to remove an image from a figure and release the memory. when colorbar is not added for the image, memory can be released successfully, however, if colorbar is added, it fails. In the demo-code bellow:
click push button Add ColorBar will add a color bar for one image in the figure.
click push button remove will remove one image(and the related colorbar) from the figure.
each time i remove the image, the colorbar related is also removed, so i don't know why the memory recycle fails, I guess there must be some extra reference to the image when add a colorbar to it, which fails the memory recycle.
import numpy as np
from PyQt5 import QtWidgets
from memory_profiler import profile
import matplotlib
from matplotlib.figure import Figure
import matplotlib.cm as cm
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.axes._axes import Axes
matplotlib.use("Qt5Agg")
class MplCanvas(FigureCanvasQTAgg):
def __init__(self, parent=None, width=5, height=4, dpi=100):
self.fig = Figure(figsize=(width, height), dpi=dpi)
self.axe = self.fig.add_subplot(1, 1, 1, label='good')
super().__init__(self.fig)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
layout = QtWidgets.QVBoxLayout()
self.canvas = MplCanvas(self, width=5, height=4, dpi=100)
self.axe = self.canvas.axe
layout.addWidget(self.canvas)
self.pushButton_addColorBar = QtWidgets.QPushButton('Add ColorBar')
layout.addWidget(self.pushButton_addColorBar)
self.pushButton_remove = QtWidgets.QPushButton('remove')
layout.addWidget(self.pushButton_remove)
widget = QtWidgets.QWidget()
widget.setLayout(layout)
self.setCentralWidget(widget)
self.pushButton_remove.clicked.connect(self.removeImage)
self.pushButton_addColorBar.clicked.connect(self.createColorBar)
self.pcolormesh_test()
def pcolormesh_test(self):
"""add two images"""
delta = 0.01
x = y = np.arange(-3.0, 3.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-X ** 2 - Y ** 2)
Z2 = np.exp(-(X - 1) ** 2 - (Y - 1) ** 2)
Z = (Z1 - Z2) * 2
im = self.axe.pcolormesh(X, Y, Z, cmap=cm.viridis, shading='auto')
im.set_clim(vmax=np.amax(Z), vmin=np.amin(Z))
Zx = (Z1 + Z2) * 2
imx = self.axe.pcolormesh(X, Y, Zx, cmap=cm.Blues, shading='auto')
imx.set_clim(vmax=np.amax(Zx), vmin=np.amin(Zx))
def createColorBar(self):
""" to create a color bar for an image. """
axe = self.axe
fig = axe.get_figure()
images = self.getImages(axe)
for image in images:
if not image.colorbar: # color bar doesn't exist
inset_axe = axe.inset_axes([1.0, 0, 0.05, 1], transform=axe.transAxes)
fig.colorbar(image, ax=axe, cax=inset_axe)
break # each trigger create one colorbar for one image
self.reDraw()
#profile
def removeImage(self, checked):
"""
Usage:
* each trigger remove one image
"""
images = self.getImages(self.axe)
# print(f'images={images}')
if images:
image = images[-1]
color_bar = image.colorbar
if color_bar:
color_bar.remove()
del color_bar
# remove image
image.remove()
del image
self.reDraw()
def getImages(self, axe: Axes):
"""to obtain the image list in the axe"""
images = []
images.extend(axe.images)
images.extend(axe.collections)
return images
def reDraw(self):
self.canvas.draw_idle()
self.canvas.flush_events()
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
I have found the solution, and post an answer to help.
we need to add gc.collect() at the end of removeImage() method. then the memory can be reclaimed when the image is removed.
I want to embed Matplotlib plot in my PyQt app using QWidget. This is the code of the widget script.
from PyQt5.QtWidgets import*
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure
from entropia import entropy
import matplotlib.pyplot as plt
import numpy as np
import random
class MplWidget(QWidget):
def __init__(self, parent = None):
QWidget.__init__(self,parent)
self.canvas = FigureCanvas(Figure())
self.vertical_layout = QVBoxLayout()
self.vertical_layout.addWidget(self.canvas)
self.setLayout(self.vertical_layout)
def draw(self):
QWidget.update(self)
self.canvas.axes = self.canvas.figure.add_subplot(111)
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.canvas.axes.clear()
self.canvas.axes.plot(t, cosinus_signal)
self.canvas.axes.plot(t, sinus_signal)
self.canvas.axes.legend(('cosinus', 'sinus'),loc='upper right')
self.canvas.axes.set_title('Cosinus - Sinus Signal')
self.canvas.draw()
I want the plot to be displayed after the pushbutton in another script is clicked. Unfortunately, this is not working. Button is connected to the function, though. If I do something like print(fs) in the "draw" method I see the variable in the python terminal when the button gets clicked.
This is how it looks when the button gets clicked:
When I move the whole thing to the init method the plot is displayed.
class MplWidget(QWidget):
def __init__(self, parent = None):
QWidget.__init__(self,parent)
self.canvas = FigureCanvas(Figure())
self.vertical_layout = QVBoxLayout()
self.vertical_layout.addWidget(self.canvas)
self.canvas.axes = self.canvas.figure.add_subplot(111)
self.setLayout(self.vertical_layout)
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.canvas.axes.clear()
self.canvas.axes.plot(t, cosinus_signal)
self.canvas.axes.plot(t, sinus_signal)
self.canvas.axes.legend(('cosinus', 'sinus'),loc='upper right')
self.canvas.axes.set_title('Cosinus - Sinus Signal')
self.canvas.draw()
So, what can I do to display the plot only after calling it from another method?
Below is python code to demonstrate the problem.
If there are 2 rows and 2 columns of images, for example, typing/erasing in the textbox is reasonably fast. However, if there are 5 rows and 5 columns, typing/erasing in the textbox is quite slow. If the xticks and yticks are drawn, interaction is even slower. So, it seems as if the entire figure is redrawn after every keystroke.
Is there a solution for this (apart from putting the textbox on a separate figure)?
(My development platform is MacOS Mojave, Python 3.7.5.)
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
from matplotlib.widgets import TextBox
class Textbox_Demo(object):
def __init__(self):
self.fig = plt.figure(figsize=(8,8))
self.string = 'label'
self.rows = 5 # reducing rows speeds up textbox interaction
self.cols = 5 # reducing cols speeds up textbox interaction
self.plot_count = self.rows * self.cols
self.gs = gridspec.GridSpec(self.rows, self.cols,
left=0.05, right=1-0.02, top=1-.02, bottom=0.10, wspace=0.3, hspace=0.4)
for k in range(self.plot_count):
ax = self.fig.add_subplot(self.gs[k])
#ax.set_xticks([]) # showing axes slows textbox interaction
#ax.set_yticks([]) # showing axes slows textbox interaction
data = np.atleast_2d(np.sin(np.linspace(1,255,255) * 50))
ax.imshow(data, aspect="auto", cmap='ocean')
# this is the user-input textbox
tb_axis = plt.axes([0.125, 0.02, 0.8, 0.05])
self.tb = TextBox(tb_axis, 'Enter label:', initial=self.string, label_pad=0.01)
self.tb.on_submit(self.on_submit)
plt.show()
def on_submit(self, text):
pass
if __name__ == "__main__":
Textbox_Demo()
Matplotlib's TextBox is inherently slow, because it uses the drawing tools provided by matplotlib itself and hence redraws the complete figure upon changes.
I would propose to use a text box of a GUI kit instead. For example for PyQt this might look like:
import numpy as np
import sys
from matplotlib.backends.backend_qt5agg import (
FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.backends.qt_compat import QtCore, QtWidgets
import matplotlib.gridspec as gridspec
from matplotlib.figure import Figure
class Textbox_Demo(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
layout = QtWidgets.QVBoxLayout(self._main)
layout.setContentsMargins(0,0,0,0)
layout.setSpacing(0)
self.fig = Figure(figsize=(8,8))
self.canvas = FigureCanvas(self.fig)
layout.addWidget(self.canvas)
self.addToolBar(NavigationToolbar(self.canvas, self))
self._textwidget = QtWidgets.QWidget()
textlayout = QtWidgets.QHBoxLayout(self._textwidget)
self.textbox = QtWidgets.QLineEdit(self)
self.textbox.editingFinished.connect(self.on_submit)
# or, if wanting to have changed apply directly:
# self.textbox.textEdited.connect(self.on_submit)
textlayout.addWidget(QtWidgets.QLabel("Enter Text: "))
textlayout.addWidget(self.textbox)
layout.addWidget(self._textwidget)
self.fill_figure()
def fill_figure(self):
self.string = 'label'
self.rows = 5 # reducing rows speeds up textbox interaction
self.cols = 5 # reducing cols speeds up textbox interaction
self.plot_count = self.rows * self.cols
self.gs = gridspec.GridSpec(self.rows, self.cols,
left=0.05, right=1-0.02, top=1-.02, bottom=0.10, wspace=0.3, hspace=0.4)
for k in range(self.plot_count):
ax = self.fig.add_subplot(self.gs[k])
#ax.set_xticks([]) # showing axes slows textbox interaction
#ax.set_yticks([]) # showing axes slows textbox interaction
data = np.atleast_2d(np.sin(np.linspace(1,255,255) * 50))
ax.imshow(data, aspect="auto", cmap='ocean')
def on_submit(self):
text = self.textbox.text()
print(text)
pass
if __name__ == "__main__":
qapp = QtWidgets.QApplication(sys.argv)
app = Textbox_Demo()
app.show()
qapp.exec_()
I want to display sensor data on a PyQT GUI with a matplotlib animation.
I already have a working Plot which gets updates every time I receive new sensor value from an external source with this code:
def __init__(self):
self.fig = Figure(figsize=(width, height), dpi=dpi)
self.axes = self.fig.add_subplot(111)
self.axes.grid()
self.xdata = []
self.ydata = []
self.entry_limit = 50
self.line, = self.axes.plot([0], [0], 'r')
def update_figure_with_new_value(self, xval: float, yval: float):
self.xdata.append(xval)
self.ydata.append(yval)
if len(self.xdata) > self.entry_limit:
self.xdata.pop(0)
self.ydata.pop(0)
self.line.set_data(self.xdata, self.ydata)
self.axes.relim()
self.axes.autoscale_view()
self.fig.canvas.draw()
self.fig.canvas.flush_events()
I want now to extend the plot to show another data series with the same x-axis. I tried to achieve this with the following additions to the init-code above:
self.axes2 = self.axes.twinx()
self.y2data = []
self.line2, = self.axes2.plot([0], [0], 'b')
and in the update_figure_with_new_value() function (for test purpose I just tried to add 1 to yval, I will extend the params of the function later):
self.y2data.append(yval+1)
if len(self.y2data) > self.entry_limit:
self.y2data.pop(0)
self.line2.set_data(self.xdata, self.ydata)
self.axes2.relim()
self.axes2.autoscale_view()
But instead of getting two lines in the plot which should have the exact same movement but just shifted by one I get vertical lines for the second plot axis (blue). The first axis (red) remains unchanged and is ok.
How can I use matplotlib to update multiple axis so that they display the right values?
I'm using python 3.4.0 with matplotlib 2.0.0.
Since there is no minimal example available, it's hard to tell the reason for this undesired behaviour. In principle ax.relim() and ax.autoscale_view() should do what you need.
So here is a complete example which works fine and updates both scales when being run with python 2.7, matplotlib 2.0 and PyQt4:
import numpy as np
import matplotlib.pyplot as plt
from PyQt4 import QtGui, QtCore
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
class Window(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.widget = QtGui.QWidget()
self.setCentralWidget(self.widget)
self.widget.setLayout(QtGui.QVBoxLayout())
self.widget.layout().setContentsMargins(0,0,0,0)
self.widget.layout().setSpacing(0)
self.fig = Figure(figsize=(5,4), dpi=100)
self.axes = self.fig.add_subplot(111)
self.axes.grid()
self.xdata = [0]
self.ydata = [0]
self.entry_limit = 50
self.line, = self.axes.plot([], [], 'r', lw=3)
self.axes2 = self.axes.twinx()
self.y2data = [0]
self.line2, = self.axes2.plot([], [], 'b')
self.canvas = FigureCanvas(self.fig)
self.canvas.draw()
self.nav = NavigationToolbar(self.canvas, self.widget)
self.widget.layout().addWidget(self.nav)
self.widget.layout().addWidget(self.canvas)
self.show()
self.ctimer = QtCore.QTimer()
self.ctimer.timeout.connect(self.update)
self.ctimer.start(150)
def update(self):
y = np.random.rand(1)
self.update_figure_with_new_value(self.xdata[-1]+1,y)
def update_figure_with_new_value(self, xval,yval):
self.xdata.append(xval)
self.ydata.append(yval)
if len(self.xdata) > self.entry_limit:
self.xdata.pop(0)
self.ydata.pop(0)
self.y2data.pop(0)
self.line.set_data(self.xdata, self.ydata)
self.axes.relim()
self.axes.autoscale_view()
self.y2data.append(yval+np.random.rand(1)*0.17)
self.line2.set_data(self.xdata, self.y2data)
self.axes2.relim()
self.axes2.autoscale_view()
self.fig.canvas.draw()
self.fig.canvas.flush_events()
if __name__ == "__main__":
qapp = QtGui.QApplication([])
a = Window()
exit(qapp.exec_())
You may want to test this and report back if it is working or not.
I have to display some data, z = f(x,y,t), in a 3d bar-chart. I'd like to have this chart changing/animated with time.
For now, I can display my data z = f(x,y,t) for any given time t but I can't find a way to redraw the chart automatically to display the data of the next time step. Is there a way for doing this?
I tried with a simple loop but apparently I can only see the data for the last time step.
Here is the current version of my code:
from mpl_toolkits.mplot3d import Axes3D
from math import cos, sin
import matplotlib.pyplot as plt
import numpy as np
# An arbitraty function for z = f(x,t)
def z_xt(x, t):
return 30*sin(5*t) + x**2 + 20
# An arbitraty function for z = f(y,t)
def z_yt(y, t):
return 20*cos(2*t) + y**3/10 + 20
# Superposition z(x,y,t) = z(x,t) + z(y,t)
def z_xyt(f_xt, f_yt):
return f_xt+f_yt
# Definition of space and time domains
nx = 9; dx = 1
ny = 6; dy = 1
nt = 10; dt = 1
y, x = np.mgrid[slice(0, ny, dy),slice(0, nx, dx)]
t_list = [round(t*dt,2) for t in range(nt)]
# The matrix that contains the solution for every time step
Z_xyt = [z_xyt(z_xt(x, t), z_yt(y, t)) for t in t_list]
# Plot data in a 3D bar chart
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x_grid, y_grid = np.meshgrid(np.arange(nx), np.arange(ny))
x_grid = x_grid.flatten()
y_grid = y_grid.flatten()
# Iterate time and plot coresponding data
for index, t in enumerate(t_list):
# Z_xyt data at time t_list[index]
z_data = Z_xyt[index].flatten()
# Draw/actualize 3D bar chart data for every time step
bar_chart = ax.bar3d(x_grid, y_grid, np.zeros(len(z_data)), 1, 1, z_data)
plt.draw()
plt.show()
Thanks in advance!
I finally found a way for doing it by embedding the chart in a PyQt window (following the instructions given in this post). The code proposed below generate a PyQt window in which the 3D bar-chart is displayed. The chart can be animated using a slider (time variations).
import sys
from PyQt4 import QtGui, QtCore
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar
from mpl_toolkits.mplot3d import Axes3D
from math import cos, sin
import matplotlib.pyplot as plt
import numpy as np
# An arbitraty function for z = f(x,t)
def z_xt(x, t):
return 30*sin(5*t) + x**2 + 20
# An arbitraty function for z = f(y,t)
def z_yt(y, t):
return 20*cos(2*t) + y**3/10 + 20
# Superposition z(x,y,t) = z(x,t) + z(y,t)
def z_xyt(f_xt, f_yt):
return f_xt+f_yt
# Definition of space and time domains
nx = 9; dx = 1
ny = 6; dy = 1
nt = 50; dt = 0.05
y, x = np.mgrid[slice(0, ny, dy),slice(0, nx, dx)]
t_list = [round(t*dt,2) for t in range(nt)]
# The matrix that contains the solution for every time step
Z_xyt = [z_xyt(z_xt(x, t), z_yt(y, t)) for t in t_list]
# Plot data in a 3D bar chart
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x_grid, y_grid = np.meshgrid(np.arange(nx), np.arange(ny))
x_grid = x_grid.flatten()
y_grid = y_grid.flatten()
class Window(QtGui.QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
# a figure instance to plot on
self.figure = plt.figure()
# this is the Canvas Widget that displays the `figure`
# it takes the `figure` instance as a parameter to __init__
self.canvas = FigureCanvas(self.figure)
# this is the Navigation widget
# it takes the Canvas widget and a parent
self.toolbar = NavigationToolbar(self.canvas, self)
# A slider to make time variations
self.horizontalSlider = QtGui.QSlider(self)
self.horizontalSlider.setOrientation(QtCore.Qt.Horizontal)
self.horizontalSlider.valueChanged.connect(self.plot)
self.horizontalSlider.setMinimum(0)
self.horizontalSlider.setMaximum(t_list.__len__()-1)
# set the layout
layout = QtGui.QVBoxLayout()
layout.addWidget(self.toolbar)
layout.addWidget(self.canvas)
layout.addWidget(self.horizontalSlider)
self.setLayout(layout)
# Generate the chart for t=0 when the window is openned
self.plot()
def plot(self):
# Read the slider value -> t = t_list[t_index]
t_index = self.horizontalSlider.value()
# Get the z-data for the given time index
z_data = Z_xyt[t_index].flatten()
# Discards the old chart and display the new one
ax = self.figure.add_subplot(111,projection='3d')
ax.hold(False)
ax.bar3d(x_grid, y_grid, np.zeros(len(z_data)), 1, 1, z_data)
# refresh canvas
self.canvas.draw()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
main = Window()
main.show()
sys.exit(app.exec_())
Graphically, the window looks like this: