为啥我在使用 PySimpleGUI 的 while 循环中对列表框的更新最终导致我的程序挂起?

Posted

技术标签:

【中文标题】为啥我在使用 PySimpleGUI 的 while 循环中对列表框的更新最终导致我的程序挂起?【英文标题】:Why does my update to a Listbox in a while loop with PySimpleGUI end up in my program hanging?为什么我在使用 PySimpleGUI 的 while 循环中对列表框的更新最终导致我的程序挂起? 【发布时间】:2022-01-15 08:07:45 【问题描述】:

所以,我正在构建一个小 Python 程序来为我做一些成本和吞吐量计算。请记住,我是一名 Python 新手,并且对这个项目的 PySimpleGUI 完全陌生。我特别关心为什么在下面的代码中,第 59 行(update_values() 函数中的thruput_window['LISTBOX'].update(prompt))导致程序挂起?我认为它应该使用指示的字符串进行更新,并继续执行 while 循环,但它只是停止运行,甚至不使用 prompt 更新 Listbox 元素。有什么想法吗?

感谢您的帮助,并为长代码块道歉;我不经常在这里发帖,也不确定要包含多少内容,所以我只发布了整个内容。

# Throughput & Cost of Ownership Calculations
# Import necessary libraries
import PySimpleGUI as sg


# Define functions
def import_values():
    mylines = []
    myvalues = []
    with open(r'throughput_variables.txt', 'r') as file:
        for myline in file:
            mylines.append(myline.rstrip('\n'))
    for element in mylines:
        start = element.find(':') + 2
        end = len(element)
        value = int(element[start:end])
        myvalues.append(value)
    return myvalues


def import_variables():
    mylines = []
    myvariables = []
    with open(r'throughput_variables.txt', 'r') as file:
        for myline in file:
            mylines.append(myline.rstrip('\n'))
    for element in mylines:
        variable = element[:element.find(':')]
        myvariables.append(variable)
    return myvariables


def display_file():
    mylines = []
    myfile = []
    with open(r'throughput_variables.txt', 'r') as file:
        for myline in file:
            mylines.append(myline.rstrip('\n'))
    for element in mylines:
        myfile.append(element)
    return myfile


def update_values():
    Qtable1 = "What is the total recipe 1 time? \n"
    Qtable2 = "What is the total recipe 2 time? \n"
    Qtable3 = "What is the total recipe 3 time? \n"
    Qcleaner = "What is the total recipe time for the cleaner? \n"
    Qwts4 = "How long does it take to unload? \n"
    Qwetbot = "How long does it take the robot to handle? \n"
    myprompts = [Qtable1, Qtable2, Qtable3, Qcleaner, Qwts4, Qwetbot]
    myvariables = import_variables()
    myvalues = ['','','','','','']
    k = 0
    thruput_window['USER-INPUT'].bind('<Return>', '_Enter')
    with open(r'throughput_variables.txt', 'w+') as file:
        while k < 6:
            prompt = [myprompts[k]]
            thruput_window['LISTBOX'].update(prompt)
            if thruputEvent == 'USER-INPUT' and '_Enter':
                myvalues.append(thruputValues['USER-INPUT'])
                thruput_window['LISTBOX'].update([prompt[k],myvalues[k]])
                file.write(myvariables[k] + ": " + myvalues[k] + "\n")
                k = k + 1
    return import_values()


def calc_thruput(myvalues):
    t1_time = myvalues[0]
    t2_time = myvalues[1]
    t3_time = myvalues[2]
    cleaner_time = myvalues[3]
    wts4_transfer_time = myvalues[4]
    wet_bot_time = myvalues[5]
    denominator = max(t1_time, t2_time, t3_time) - min(t1_time, t2_time, t3_time) + \
                  max(t3_time, cleaner_time) + wts4_transfer_time + wet_bot_time
    numerator = 3600
    thruput = numerator / denominator
    return round(thruput, 2)


# Setup the home screen GUI
sg.theme('Dark2')
fnt = ('Gadugi', 20)
layout = [[sg.Text("What would you like to calculate? \n", font=('Gadugi', 40))],
          [sg.Button("Cost of Ownership")],
          [sg.Button("Throughput")],
          [sg.Button("Exit")]]
