2

I have just started learning PySimpleGUI and I wanted to create a custom GUI progress bar that will display what parts of the program are running at different times.

For example, I have a video processing program with various components so I want the progress bar to display text like:

'Extracting Frame 1 from Video'

'Cropping images'

'Removing Duplicate Images'

But all these lines require updating the progress bar window from different functions in the program where the GUI code associated with the window containing the progress bar is not running.

My code:

    image_name_list = Frame_Capture(f"{main_dir}\\{input_line2}.mp4")  # Generates image list of frames of video
    Remove_Duplicates(image_name_list)  # Removes duplicate images
    print("Enter name of pdf file")
    pdf_name = f'{input()}.pdf'
    makePdf(pdf_name, image_name_list)  # Converts images to pdf
    Cleanup(image_name_list)  # Deletes unneeded images
    os.startfile(pdf_name)

Here, I need to update the GUI Process bar from within the 'Frame_Capture', 'Remove_Duplicates', 'makePDF' and 'Cleanup' functions when the GUI component of my program itself is running in another part of the program.

The two solutions I can think of are:

  1. Creating my window globally and updating the progress bar in it globally
  2. Writing all my progress bar statements into a text file line by line when my program reaches a part where the progress bar needs to be updated and simultaneously loading the latest statement from the text file into my progress bar every few milliseconds.

Neither of these solutions sound good. Is there some other way I can do this?

Anmol Saksena
  • 91
  • 2
  • 10
  • first try to write it and see how it works. And how about sending `progressbar` to functions as argument - `Frame_Capture(..., my_progress_bar)` ? – furas Jan 01 '21 at 19:33
  • do you have to do it inside functions ? Can you update progressbar before every function? – furas Jan 01 '21 at 19:36
  • @furas That does sound easier. I will try to do that but the main problem is still there. I kind of have a test function that calls another function that calls another function that calls a function containing the code I posted. So, I guess instead of calling the code I posted 3 functions deep, I can remove all the function calls and integrate all the code into a single function which contains the GUI part of the code and update progress bar from there. But that would remove a lot of modularity from the program and make one function 200 lines long. – Anmol Saksena Jan 01 '21 at 19:44

1 Answers1

5

Make a main event loop for your GUI. When any function comes to a point that GUI needs to be updated, write an event to your main window using write_event_value(key, value). Example:

def test():
    import threading
    layout = [[sg.Text('Testing progress bar:')],
              [sg.ProgressBar(max_value=10, orientation='h', size=(20, 20), key='progress_1')]]

    main_window = sg.Window('Test', layout, finalize=True)
    current_value = 1
    main_window['progress_1'].update(current_value)

    threading.Thread(target=another_function,
                     args=(main_window, ),
                     daemon=True).start()

    while True:
        window, event, values = sg.read_all_windows()
        if event == 'Exit':
            break
        if event.startswith('update_'):
            print(f'event: {event}, value: {values[event]}')
            key_to_update = event[len('update_'):]
            window[key_to_update].update(values[event])
            window.refresh()
            continue
        # process any other events ...
    window.close()

def another_function(window):
    import time
    import random
    for i in range(10):
        time.sleep(2)
        current_value = random.randrange(1, 10)
        window.write_event_value('update_progress_1', current_value)
    time.sleep(2)
    window.write_event_value('Exit', '')