This my first time using gui in python so sorry if my code is not pretty.
What I am doing is showing three signals of human body generated by the neurokit2 library (ecg, emg,rsp). I succeeded in showing the signal as it progress with time using matplotlib animation (it has a lot of flaws but I think I can fix them if I searched long enough). What I am truly struggling with for days now is that I need to show a spectrogram of the three signals (not animated though just a static graph).
But each time I try to show the spectrogram, it interferes with the plot of the animation producing garbage graphs and freezes the program.
So how can I totally separate these two functions from each other and produce different windows for each function?
import PySimpleGUI as sg
import matplotlib.pyplot as plt
import neurokit2 as nk
from matplotlib.animation import FuncAnimation
from matplotlib.backends._backend_tk import Toolbar, NavigationToolbar2Tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
class Toolbar(NavigationToolbar2Tk):
# only display the buttons we need
toolitems = [t for t in NavigationToolbar2Tk.toolitems if
t[0] in ('Home', 'Pan', 'Zoom')]
# t[0] in ('Home', 'Pan', 'Zoom','Save')]
def _init_(self, *args, **kwargs):
super(Toolbar, self)._init_(*args, **kwargs)
def draw_figure(canvas, figure, canvas_toolbar=None):
figure_canvas_agg = FigureCanvasTkAgg(figure, canvas)
figure_canvas_agg.draw_idle()
if canvas_toolbar is not None:
toolbar = Toolbar(figure_canvas_agg, canvas_toolbar)
toolbar.update()
figure_canvas_agg.get_tk_widget().pack(side='top', fill='both', expand=1)
return figure_canvas_agg
def main():
# define the form layout
layout = [[sg.Text('Signal Viewer', size=(40, 1), justification='center', font='Helvetica 20')],
[sg.Canvas(key='controls_cv', pad=((280, 0), 3))],
[sg.Canvas(size=(50, 36), key='-CANVAS-'), sg.Canvas(size=(50, 36), key='-CANVAS0-')],
[sg.Button('ECG', pad=((250, 0), 3)), sg.Button('EMG'), sg.Button('RSP'), sg.Button('Spec')],
[sg.Button('Exit', size=(5, 1), pad=((280, 0), 3), font='Helvetica 14')]]
# create the form and show it without the plot
window = sg.Window('Signal Viewer', layout, finalize=True)
canvas_elem = window['-CANVAS-']
canvas2_e = window['-CANVAS0-']
canvas = canvas_elem.TKCanvas
canvas2 = canvas2_e.TKCanvas
x, y = [], []
fig, ax = plt.subplots()
fig2, ax2 = plt.subplots()
plt.grid(True)
fig_agg = draw_figure(canvas, fig, window.FindElement('controls_cv').TKCanvas)
fig_agg2 = draw_figure(canvas2, fig2)
def wave2(ecg100):
plt.specgram(ecg100, NFFT=64, Fs=256, noverlap=32)
fig_agg2.draw_idle()
def wave_form(ecg100):
def update_time():
t = 0
t_max = len(ecg100)
while t < t_max and t >= 0:
t = ani.direction
yield t
def animate(frame):
st, end = frame / 2, frame 0.5
x.append(ecg100[frame])
plt.cla()
plt.ylim([ecg100.min(), ecg100.max()])
plt.plot(x)
plt.grid()
plt.tight_layout()
ax.set_xlim(st, end)
def on_press(event1):
print(event1.key)
if event1.key.isspace():
if ani.running:
ani.event_source.stop()
else:
ani.event_source.start()
ani.running ^= True
elif event1.key == 'left':
ani.direction = -1
elif event1.key == 'right':
ani.direction = 1
if event1.key in ['left', 'right']:
t = ani.frame_seq.__next__()
animate(t)
fig.canvas.mpl_connect('key_press_event', on_press)
ani = FuncAnimation(plt.gcf(), animate, frames=update_time(), interval=5, repeat=True)
ani.running = True
ani.direction = 1
fig_agg.draw_idle()
while True:
event, values = window.read(timeout=10)
if event in ('Exit', sg.WIN_CLOSED):
exit(69)
elif event in 'ECG':
ecg100 = nk.ecg_simulate(duration=10, noise=0.01, heart_rate=100)
wave_form(ecg100)
wave2(ecg100)
elif event in 'EMG':
ecg100 = nk.emg_simulate(duration=10, sampling_rate=100, burst_number=4)
wave_form(ecg100)
wave2(ecg100)
elif event in 'RSP':
ecg100 = nk.rsp_simulate(duration=30, sampling_rate=50, noise=0.01)
wave_form(ecg100)
wave2(ecg100)
window.close()
if __name__ == '__main__':
main()
The funtion wave2 is the function that I want to implement for the signal to produce spectrogram in different plot when I press the 'spec' button
CodePudding user response:
User code cannot run on my platform, so just example here.
Of course, you can use sg.Canvas
here.
import math
from matplotlib import use as use_agg
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
import PySimpleGUI as sg
def pack_figure(graph, figure):
canvas = FigureCanvasTkAgg(figure, graph.Widget)
plot_widget = canvas.get_tk_widget()
plot_widget.pack(side='top', fill='both', expand=1)
return plot_widget
def plot_figure(index, theta):
fig = plt.figure(index) # Active an existing figure
ax = plt.gca() # Get the current axes
x = [degree for degree in range(1080)]
y = [math.sin((degree theta)/180*math.pi) for degree in range(1080)]
ax.cla() # Clear the current axes
ax.set_title(f"Sensor Data {index}")
ax.set_xlabel("X axis")
ax.set_ylabel("Y axis")
ax.set_xscale('log')
ax.grid()
plt.plot(x, y) # Plot y versus x as lines and/or markers
fig.canvas.draw() # Rendor figure into canvas
# Use Tkinter Agg
use_agg('TkAgg')
layout = [[sg.Graph((640, 480), (0, 0), (640, 480), key='Graph1'), sg.Graph((640, 480), (0, 0), (640, 480), key='Graph2')]]
window = sg.Window('Matplotlib', layout, finalize=True)
# Initial
graph1 = window['Graph1']
graph2 = window['Graph2']
plt.ioff() # Turn the interactive mode off
fig1 = plt.figure(1) # Create a new figure
ax1 = plt.subplot(111) # Add a subplot to the current figure.
fig2 = plt.figure(2) # Create a new figure
ax2 = plt.subplot(111) # Add a subplot to the current figure.
pack_figure(graph1, fig1) # Pack figure under graph
pack_figure(graph2, fig2)
theta1 = 0 # theta for fig1
theta2 = 90 # theta for fig2
plot_figure(1, theta1)
plot_figure(2, theta2)
while True:
event, values = window.read(timeout=10)
if event == sg.WINDOW_CLOSED:
break
elif event == sg.TIMEOUT_EVENT:
theta1 = (theta1 40) % 360
plot_figure(1, theta1)
theta2 = (theta2 40) % 260
plot_figure(2, theta2)
window.close()