# Create the window
main_window = sg.Window("Operations Estimations", layout, margins=(400, 250), font=fnt)

# Create an event loop
while True:
    event, values = main_window.read()
    if event == "Throughput":
        with open(r'throughput_variables.txt', 'r') as file:
            content = display_file()
        layout = [[sg.Text("Do these times look correct, or would you like to append? ", font=('Gadugi', 30))],
                  [sg.Text("All times are in seconds. ", font=fnt)],
                  [sg.Listbox(values=content, size=(70, 10), key='LISTBOX')],
                  [sg.Input(size=(25, 1), enable_events=True, key='USER-INPUT')],
                  [sg.Button("Correct")],
                  [sg.Button("Append")],
                  [sg.Button("Go Back")]]
        thruput_window = sg.Window("Throughput Calculator", layout, margins=(325, 150), font=fnt)

        while True:
            thruputEvent, thruputValues = thruput_window.read()

            if thruputEvent == "Correct":
                answer = ["Throughput = ", str(calc_thruput(import_values())) + " microns/hour"]
                thruput_window['LISTBOX'].update(answer)
            if thruputEvent == "Append":
                answer = ["Throughput = ", str(calc_thruput(update_values())) + " microns/hour"]
                thruput_window['LISTBOX'].update(answer)
            elif thruputEvent == "Go Back" or event == sg.WIN_CLOSED:
                break
        thruput_window.close()
    # End program if user presses Exit or closes the window
    if event == "Exit" or event == sg.WIN_CLOSED:
        break
main_window.close()

编辑:所以我接受了摆脱嵌套的窗口(谢谢)的建议,并在单击左列中的相应选择时,设置右列。但即使现在它更简单了,我现在什至无法进入 CorrectAppend 循环 - 我错过了什么?我确定这很简单,但我过于复杂了。

# Setup the overall GUI font and theme, column structure, and window layout
sg.theme('Dark2')
fnt = ('Gadugi', 20)
left_column = [[sg.Text("What would you like to calculate? \n", font=('Gadugi', 30))],
               [sg.Button("Throughput"),
               sg.Button("Cost of Ownership"),
               sg.Button("Exit")]]
right_column = [[sg.Text("Do these times look correct, or would you like to append? ", font=('Gadugi', 30), key='HEADER', visible=False)],
                  [sg.Text("All times are in seconds. ", font=fnt, key='WARNING', visible=False)],
                  [sg.Listbox(values=[], size=(70, 10), key='LISTBOX', visible=False)],
                  [sg.Input(size=(25, 1), enable_events=True, key='USER-INPUT', visible=False)],
                  [sg.Button("Correct", key='CORRECT', visible=False),
                   sg.Button("Append", key='APPEND', visible=False)]]
layout = [[sg.Column(left_column),
          sg.VSeparator(),
          sg.Column(right_column)]]
window = sg.Window("Operations Estimations", layout, font=fnt)

# Create an event loop
while True:
    event, values = window.read()
    if event == "Exit" or event == sg.WIN_CLOSED:
        break
    if event == "Throughput":
        window['HEADER'].update(visible=True)
        window['WARNING'].update(visible=True)
        window['LISTBOX'].update(visible=True)
        window['USER-INPUT'].update(visible=True)
        window['CORRECT'].update(visible=True)
        window['APPEND'].update(visible=True)
        with open(r'throughput_variables.txt', 'r') as file:
            content = display_file()
        window['LISTBOX'].update(content)
    if event == "Correct":
        window['LISTBOX'].update(["TEST"])
        print('This is working')
        # answer = ["6EZ Throughput = ", str(calc_thruput(import_values())) + " microns/hour"]
        # window['LISTBOX'].update(answer)
    if event == "Append":
        window['USER-INPUT'].bind('<Return>', '_Enter')
        answer = ["Throughput = ", str(calc_thruput(update_values())) + " microns/hour"]
        window['LISTBOX'].update(answer)

window.close()

【问题讨论】:

