Home > other >  Why does my update to a Listbox in a while loop with PySimpleGUI end up in my program hanging?
Why does my update to a Listbox in a while loop with PySimpleGUI end up in my program hanging?

Time:12-12

So, I am building a little Python program to do some cost and throughput calculations for me. Keep in mind, I am a Python novice and completely new to PySimpleGUI with this project. I am specifically concerned with why, in the code below, line 59 (thruput_window['LISTBOX'].update(prompt) within the update_values() function) is causing the program to hang? I would think it should update with the indicated string, and continue on through the while loop, but instead it just stalls out, and doesn't even update the Listbox element with prompt. Any ideas?

Thanks for the help, and apologies for the long code block; I don't post on here often, and wasn't sure how much to include so I just posted the whole thing.

# 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. But even though it's much simpler now, I am never able to even enter the Correct or Append loops now - what am I missing? I'm sure it's something simple I am overcomplicating.

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

CodePudding user response:

update_values called in the event "Append" of thruput_window.

In function update_values, you do something wrong

  • bind return key to element 'USER-INPUT', binding should be done right after your thruput_window finalized, then while loop to read event.
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')
  • update element again and again, should be better to update once. Including update in the event "Append" of thruput_window, total 13 times, but only last one shown.
    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
  • Wrong event handling, it is under event "Append", so following if will be always False. It should be in while loop for thruput_window, so a new thruput_window.read() to get the event, without read, no new thruputEvent and thruputValues.
if thruputEvent == 'USER-INPUT' and '_Enter':
  • Event from binding of element with str key, the event will be key '_Enter', so shoule be like this
if thruputEvent == 'USER-INPUT_Enter':

Nested window or while-loop make you code complex, it is preferred to use another function to handle your second window, maybe like this

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

enter image description here

[Update]

For key of a Button, it is defined by

  • if there's an option key defined
  • if there's an option k defined
  • use button_text if not duplicate in layout
  • use str(element.Key) str(window.UniqueKeyCounter)) if duplicate

Here, you defined key of buttons as 'CORRECT' and 'APPEND', an the event is always the key of the element, not the button_text. that's why you never go into the event handlers of 'Correct' and 'Append'.

  • Related