Using non-multithreaded PySimpleGUI window before multithreading

Question:

This question could also be rephrased as: Asking if a PySimpleGUI application should enable multithreading, by using a PySimpleGUI window.

This illustrates the dilema I currently have.

I have two versions of the application:

  1. Regular, that does things step by step.
  2. Threaded, that processes several dataframe lines further so that they could be displayed instantly.

They both work as expected.

The problem arises when I try to ask which version should be used I also use this time for folder selection. Right now I have it like this:

if __name__ == '__main__':

    search_file = None
    data_folder = None
    multithreading = False

    folder_selection_screen()

    if multithreading:
        main_with_threading()
    else:
        main()

folder_selection_screen()

def folder_selection_screen():
    global search_file
    global data_folder
    global multithreading

    layout = [
        [sg.Text('Select xlsx file:'), sg.Input(key='-FILE-'), sg.FileBrowse(file_types=(("Excel Files", "*.xlsx"),))],
        [sg.Text('Select folder with xlsx files:'), sg.Input(key='-FOLDER-'), sg.FolderBrowse()],
        [sg.Checkbox('Enable multithreading', key='-MULTITHREADING-'), sg.Button('Submit')]]

    window = sg.Window('Browse Documents', layout)

    while True:
        event, values = window.read()
        if event == sg.WIN_CLOSED:
            break
        if event == 'Submit':
            print(f"Selected file: {values['-FILE-']}")
            search_file = values['-FILE-']
            print(f"Selected folder: {values['-FOLDER-']}")
            data_folder = values['-FOLDER-']
            print(f"Multithreading enabled: {values['-MULTITHREADING-']}")
            multithreading = values['-MULTITHREADING-']
            window.close()
            break

main_with_threading()

def main_with_threading():

    queue = Queue(maxsize=3)

    gui_thread = Thread(
        target=gui_process,
        args=(queue,)
    )

    gui_thread.start()

    matching_thread = Thread(
        target=process_sku,
        args=(queue,)
    )
    matching_thread.start()

    queue.join()

And possibly relevant gui_process(queue):

def gui_process(queue):
    while True:
        try:
            item = queue.get()
        except Empty:
            continue
        else:
            print(f'Processing SKU: {item[0]}')
            queue.task_done()
            action = display_matches(*item)
            if action == "Confirm":
                pass
            elif action == "Skip":
                pass

When folder_selection_screen() is used, it seems that I am no longer able to use PySimpleGUI inside a thread.

I get the:

RuntimeError: main thread is not in main loop

Error. I am probably misunderstanding something or missing something obvious as this is the first time I am using threads, but right now the only difference is that a PySimpleGUI window is called without multithreading. Does it mean that "main loop" was defined?
Is there no way of completely clearing the data so I can run the process on selected thread?
Or is the only way of solving this is moving the folder_selection_screen() into the gui_process(queue)?

Like in some other questions about this error that have been asked:
I think this is an example:
console application main thread is not in main loop using pysimplegui

But wouldn’t that defeat the purpose?

I also found this question which might be related, but it seems unanswered:
Python, threading issue when using pysimplegui and tkPDFViewer

If any other info is necessary, please, do tell.

I will be grateful for an answer.

I tried to run a regular PySimpleGUI window to ask for some information. Using that information I can determine should multithreading be used. But that led to me not being able to use multithreading at all.

Edit:
I went around the problem, and if the option to select the multithreaded version is chosen, then the entire app restarts with the same folder screen already put on the main GUI thread. I would have liked for it to be seamless, but I guess this is fine as well.

Asked By: Unex256

||

Answers:

You cannot run the PySimpleGUI or GUI update not in the main thread.

Following code demo the way how I do it, all the code for the GUI are on the main thread. To avoid GUI update on other thread, method window.write_event_value used to generate an event to main thread to update the GUI.

import time
import datetime
import threading
import PySimpleGUI as sg

def job(window):
    global running
    while running:
        window.write_event_value('Event', datetime.datetime.now().strftime("%H:%M:%S"))
        time.sleep(0.1)

def stop(thread):
    global running
    if thread:
        running = False
        thread.join()
    return None

layout = [[sg.Button('Start'), sg.Button('Stop')]]
window = sg.Window('Threading', layout)
thread, running, state = None, False, None

while True:

    event, values = window.read()

    if event == sg.WIN_CLOSED:
        thread = stop(thread)
        break

    elif event == 'Start':
        thread = stop(thread)
        running = True
        thread = threading.Thread(target=job, args=(window, ), daemon=True)
        thread.start()

    elif event == 'Stop':
        thread = stop(thread)

    elif event == 'Event':
        now = values[event]
        if now != state:
            print(now)
            state = now

window.close()
Answered By: Jason Yang