Tkinter GUI 窗口冻结,我该如何解决该问题?

问题描述 投票:0回答:1

这是超级电容器充电和放电的较大程序中的一个问题。顾名思义,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 分开的线程中运行放电,但这在单独的线程中运行放电进程并继续程序的后续步骤。如何更好地实现多线程或可能使用事件处理程序?该解决方案还需要在整个充电和放电过程中适用于其他几个函数调用。

python python-3.x tkinter pyvisa
1个回答
0
投票

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 )
© www.soinside.com 2019 - 2024. All rights reserved.