I'm creating a GUI to allow users to see a "live view" of a spectrometer where data is taken from the spectrometer and plotted in Matplotlib to be displayed in the GUI window. The GUI also has a few other buttons which allow the user to go through other functions (irrelevant but just background).
I've gotten the live view to work in matplotlib using a while loop and clearing the data to re-plot:
while True:
data = ccs.take_data(num_avg=3) # spectrometer function
norm = (data[0]-dark[0])/(light[0]-dark[0]) # some calcs.
plt.plot(data[1],norm)
plt.axis([400,740,0,1.1])
plt.grid(color='w', linestyle='--')
plt.xlabel('Wavelength [nm]')
plt.ylabel('Normalized Intesity')
plt.pause(0.1)
plt.cla()
Next step was to show this figure in PySimpleGUI. Harder than expexted... I was able to use a few demo codes from PySimpleGUI to get a single figure to appear and update if user presses 'update' button:
from instrumental.drivers.spectrometers import thorlabs_ccs
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import PySimpleGUI as sg
import matplotlib, time, threading
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
def fig_maker(ccs, dark, sub):
plt.clf()
plt.close()
data = ccs.take_data(num_avg=3)
norm = (data[0]-dark[0])/(sub[0]-dark[0])
plt.plot(data[1],norm,c='r')
plt.axis([400,750,0,1.1])
plt.grid(color='w', linestyle='--')
plt.xlabel('Wavelength [nm]')
plt.ylabel('Normalized Intesity')
return plt.gcf()
def draw_figure(canvas, figure, loc=(0, 0)):
figure_canvas_agg = FigureCanvasTkAgg(figure, canvas)
figure_canvas_agg.draw()
figure_canvas_agg.get_tk_widget().pack(side='top', fill='both', expand=1)
return figure_canvas_agg
def delete_fig_agg(fig_agg):
fig_agg.get_tk_widget().forget()
plt.close('all')
if __name__ == '__main__':
... some code ...
# define the window layout
layout = [[sg.Button('update')],
[sg.Text('Plot test', font='Any 18')],
[sg.Canvas(size=(500,500), key='canvas')] ]
# create the form and show it without the plot
window = sg.Window('Demo Application - Embedding Matplotlib In PySimpleGUI',
layout, finalize=True)
fig_agg = None
while True:
event, values = window.read()
if event is None: # if user closes window
break
if event == "update":
if fig_agg is not None:
delete_fig_agg(fig_agg)
fig = fig_maker(ccs,dark,sub)
fig_agg = draw_figure(window['canvas'].TKCanvas, fig)
window.close()
Now for the fun part (I can't seem to get it to work). I would like the plot to always be updating similar to how I did it using just matplotlib so that the user doesn't have to press 'update'. Using PySimpleGUI long_task threaded example is where my program starts to fail. I don't actually get any errors thrown except for a print to the Debug I/O stating *** Faking Timeout *** before Python closes the script.
I even just tried to do a for loop of 10 iterations instead of continuous while loop:
from instrumental.drivers.spectrometers import thorlabs_ccs
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import PySimpleGUI as sg
import matplotlib, time, threading
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
def long_function_thread(window, ccs, dark, sub):
for i in range(10):
fig = fig_maker(ccs, dark, sub)
fig_agg = draw_figure(window['canvas'].TKCanvas, fig)
window.write_event_value('-THREAD PROGRESS-', i)
time.sleep(1)
delete_fig_agg(fig_agg)
time.sleep(0.1)
window.write_event_value('-THREAD DONE-', '')
def long_function(window, ccs, dark, sub):
print('In long_function')
threading.Thread(target=long_function_thread, args=(window, ccs, dark, sub), daemon=True).start()
def fig_maker(ccs, dark, sub):
plt.clf()
plt.close()
data = ccs.take_data(num_avg=3)
norm = (data[0]-dark[0])/(sub[0]-dark[0])
plt.plot(data[1],norm,c='r')
plt.axis([400,750,0,1.1])
plt.grid(color='w', linestyle='--')
plt.xlabel('Wavelength [nm]')
plt.ylabel('Normalized Intesity')
return plt.gcf()
def draw_figure(canvas, figure, loc=(0, 0)):
figure_canvas_agg = FigureCanvasTkAgg(figure, canvas)
figure_canvas_agg.draw()
figure_canvas_agg.get_tk_widget().pack(side='top', fill='both', expand=1)
return figure_canvas_agg
def delete_fig_agg(fig_agg):
fig_agg.get_tk_widget().forget()
plt.close('all')
if __name__ == '__main__':
... some code ...
# define the window layout
layout = [[sg.Button('Go')],
[sg.Text('Plot test', font='Any 18')],
[sg.Canvas(size=(500,500), key='canvas')] ]
# create the form and show it without the plot
window = sg.Window('Demo Application - Embedding Matplotlib In PySimpleGUI',
layout, finalize=True)
fig_agg = None
while True:
event, values = window.read()
if event is None or event == 'Exit':
break
if event == 'Go':
print('Calling plotter')
long_function(window, ccs, dark, sub)
print('Long function has returned from starting')
elif event == '-THREAD DONE-':
print('Your long operation completed')
window.close()
Appologies on the long description and code dump but I thought this is the easiest way to explain. Any help or links on this issue would be greatly appreciated.
If someone wants to try and run my script this should just produce a random plot instead
def random_fig_maker():
plt.scatter(np.random.rand(1,10),np.random.rand(1,10))
return plt.gcf()
