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:
- Regular, that does things step by step.
- 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.
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()
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:
- Regular, that does things step by step.
- 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.
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()