这是超级电容器充电和放电的较大程序中的一个问题。顾名思义,CELL_DISCHARGE 函数是对所连接的超级电容器的电池进行放电的函数。在该函数中,GUI 窗口在多次调用
后冻结 CELL_DISCHARGE_frame.update_idletasks()
这是整个功能:
def CELL_DISCHARGE():
SCPI_instrument_frame.grid_forget()
CELL_DISCHARGE_frame.grid()
#cell discharge
global Chroma_address
global DAQ_address
rm = pyvisa.ResourceManager()
myChroma = rm.open_resource(Chroma_address)
my34970A = rm.open_resource(DAQ_address)
ttk.Label(CELL_DISCHARGE_frame, text="Cell discharge for 3 minutes").grid(row=0, column=0, pady=10)
voltage_label = ttk.Label(CELL_DISCHARGE_frame, text="Voltage")
voltage_label.grid(row=2, column=0, pady=10)
progress = ttk.Progressbar(CELL_DISCHARGE_frame, orient="horizontal", length=180, mode="determinate")
progress.grid(row=1, column=0, pady=10)
progress["maximum"] = 180
print("cell discharge for 3 minutes") #3min is for test 10 min for live
DAQ_status = my34970A.query("*IDN?")
print("DAQ Serial:" + DAQ_status)
myChroma.write('CHAN 1') #discharge
myChroma.write('MODE CCL')
myChroma.write('CURR:STAT:L1 ' + str(Discharge_curr)) #CC, static, ch 1 (discharge), 1A
myChroma.write('LOAD ON')
my34970A.write("ROUT:OPEN (@202,204,206,208,210,212,214,216,218)") #unaffected / set to discharge
#debug*******************************************************************
my34970A.write("ROUT:OPEN (@302,304,306,308,310,312,314,316,318)") #unaffected / set to discharge
#debug*******************************************************************
my34970A.write("ROUT:CLOS (@201,203,205,207,209,211,213,215,217,219)") #affected / set for in circuit
#debug*******************************************************************
my34970A.write("ROUT:CLOS (@301,303,305,307,309,311,313,315,317,319)") #affected / set for in circuit
#debug*******************************************************************
for countdown in range(180,0,-1):
progress["value"] = 180 - countdown
CELL_DISCHARGE_frame.update_idletasks()
print(str(countdown) + ": Seconds Remaining - initial discharge")
max_range = Num_samples + 101
for t in range(101,max_range,1):
code = 200 + (t)
print(code)
#serialcomm.write(str(code).encode())
my34970A.write('MEAS:VOLTAGE:DC? AUTO, DEF, (@' + str(t) + ')')
voltage = float(my34970A.read())
print("Voltage for Channel " + str(t) + " = " + str(voltage) + " VDC")
voltage_label.config(text="Voltage:" + str(voltage))
CELL_DISCHARGE_frame.update_idletasks()
if msvcrt.kbhit():
ASCII_in = ord(msvcrt.getche())
if ASCII_in == 120:
break
time.sleep(1)
我尝试使用多线程来解决这个问题,因为我已经看到了这个建议,但是程序的行为不符合预期。添加线程后,代码如下所示:
import threading
def CELL_DISCHARGE():
SCPI_instrument_frame.grid_forget()
CELL_DISCHARGE_frame.grid()
# Cell discharge
global Chroma_address
global DAQ_address
rm = pyvisa.ResourceManager()
myChroma = rm.open_resource(Chroma_address)
my34970A = rm.open_resource(DAQ_address)
ttk.Label(CELL_DISCHARGE_frame, text="Cell discharge for 3 minutes").grid(row=0, column=0, pady=10)
# Create a progress bar
progress = ttk.Progressbar(CELL_DISCHARGE_frame, orient="HORIZONTAL", length=180, mode="determinate")
progress.grid(row=1, column=0, pady=10)
progress["maximum"] = 180 # 180 seconds = 3 minutes
# Create the label separately from the grid layout call
voltage_label = ttk.Label(CELL_DISCHARGE_frame, text="Voltage: -- VDC")
voltage_label.grid(row=2, column=0, pady=10)
def discharge_process():
print("cell discharge for 3 minutes")
DAQ_status = my34970A.query("*IDN?")
print("DAQ Serial:" + DAQ_status)
myChroma.write('CHAN 1') # discharge
myChroma.write('MODE CCL')
myChroma.write('CURR:STAT:L1 ' + str(Discharge_curr)) # CC, static, ch 1 (discharge), 1A
myChroma.write('LOAD ON')
my34970A.write("ROUT:OPEN (@202,204,206,208,210,212,214,216,218)") # unaffected / set to discharge
my34970A.write("ROUT:OPEN (@302,304,306,308,310,312,314,316,318)") # unaffected / set to discharge
my34970A.write("ROUT:CLOS (@201,203,205,207,209,211,213,215,217,219)") # affected / set for in circuit
my34970A.write("ROUT:CLOS (@301,303,305,307,309,311,313,315,317,319)") # affected / set for in circuit
# Loop for 180 seconds (3 minutes) with progress updates
for countdown in range(180, 0, -1):
print(f"{countdown}: Seconds Remaining - initial discharge")
# Update the progress bar
progress["value"] = 180 - countdown
CELL_DISCHARGE_frame.update_idletasks() # Force update the window
max_range = Num_samples + 101
for t in range(101, max_range, 1):
code = 200 + t
print(code)
# Measure voltage
my34970A.write('MEAS:VOLTAGE:DC? AUTO, DEF, (@' + str(t) + ')')
voltage = float(my34970A.read())
print(f"Voltage for Channel {t} = {voltage:.2f} VDC")
# Update the voltage label in the GUI
voltage_label.config(text=f"Voltage: {voltage:.2f} VDC")
CELL_DISCHARGE_frame.update_idletasks() # Force update the window
if msvcrt.kbhit(): # Check for keyboard hit
ASCII_in = ord(msvcrt.getche())
if ASCII_in == 120: # If 'x' is pressed, break the loop
break
time.sleep(1) # Wait for 1 second before next iteration
# Run the discharge process in a separate thread
discharge_thread = threading.Thread(target=discharge_process)
discharge_thread.start()
添加此更改后,我希望程序在与 GUI 分开的线程中运行放电,但这在单独的线程中运行放电进程并继续程序的后续步骤。如何更好地实现多线程或可能使用事件处理程序?该解决方案还需要在整个充电和放电过程中适用于其他几个函数调用。
time.sleep
是导致 GUI 冻结的原因。 将 for countdown in range(180,0,-1):
循环替换为使用 tk.after
调用自身的函数应该可行。 没有你的库和硬件我无法测试它。 有很多方法可以使用after
来实现你想要的。 在 stackoverflow 中搜索 [tkinter] after 和 sleep 会找到很多例子。
def do_countdown( countdown ):
progress["value"] = 180 - countdown
CELL_DISCHARGE_frame.update_idletasks()
print(str(countdown) + ": Seconds Remaining - initial discharge")
max_range = Num_samples + 101
for t in range(101,max_range,1):
code = 200 + (t)
print(code)
#serialcomm.write(str(code).encode())
my34970A.write('MEAS:VOLTAGE:DC? AUTO, DEF, (@' + str(t) + ')')
voltage = float(my34970A.read())
print("Voltage for Channel " + str(t) + " = " + str(voltage) + " VDC")
voltage_label.config(text="Voltage:" + str(voltage))
CELL_DISCHARGE_frame.update_idletasks()
if msvcrt.kbhit():
ASCII_in = ord(msvcrt.getche())
if ASCII_in == 120:
break
if countdown >= 0:
CELL_DISCHARGE_frame.after( 1000, do_countdown, countdown-1 )
do_countdown( 180 )