首页 > 解决方案 > Tkinter python中的线程

问题描述

我正在尝试在 tkinter GUI 中运行无限任务(直到被杀死)。在执行此任务后,我尝试使用线程来停止 GUI 冻结和无限挂起。我的期望是线程会停止 GUI(主循环)挂起,但事实并非如此。附上我的代码。有任何想法吗?我调用线程函数的代码中只有一行。

问候,

约旦。

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()

标签: multithreadingtkinter

解决方案


你想替换:

threading.Thread(target=fire_continuous(dev,channel,pulse_on,pulse_off,N,command)).start()

with(实际参数,而不是这些占位符)

threading.Thread(target=fire_continuous, args=(dev,channel,pulse_on,pulse_off,N,command)).start()

注意args=第二个版本中的参数。

在第一个版本中使用括号调用fire_continuous会立即调用该函数(在主线程内)。的target参数threading.Thread需要一个可调用的(尚未调用)


推荐阅读