【参考方案1】:

update_values 在 thruput_window 的“追加”事件中调用。

在函数update_values中,你做错了

将返回键绑定到元素'USER-INPUT',绑定应该在你的thruput_window完成后立即完成,然后while循环读取事件。
thruput_window = sg.Window("Throughput Calculator", layout, margins=(325, 150), font=fnt, finalize=True)    # Add option `finalize=True`
thruput_window['USER-INPUT'].bind('<Return>', '_Enter')
一次又一次更新元素,最好更新一次。包括 thruput_window 的“Append”事件中的更新,一共 13 次,但只显示了最后一次。
    with open(r'throughput_variables.txt', 'w+') as file:
        while k < 6:
            prompt = [myprompts[k]]
            thruput_window['LISTBOX'].update(prompt)
            if thruputEvent == 'USER-INPUT' and '_Enter':
                myvalues.append(thruputValues['USER-INPUT'])
                thruput_window['LISTBOX'].update([prompt[k],myvalues[k]])
                file.write(myvariables[k] + ": " + myvalues[k] + "\n")
                k = k + 1
错误的事件处理,它在事件“追加”下,所以跟随 if 将始终为 False。 thruput_window 应该在 while 循环中,所以一个新的 thruput_window.read() 来获取事件,没有read,没有新的 thruputEvent 和 thruputValues。
if thruputEvent == 'USER-INPUT' and '_Enter':
元素与str键绑定的事件,该事件是key+'_Enter',所以应该是这样的
if thruputEvent == 'USER-INPUT_Enter':

嵌套窗口或while循环使您的代码复杂,最好使用另一个函数来处理您的第二个窗口,也许像这样

import PySimpleGUI as sg

def function():

    sg.theme("DarkGrey3")
    layout = [
        [sg.Text()],
        [sg.Input(key='INPUT')],
        [sg.Button('OK'), sg.Button('Cancel')],
        [sg.Text()],
    ]
    window = sg.Window('POPUP', layout, finalize=True, modal=True)
    window['INPUT'].bind("<Return>", "_RETURN")

    while True:

        event, values = window.read()
        if event in (sg.WINDOW_CLOSED, 'Cancel'):
            value = None
            break
        elif event == 'OK':
            value = values['INPUT']
            break

    window.close()
    return value

font = ('Courier New', 11)
sg.theme('DarkBlue3')
sg.set_options(font=font)

layout = [
    [sg.Button("Get Input")],
    [sg.Text("",size=(80, 1), key='TEXT')],
]
window = sg.Window('Title', layout, finalize=True)

while True:

    event, values = window.read()
    if event == sg.WINDOW_CLOSED:
        break
    elif event == 'Get Input':
        value = function()
        if value is None:
            window['TEXT'].update('[Cancel]')
        else:
            window['TEXT'].update(value)

window.close()

[更新]

对于按钮的键,它是由定义的

如果有选项key 已定义 如果有选项 k 已定义 如果布局中不重复,则使用 button_text 如果重复使用str(element.Key) + str(window.UniqueKeyCounter))

在这里,您将按钮的键定义为“正确”和“附加”,事件始终是元素的键,而不是 button_text。这就是为什么你永远不会进入 'Correct' 和 'Append' 的事件处理程序。

【讨论】:

所以我接受了摆脱嵌套的窗口(谢谢)的建议,并刚刚在单击左列中的相应选择时设置右列。但即使它现在简单得多,我现在什至无法进入 CorrectAppend 循环 - 我错过了什么?我确定这很简单,我过于复杂了。编辑后的代码现在在主帖中。 如上更新

以上是关于为啥我在使用 PySimpleGUI 的 while 循环中对列表框的更新最终导致我的程序挂起?的主要内容,如果未能解决你的问题,请参考以下文章

Pysimplegui 和 Pygame 合并

PySimpleGui:如何将值从一个列表框添加到另一个列表框

PySimpleGUI 中的交互式 matplotlib 绘图

在 PySimpleGui 中创建自定义按钮

如何在PySimpleGUI中清除窗口?

让 PySimpleGUI 列表框列出对象属性