I am trying to run an infinite task (until killed) in a tkinter GUI. I have attempted to use threads to stop the GUI freezing and infinitely hanging after executing this task. My expectation was that threading would stop the GUI (mainloop) hanging but this is not the case. My code is attached. Any ideas? There is only one line in the code I call the threading function.
Regards,
Jordan.
import nidaqmx.system
import nidaqmx
import numpy as np
import matplotlib.pyplot as plt
#from nidaqmx.stream_readers import AnalogMultiChannelReader, DigitalMultiChannelReader
#from nidaqmx.stream_writers import AnalogSingleChannelWriter
Volts = nidaqmx.constants.VoltageUnits.VOLTS
system = nidaqmx.system.System.local()
system.driver_version
from nidaqmx import stream_writers
#from nidaqmx.constants import LineGrouping
#from nidaqmx.constants import TerminalConfiguration
import time
import os
from PIL import Image
import tkinter as tk
from PIL import ImageTk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import threading
dirpath = os.getcwd()
os.chdir(dirpath)
filename = r'Cutera_2.jpg'
img = Image.open(filename)
img.save('Cutera_logo.ico')
logo = dirpath + '\Cutera_logo.ico'
Astrum = dirpath + '\Astrum_board.png'
img = Image.open(Astrum) # image extension *.png,*.jpg
width, height = img.size
enlarge = 1.75
new_width = int(width*enlarge)
new_height = int(height*enlarge)
img = img.resize((new_width, new_height), Image.ANTIALIAS)
img.save('Astrum2.png')
Astrum2 = dirpath + '\Astrum2.png'
system = nidaqmx.system.System.local()
print(system.driver_version)
for device in system.devices:
dev = str(device).split('=')[1].split(')')[0]
print(device)
print(dev)
sample_clock = '/'+dev+'/ai/SampleClock'
def arm_laser(dev,state):
if state == 'on':
state = True
elif state == 'off':
state = False
with nidaqmx.Task("arm") as arm:
arm.do_channels.add_do_chan(dev+"/port1/line0")
arm.write(state, auto_start=True)
def current_to_voltage(current): #gives command voltage for desired current 50mV = 1A
voltage = current/20
return float(voltage)
#fire_astrum(dev,ana_out,ana_in,N,pulse_count,pulse_on,pulse_off,command,mode)
def fire_single_shot(dev,channel,pulse_duration,N,command):
channel = dev +'/' + channel
arm_laser(dev,'on') # Sends 5V to Astrum to arm laser diode
time.sleep(0.2) # wait for 100ms before data stream
with nidaqmx.Task() as task:
samples = np.append(command*np.ones(1),np.zeros(1))
task.ao_channels.add_ao_voltage_chan(channel)
task.timing.cfg_samp_clk_timing(rate=1/pulse_duration*1000, sample_mode= nidaqmx.constants.AcquisitionType.FINITE, samps_per_chan = N)
Writer = stream_writers.AnalogSingleChannelWriter(task.out_stream, auto_start=True)
Writer.write_many_sample(samples)
task.wait_until_done(timeout=10)
time_vec = np.linspace(0, (pulse_duration+pulse_duration)*int(1), num=N, endpoint=True)
print('length of time vec = ' + str(len(time_vec)))
print('length of trigger = ' + str(len(samples)))
return time_vec, samples
arm_laser(dev,'off')
def fire_burst(dev,channel,pulse_on,pulse_off,pulse_count,N,command):
channel = dev +'/' + channel
arm_laser(dev,'on') # Sends 5V to Astrum to arm laser diode
time.sleep(0.2) # wait for 100ms before data stream
with nidaqmx.Task() as task:
duty = pulse_on/(pulse_on+pulse_off) # duty cycle in %
#N = 2**8 # no of points in stream array (8-bit)
array_on = int(N*duty) # on values in array
array_off = int(N-array_on) # off values in array
samples = np.append(command*np.ones(array_on),np.zeros(array_off))
task.ao_channels.add_ao_voltage_chan(channel)
task.timing.cfg_samp_clk_timing(rate=(array_on/N)*1/pulse_on*1000*N, sample_mode= nidaqmx.constants.AcquisitionType.FINITE , samps_per_chan= pulse_count*len(samples))
Writer = stream_writers.AnalogSingleChannelWriter(task.out_stream, auto_start=True)
Writer.write_many_sample(samples)
task.wait_until_done(timeout=60) # Alternative command lokk at API
trigger = np.tile(samples,pulse_count)
time_vec = np.linspace(0, (pulse_on+pulse_off)*pulse_count, num=len(trigger), endpoint=True)
print('length of time vec = ' + str(len(time_vec)))
print('length of trigger = ' + str(len(trigger)))
return time_vec, trigger
arm_laser(dev,'off')
def fire_continuous(dev,channel,pulse_on,pulse_off,N,command):
channel = dev +'/' + channel
arm_laser(dev,'on') # Sends 5V to Astrum to arm laser diode
time.sleep(0.2) # wait for 100ms before data stream
with nidaqmx.Task() as task:
duty = pulse_on/(pulse_on+pulse_off) # duty cycle in %
#N = 2**8 # no of points in stream array (8-bit)
array_on = int(N*duty) # on values in array
array_off = int(N-array_on) # off values in array
samples = np.append(command*np.ones(array_on),np.zeros(array_off))
task.ao_channels.add_ao_voltage_chan(channel)
task.timing.cfg_samp_clk_timing(rate=(array_on/N)*1/pulse_on*1000*N, sample_mode= nidaqmx.constants.AcquisitionType.CONTINUOUS)
Writer = stream_writers.AnalogSingleChannelWriter(task.out_stream, auto_start=True)
Writer.write_many_sample(samples)
task.wait_until_done(timeout=nidaqmx.constants.WAIT_INFINITELY) # Alternative command lokk at API
#def fire_continuous(dev,channel,pulse_on,pulse_off,N,command): # Fire continuous has to be threaded
# channel = dev +'/' + channel
#
# arm_laser(dev,'on') # Sends 5V to Astrum to arm laser diode
# time.sleep(0.2) # wait for 100ms before data stream
#
# task = nidaqmx.Task()
# duty = pulse_on/(pulse_on+pulse_off) # duty cycle in %
# #N = 2**8 # no of points in stream array
# array_on = int(N*duty) # on values in array
# array_off = int(N-array_on) # off values in array
# samples = np.append(command*np.ones(array_on),np.zeros(array_off))
# task.ao_channels.add_ao_voltage_chan(channel)
# task.timing.cfg_samp_clk_timing(rate=(array_on/N)*1/pulse_on*1000*N, source=sample_clock, sample_mode= nidaqmx.constants.AcquisitionType.CONTINUOUS)
# Writer = stream_writers.AnalogSingleChannelWriter(task.out_stream, auto_start=True)
# Writer.write_many_sample(samples)
## task.start()
#root = tk.Toplevel
window = tk.Tk()
window.resizable(width=True, height=True)
#window.withdraw()
window.iconbitmap(logo)
window.configure(background='white')
window.geometry("1700x1500") # This sets the Window size to work with
#window.geometry("600x500") # This sets the Window size to work with
window.title('Astrum laser pulser')
defaultbg = window.cget('bg')
img = ImageTk.PhotoImage(Image.open(Astrum))
panel = tk.Label(window, image = img,background="white")
panel.place(relx=.7, rely=.65)
fig = plt.Figure(figsize=(5,4), dpi=100)
fig.tight_layout(True)
ax1 = fig.add_subplot(111)
canvas = FigureCanvasTkAgg(fig, window)
fig.canvas.draw()
canvas.get_tk_widget().place(relx=0.55, rely=0.35, anchor=tk.W)
trig, = ax1.plot([],[], color = 'g')
ax1.set_title('Pulse trigger')
ax1.set(xlabel='time (ms)', ylabel='current (A)')
ax1.grid(linewidth=0.7, linestyle=':')
def update_plot(x,y):
trig.set_data(x,y)
ax1.set_xlim(0, max(x))
ax1.set_ylim(0, 1.1*max(y))
fig.canvas.draw()
fig.canvas.flush_events()
def get_trigger(pulse_count,pulse_on,pulse_off,N,command):
duty = pulse_on/(pulse_on+pulse_off) # duty cycle in %
array_on = int(N*duty) # on values in array
array_off = int(N-array_on)
samples = np.append(command*np.ones(array_on),np.zeros(array_off))
trigger = np.tile(samples,pulse_count)
return trigger
l = tk.Label(window, bg='white', fg='black', width=20, text='Diode current = 0.0A')
l.pack()
def print_selection(v):
l.config(text='Diode current = ' + v + "A")
global variable
#print(v)
variable = v
def savecurrent():
print("current selcted was = " + variable + "A")
return float(variable)
s = tk.Scale(window, label='Diode current (A)', from_=0, to=70, orient=tk.HORIZONTAL, length=1400, showvalue=2,tickinterval=5, resolution=0.5, command=print_selection)
s.pack()
def selected():
print(var.get())
def fire_laser():
#global final
print(var.get(),get_pulse_on(),get_pulse_off(),get_pulse_count(),get_trig_delay())
print("Enter function to get configuration, write configuration then fire laser...")
diode_curr = savecurrent()
print(diode_curr)
if var.get() == 'SINGLE SHOT':
data = fire_burst(dev,'ao0',float(get_pulse_on()),float(get_pulse_off()),int(1),1000,current_to_voltage(diode_curr))
mode = get_trigger(int(1),float(get_pulse_on()),float(get_pulse_off()),1000,float(diode_curr))
t = data[0]
#mode = data[1]
print('single shot fired')
elif var.get() == 'CONTINUOUS':
#fire_continuous(dev,channel,pulse_on,pulse_off,N,command)
mode = get_trigger(int(get_pulse_count()),float(get_pulse_on()),float(get_pulse_off()),1000,float(diode_curr))
threading.Thread(target=fire_continuous(dev,'ao0',float(get_pulse_on()),float(get_pulse_off()),1000,current_to_voltage(diode_curr))).start()
t = np.linspace(0, (float(get_pulse_on())+float(get_pulse_off()))*int(get_pulse_count()), num=len(mode), endpoint=True)
#t = data[0]
print('continuous pulse modulation')
elif var.get() == 'PULSE TRAIN':
data = fire_burst(dev,'ao0',float(get_pulse_on()),float(get_pulse_off()),int(get_pulse_count()),1000,current_to_voltage(diode_curr))
mode = get_trigger(int(get_pulse_count()),float(get_pulse_on()),float(get_pulse_off()),1000,float(diode_curr))
t = data[0]
#mode = data[1]
print('A burst of ' + str(int(get_pulse_count())) + ' pulses fired')
update_plot(t,mode)
#final = [var.get(),var1.get(),var2.get(),get_noise_scan(),get_power_scan(), get_delay(), get_cycle(), get_dt(), get_temp(), get_step()]
# window.quit()
# window.destroy()
# return final
def stop_laser():
#global final
#fire_astrum(dev,'ao0','ai0:1',1000,int(1),float(get_pulse_on()),float(get_pulse_off()),current_to_voltage(0),'train')
print("Enter function here to kill the laser")
look = get_trigger(int(1),float(get_pulse_on()),float(get_pulse_off()),1000,float(0))
t = list(range(len(look)))
print('laser stopped')
update_plot(t,look)
arm_laser(dev,'off')
# final = [var.get(),var1.get(),var2.get(),get_noise_scan(),get_power_scan(), get_delay(), get_cycle(), get_dt(), get_temp(), get_step()]
# window.quit()
# window.destroy()
# return final
def get_pulse_on():
on = entry1.get()
print(on)
return on
def get_pulse_off():
off = entry2.get()
print(off)
return off
def get_pulse_count():
count = entry3.get()
print(count)
return count
def get_trig_delay():
delay = entry4.get()
print(delay)
return delay
def get_power_scan():
p = entry4.get()
print(p)
return p
# if var1.get() == "BRF" or var.get() == "single" or (var1.get() == "BRF" and var2.get() == "BRF"):
# brf.config(state=tk.DISABLED, selectcolor = "snow")
#
# elif var1.get() == "SHG" or var1.get() == "THG" or var1.get() == "ETA" or var1.get() == "MAIN TEC":
# brf.config(state=tk.ACTIVE, selectcolor = "peach puff")
#
# if var1.get() == "SHG" or var.get() == "single" or (var1.get() == "SHG" and var2.get() == "SHG"):
# shg.config(state=tk.DISABLED, selectcolor = "snow")
#
# elif var1.get() == "BRF" or var1.get() == "THG" or var1.get() == "ETA" or var1.get() == "MAIN TEC":
# shg.config(state=tk.ACTIVE, selectcolor = "spring green")
#
# if var1.get() == "THG" or var.get() == "single" or (var1.get() == "THG" and var2.get() == "THG"):
# thg.config(state=tk.DISABLED, selectcolor = "snow")
#
# elif var1.get() == "BRF" or var1.get() == "SHG" or var1.get() == "ETA" or var1.get() == "MAIN TEC":
# thg.config(state=tk.ACTIVE, selectcolor = "thistle1")
#
# if var1.get() == "ETA" or var.get() == "single" or (var1.get() == "ETA" and var2.get() == "ETA"):
# eta.config(state=tk.DISABLED, selectcolor = "snow")
#
# elif var1.get() == "BRF" or var1.get() == "SHG" or var1.get() == "THG" or var1.get() == "MAIN TEC":
# eta.config(state=tk.ACTIVE, selectcolor = "sky blue")
#
# if var1.get() == "MAIN TEC" or var.get() == "single" or (var1.get() == "MAIN TEC" and var2.get() == "MAIN TEC"):
# maint.config(state=tk.DISABLED, selectcolor = "snow")
#
# elif var1.get() == "BRF" or var1.get() == "SHG" or var1.get() == "THG" or var1.get() == "ETA":
# maint.config(state=tk.ACTIVE, selectcolor = "salmon")
############
trigg = tk.StringVar()
check1 = tk.Checkbutton(window, text='Trigger',
command=get_power_scan, variable = trigg,
onvalue="Yes", offvalue="No")
#
check1.place(x=200,y=100+15*20*2)
check1.deselect()
#############
mode = ["SINGLE SHOT", "CONTINUOUS", "PULSE TRAIN"]
#get_scan
# button = tk.Button(window, font="Heltavica",text ="PROCEED", command=get_scan)
# button.config(bd=8, font="Ariel", justify="center")
# button.place(relx=.39, rely=0.9)
#used to get the 'value' property of a tkinter.Radiobutton
var = tk.StringVar() #MODE
var2 = tk.StringVar() #FIRE
var3 = tk.StringVar() #STOP
fire = tk.Button(window, width=8 , bd=4, text="FIRE", command = fire_laser, activebackground="red")#, onvalue=1, offvalue=0)
fire.place(x=200,y=100+15*35*1)
#fire.deselect()
#fire.config(selectcolor = "snow")
#fire.pack()
#fire.update()
stop = tk.Button(window, width=8 , bd=4, text="STOP", command = stop_laser, activebackground="green")#, onvalue=1, offvalue=0)
stop.place(x=600,y=100+15*35*1)
#stop.deselect()
#stop.config(selectcolor = "snow")
#stop.pack()
#stop.update()
ss = tk.Radiobutton(window, selectcolor = "peach puff", width=12, bd=4, text="SINGLE SHOT", variable=var, value = mode[0], command = selected , indicatoron = 0)#, onvalue=1, offvalue=0)
ss.place(x=200,y=100+15*5*1)
ss.select()
#ss.config(selectcolor = "snow")
#ss.pack
ss.update()
c = tk.Radiobutton(window, selectcolor = "spring green", width=12 , bd=4, text="CONTINUOUS", variable=var, value = mode[1], command = selected , indicatoron = 0)#, onvalue=1, offvalue=0)
c.place(x=200,y=100+15*10*1)
c.deselect()
#c.config(selectcolor = "snow")
#c.pack
c.update()
pt = tk.Radiobutton(window, selectcolor = "thistle1" , width=12 , bd=4, text="PULSE TRAIN", variable=var, value = mode[2], command = selected , indicatoron = 0)#, onvalue=1, offvalue=0)
pt.place(x=200,y=100+15*15*1)
pt.deselect()
#pt.config(selectcolor = "snow")
#pt.pack
pt.update()
entry1 = tk.Entry(window, width=4)
entry1.insert(0, "40")
entry1.place(x=500,y=30+100+15*5*1)
entry1.config(bd=2, font="Ariel", justify="center",state=tk.NORMAL)
entry2 = tk.Entry(window, width=4)
entry2.insert(0, "15")
entry2.place(x=500,y=30+100+15*10*1)
entry2.config(bd=2, font="Ariel", justify="center",state=tk.NORMAL)
entry3 = tk.Entry(window, width=4)
entry3.insert(0, "10")
entry3.place(x=500,y=30+100+15*15*1)
entry3.config(bd=2, font="Ariel", justify="center",state=tk.NORMAL)
entry4 = tk.Entry(window, width=4)
entry4.insert(0, "100")
entry4.place(x=500,y=30+100+15*20*1)
entry4.config(bd=2, font="Ariel", justify="center",state=tk.NORMAL)
L = tk.Label(window, text = "Pulse on (ms) ", font="Ariel", width=22, height = 1 , background = defaultbg)
L.place(x=400,y=100+15*5*1)
L2 = tk.Label(window, text = "Pulse off (ms) " , font="Ariel", width=22, height = 1 , background = defaultbg)
L2.place(x=400,y=100+15*10*1)
L3 = tk.Label(window, text = "Pulse count " , font="Ariel", width=22, height = 1 , background = defaultbg)
L3.place(x=400,y=100+15*15*1)
L4 = tk.Label(window, text = "Delay (ms) " , font="Ariel", width=22, height = 1 , background = defaultbg)
L4.place(x=400,y=100+15*20*1)
window.mainloop()