为什么我在使用 PySimpleGUI 的 while 循环中更新列表框最终导致我的程序挂起?
Why does my update to a Listbox in a while loop with PySimpleGUI end up in my program hanging?
因此,我正在构建一个 Python 小程序来为我计算一些成本和吞吐量。请记住,我是一个 Python 新手,并且对这个项目的 PySimpleGUI 完全陌生。我特别关心为什么在下面的代码中,第 59 行(update_values()
函数中的 thruput_window['LISTBOX'].update(prompt)
)导致程序挂起?我认为它应该用指定的字符串更新,并继续通过 while 循环,但它只是停滞不前,甚至没有用 prompt
更新列表框元素。有什么想法吗?
感谢您的帮助,对于长代码块表示歉意;我不经常 post 在这里,也不确定要包括多少,所以我只是 post 编辑了整个内容。
# 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()
Edit: So I took your advice on getting rid of the nested windows (thanks), and just set up a right column that appears when the appropriate selection in the left column is clicked.但即使现在简单得多,我现在甚至无法进入 Correct
或 Append
循环 - 我错过了什么?我确定这是简单的事情,但我过于复杂了。
# 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()
update_values
在 thruput_window 的“追加”事件中调用。
在函数update_values
中,你做错了
- 将 return 键绑定到元素
'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 的“追加”事件中更新,共 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
- 错误的事件处理,它在事件“Append”下,所以后面的 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':
嵌套 window 或 while 循环使您的代码变得复杂,最好使用另一个函数来处理您的第二个 window,可能像这样
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()
[更新]
对于Button的key,定义为
- 如果有选项
key
已定义
- 如果定义了一个选项
k
- 如果在布局中不重复,则使用button_text
- 如果重复使用
str(element.Key) + str(window.UniqueKeyCounter))
在这里,您将按钮的键定义为'CORRECT'和'APPEND',事件始终是元素的键,而不是button_text。这就是为什么您永远不会进入 'Correct' 和 'Append'.
的事件处理程序的原因
因此,我正在构建一个 Python 小程序来为我计算一些成本和吞吐量。请记住,我是一个 Python 新手,并且对这个项目的 PySimpleGUI 完全陌生。我特别关心为什么在下面的代码中,第 59 行(update_values()
函数中的 thruput_window['LISTBOX'].update(prompt)
)导致程序挂起?我认为它应该用指定的字符串更新,并继续通过 while 循环,但它只是停滞不前,甚至没有用 prompt
更新列表框元素。有什么想法吗?
感谢您的帮助,对于长代码块表示歉意;我不经常 post 在这里,也不确定要包括多少,所以我只是 post 编辑了整个内容。
# 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()
Edit: So I took your advice on getting rid of the nested windows (thanks), and just set up a right column that appears when the appropriate selection in the left column is clicked.但即使现在简单得多,我现在甚至无法进入 Correct
或 Append
循环 - 我错过了什么?我确定这是简单的事情,但我过于复杂了。
# 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()
update_values
在 thruput_window 的“追加”事件中调用。
在函数update_values
中,你做错了
- 将 return 键绑定到元素
'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 的“追加”事件中更新,共 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
- 错误的事件处理,它在事件“Append”下,所以后面的 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':
嵌套 window 或 while 循环使您的代码变得复杂,最好使用另一个函数来处理您的第二个 window,可能像这样
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()
[更新]
对于Button的key,定义为
- 如果有选项
key
已定义 - 如果定义了一个选项
k
- 如果在布局中不重复,则使用button_text
- 如果重复使用
str(element.Key) + str(window.UniqueKeyCounter))
在这里,您将按钮的键定义为'CORRECT'和'APPEND',事件始终是元素的键,而不是button_text。这就是为什么您永远不会进入 'Correct' 和 'Append'.
的事件处理程序的原因