Changed multi-threaded demos to use new Window.write_event_value method

This commit is contained in:
PySimpleGUI 2020-07-18 13:42:32 -04:00
parent 934cea5881
commit 04dfa364d5
6 changed files with 109 additions and 139 deletions

View File

@ -22,17 +22,20 @@ def process_thread():
proc = subprocess.run('pip list', shell=True, stdout=subprocess.PIPE) proc = subprocess.run('pip list', shell=True, stdout=subprocess.PIPE)
thread = threading.Thread(target=process_thread, daemon=True) def main():
thread.start() thread = threading.Thread(target=process_thread, daemon=True)
thread.start()
while True: while True:
sg.popup_animated(sg.DEFAULT_BASE64_LOADING_GIF, 'Loading list of packages', time_between_frames=100) sg.popup_animated(sg.DEFAULT_BASE64_LOADING_GIF, 'Loading list of packages', time_between_frames=100)
thread.join(timeout=.1) thread.join(timeout=.1)
if not thread.is_alive(): if not thread.is_alive():
break break
sg.popup_animated(None) sg.popup_animated(None)
output = proc.__str__().replace('\\r\\n', '\n') output = proc.__str__().replace('\\r\\n', '\n')
sg.popup_scrolled(output, font='Courier 10') sg.popup_scrolled(output, font='Courier 10')
if __name__ == '__main__':
main()

View File

@ -1,10 +1,4 @@
#!/usr/bin/python3 #!/usr/bin/python3
# Rather than importing individual classes such as threading.Thread or queue.Queue, this
# program is doing a simple import and then indicating the package name when the functions
# are called. This seemed like a great way for the reader of the code to get an understanding
# as to exactly which package is being used. It's purely for educational and explicitness purposes
import queue
import threading import threading
import time import time
import itertools import itertools
@ -16,14 +10,13 @@ import PySimpleGUI as sg
The PySimpleGUI code, and thus the underlying GUI framework, runs as the primary, main thread The PySimpleGUI code, and thus the underlying GUI framework, runs as the primary, main thread
Other parts of the software are implemented as threads Other parts of the software are implemented as threads
A queue.Queue is used by the worker threads to communicate with code that calls PySimpleGUI directly. While users never know the implementation details within PySimpleGUI, the mechanism is that a queue.Queue
is used to communicate data between a thread and a PySimpleGUI window.
The PySimpleGUI code is structured just like a typical PySimpleGUI program. A layout defined, The PySimpleGUI code is structured just like a typical PySimpleGUI program. A layout defined,
a Window is created, and an event loop is executed. a Window is created, and an event loop is executed.
What's different is that within this otherwise normal PySimpleGUI Event Loop, there is a check for items
in the Queue. If there are items found, process them by making GUI changes, and continue. Copyright 2020 PySimpleGUI.org
This design pattern works for all of the flavors of PySimpleGUI including the Web and also repl.it
You'll find a repl.it version here: https://repl.it/@PySimpleGUI/Async-With-Queue-Communicationspy
""" """
@ -35,55 +28,58 @@ import PySimpleGUI as sg
# ## ## ## ## ## ## ## ## ## ## # ## ## ## ## ## ## ## ## ## ##
# ## ## ## ## ## ######## ## ## ######## # ## ## ## ## ## ######## ## ## ########
def worker_thread1(thread_name, run_freq, gui_queue): def worker_thread1(thread_name, run_freq, window):
""" """
A worker thread that communicates with the GUI A worker thread that communicates with the GUI
These threads can call functions that block without affecting the GUI (a good thing) These threads can call functions that block without affecting the GUI (a good thing)
Note that this function is the code started as each thread. All threads are identical in this way Note that this function is the code started as each thread. All threads are identical in this way
:param thread_name: Text name used for displaying info :param thread_name: Text name used for displaying info
:param run_freq: How often the thread should run in milliseconds :param run_freq: How often the thread should run in milliseconds
:param gui_queue: Queue used to communicate with the GUI :param window: window this thread will be conversing with
:type window: sg.Window
:return: :return:
""" """
print('Starting thread 1 - {} that runs every {} ms'.format(thread_name, run_freq)) print('Starting thread 1 - {} that runs every {} ms'.format(thread_name, run_freq))
for i in itertools.count(): # loop forever, keeping count in i as it loops for i in itertools.count(): # loop forever, keeping count in i as it loops
time.sleep(run_freq/1000) # sleep for a while time.sleep(run_freq/1000) # sleep for a while
# put a message into queue for GUI # put a message into queue for GUI
gui_queue.put('{} - {}'.format(thread_name, i)) window.write_event_value(thread_name, f'count = {i}')
def worker_thread2(thread_name, run_freq, gui_queue): def worker_thread2(thread_name, run_freq, window):
""" """
A worker thread that communicates with the GUI A worker thread that communicates with the GUI
These threads can call functions that block without affecting the GUI (a good thing) These threads can call functions that block without affecting the GUI (a good thing)
Note that this function is the code started as each thread. All threads are identical in this way Note that this function is the code started as each thread. All threads are identical in this way
:param thread_name: Text name used for displaying info :param thread_name: Text name used for displaying info
:param run_freq: How often the thread should run in milliseconds :param run_freq: How often the thread should run in milliseconds
:param gui_queue: Queue used to communicate with the GUI :param window: window this thread will be conversing with
:type window: sg.Window
:return: :return:
""" """
print('Starting thread 2 - {} that runs every {} ms'.format(thread_name, run_freq)) print('Starting thread 2 - {} that runs every {} ms'.format(thread_name, run_freq))
for i in itertools.count(): # loop forever, keeping count in i as it loops for i in itertools.count(): # loop forever, keeping count in i as it loops
time.sleep(run_freq/1000) # sleep for a while time.sleep(run_freq/1000) # sleep for a while
# put a message into queue for GUI # put a message into queue for GUI
gui_queue.put('{} - {}'.format(thread_name, i)) window.write_event_value(thread_name, f'count = {i}')
def worker_thread3(thread_name, run_freq, gui_queue): def worker_thread3(thread_name, run_freq, window):
""" """
A worker thread that communicates with the GUI A worker thread that communicates with the GUI
These threads can call functions that block without affecting the GUI (a good thing) These threads can call functions that block without affecting the GUI (a good thing)
Note that this function is the code started as each thread. All threads are identical in this way Note that this function is the code started as each thread. All threads are identical in this way
:param thread_name: Text name used for displaying info :param thread_name: Text name used for displaying info
:param run_freq: How often the thread should run in milliseconds :param run_freq: How often the thread should run in milliseconds
:param gui_queue: Queue used to communicate with the GUI :param window: window this thread will be conversing with
:type window: sg.Window
:return: :return:
""" """
print('Starting thread 3 - {} that runs every {} ms'.format(thread_name, run_freq)) print('Starting thread 3 - {} that runs every {} ms'.format(thread_name, run_freq))
for i in itertools.count(): # loop forever, keeping count in i as it loops for i in itertools.count(): # loop forever, keeping count in i as it loops
time.sleep(run_freq/1000) # sleep for a while time.sleep(run_freq/1000) # sleep for a while
# put a message into queue for GUI # put a message into queue for GUI
gui_queue.put('{} - {}'.format(thread_name, i)) window.write_event_value(thread_name, f'count = {i}')
@ -96,7 +92,7 @@ def worker_thread3(thread_name, run_freq, gui_queue):
# ###### ####### #### # ###### ####### ####
def the_gui(gui_queue): def the_gui():
""" """
Starts and executes the GUI Starts and executes the GUI
Reads data from a Queue and displays the data to the window Reads data from a Queue and displays the data to the window
@ -107,28 +103,30 @@ def the_gui(gui_queue):
""" """
layout = [[sg.Text('Multithreaded Window Example')], layout = [[sg.Text('Multithreaded Window Example')],
[sg.Text('', size=(15, 1), key='-OUTPUT-')], [sg.Text('', size=(15, 1), key='-OUTPUT-')],
[sg.Output(size=(40, 6))], [sg.Multiline(size=(40, 26), key='-ML-', autoscroll=True)],
[sg.Button('Exit')], ] [sg.Button('Exit')], ]
window = sg.Window('Multithreaded Window', layout) window = sg.Window('Multithreaded Window', layout, finalize=True)
# -- Create a Queue to communicate with GUI --
# queue used to communicate between the gui and the threads
# -- Start worker threads, each taking a different amount of time
threading.Thread(target=worker_thread1, args=('Thread 1', 500, window,), daemon=True).start()
threading.Thread(target=worker_thread2, args=('Thread 2', 200, window,), daemon=True).start()
threading.Thread(target=worker_thread3, args=('Thread 3', 1000, window,), daemon=True).start()
# -- Start the GUI passing in the Queue --
sg.cprint_set_output_destination(window, '-ML-')
colors = {'Thread 1':('white', 'red'), 'Thread 2':('white', 'purple'), 'Thread 3':('white', 'blue')}
# --------------------- EVENT LOOP --------------------- # --------------------- EVENT LOOP ---------------------
while True: while True:
# wait for up to 100 ms for a GUI event # wait for up to 100 ms for a GUI event
event, values = window.read(timeout=100) event, values = window.read()
if event in (sg.WIN_CLOSED, 'Exit'): if event in (sg.WIN_CLOSED, 'Exit'):
break break
# --------------- Loop through all messages coming in from threads --------------- # --------------- Loop through all messages coming in from threads ---------------
while True: # loop executes until runs out of messages in Queue sg.cprint(event, values[event], c=colors[event])
try: # see if something has been posted to Queue
message = gui_queue.get_nowait()
except queue.Empty: # get_nowait() will get exception when Queue is empty
break # break from the loop if no more messages are queued up
# if message received from queue, display the message in the Window
if message:
window['-OUTPUT-'].update(message)
# do a refresh because could be showing multiple messages before next Read
window.refresh()
print(message)
# if user exits the window, then close the window and exit the GUI func # if user exits the window, then close the window and exit the GUI func
window.close() window.close()
@ -142,16 +140,5 @@ def the_gui(gui_queue):
## ## ## ## #### ## ## ## ## ## ## #### ## ##
if __name__ == '__main__': if __name__ == '__main__':
# -- Create a Queue to communicate with GUI -- the_gui()
# queue used to communicate between the gui and the threads
gui_queue = queue.Queue()
# -- Start worker threads, each taking a different amount of time
threading.Thread(target=worker_thread1, args=(
'Thread 1', 500, gui_queue,), daemon=True).start()
threading.Thread(target=worker_thread2, args=(
'Thread 2', 200, gui_queue,), daemon=True).start()
threading.Thread(target=worker_thread3, args=(
'Thread 3', 1000, gui_queue,), daemon=True).start()
# -- Start the GUI passing in the Queue --
the_gui(gui_queue)
print('Exiting Program')

View File

@ -17,26 +17,21 @@ import PySimpleGUI as sg
long as the task is running. long as the task is running.
""" """
total = 100 # number of units that are used with the progress bar
message = '' # used by thread to send back a message to the main thread
progress = 0 # current progress up to a maximum of "total"
def long_operation_thread(seconds, window):
def long_operation_thread(seconds):
""" """
A worker thread that communicates with the GUI through a global message variable A worker thread that communicates with the GUI through a global message variable
This thread can block for as long as it wants and the GUI will not be affected This thread can block for as long as it wants and the GUI will not be affected
:param seconds: (int) How long to sleep, the ultimate blocking call :param seconds: (int) How long to sleep, the ultimate blocking call
""" """
progress = 0
global message, progress
print('Thread started - will sleep for {} seconds'.format(seconds)) print('Thread started - will sleep for {} seconds'.format(seconds))
for i in range(int(seconds * 10)): for i in range(int(seconds * 10)):
time.sleep(.1) # sleep for a while time.sleep(.1) # sleep for a while
progress += total / (seconds * 10) progress += 100 / (seconds * 10)
window.write_event_value('-PROGRESS-', progress)
message = f'*** The thread says.... "I am finished" ***' window.write_event_value('-THREAD-', '*** The thread says.... "I am finished" ***')
def the_gui(): def the_gui():
""" """
@ -44,47 +39,47 @@ def the_gui():
Reads data from a global variable and displays Reads data from a global variable and displays
Returns when the user exits / closes the window Returns when the user exits / closes the window
""" """
global message, progress
sg.theme('Light Brown 3') sg.theme('Light Brown 3')
layout = [[sg.Text('Long task to perform example')], layout = [[sg.Text('Long task to perform example')],
[sg.Output(size=(80, 12))], [sg.MLine(size=(80, 12), k='-ML-', reroute_stdout=True,write_only=True, autoscroll=True, auto_refresh=True)],
[sg.Text('Number of seconds your task will take'), [sg.Text('Number of seconds your task will take'),
sg.Input(key='-SECONDS-', size=(5, 1)), sg.Input(key='-SECONDS-', focus=True, size=(5, 1)),
sg.Button('Do Long Task', bind_return_key=True), sg.Button('Do Long Task', bind_return_key=True),
sg.CBox('ONE chunk, cannot break apart', key='-ONE CHUNK-')], sg.CBox('ONE chunk, cannot break apart', key='-ONE CHUNK-')],
[sg.Text('Work progress'), sg.ProgressBar(total, size=(20, 20), orientation='h', key='-PROG-')], [sg.Text('Work progress'), sg.ProgressBar(100, size=(20, 20), orientation='h', key='-PROG-')],
[sg.Button('Click Me'), sg.Button('Exit')], ] [sg.Button('Click Me'), sg.Button('Exit')], ]
window = sg.Window('Multithreaded Demonstration Window', layout) window = sg.Window('Multithreaded Demonstration Window', layout, finalize=True)
thread = None
timeout = thread = None
# --------------------- EVENT LOOP --------------------- # --------------------- EVENT LOOP ---------------------
while True: while True:
event, values = window.read(timeout=100) event, values = window.read(timeout=timeout)
# print(event, values)
if event in (sg.WIN_CLOSED, 'Exit'): if event in (sg.WIN_CLOSED, 'Exit'):
break break
elif event.startswith('Do') and not thread: elif event.startswith('Do') and not thread:
print('Thread Starting! Long work....sending value of {} seconds'.format(float(values['-SECONDS-']))) print('Thread Starting! Long work....sending value of {} seconds'.format(float(values['-SECONDS-'])))
thread = threading.Thread(target=long_operation_thread, args=(float(values['-SECONDS-']),), daemon=True) timeout = 100 if values['-ONE CHUNK-'] else None
thread = threading.Thread(target=long_operation_thread, args=(float(values['-SECONDS-']),window), daemon=True)
thread.start() thread.start()
if values['-ONE CHUNK-']:
sg.popup_animated(sg.DEFAULT_BASE64_LOADING_GIF, background_color='white', transparent_color='white', time_between_frames=100)
elif event == 'Click Me': elif event == 'Click Me':
print('Your GUI is alive and well') print('Your GUI is alive and well')
elif event == '-PROGRESS-':
if thread: # If thread is running if not values['-ONE CHUNK-']:
if values['-ONE CHUNK-']: # If one big operation, show an animated GIF window['-PROG-'].update_bar(values[event], 100)
sg.popup_animated(sg.DEFAULT_BASE64_LOADING_GIF, background_color='white', transparent_color='white', time_between_frames=100) elif event == '-THREAD-': # Thread has completed
else: # Not one big operation, so update a progress bar instead
window['-PROG-'].update_bar(progress, total)
thread.join(timeout=0) thread.join(timeout=0)
if not thread.is_alive(): # the thread finished print('Thread finished')
print(f'message = {message}') sg.popup_animated(None) # stop animination in case one is running
sg.popup_animated(None) # stop animination in case one is running thread, message, progress, timeout = None, '', 0, None # reset variables for next run
thread, message, progress = None, '', 0 # reset variables for next run window['-PROG-'].update_bar(0,0) # clear the progress bar
window['-PROG-'].update_bar(0,0) # clear the progress bar if values['-ONE CHUNK-'] and thread is not None:
sg.popup_animated(sg.DEFAULT_BASE64_LOADING_GIF, background_color='white', transparent_color='white', time_between_frames=100)
window.close() window.close()

View File

@ -1,11 +1,8 @@
#!/usr/bin/python3 #!/usr/bin/python3
import queue
import threading import threading
import time import time
import PySimpleGUI as sg import PySimpleGUI as sg
# This program has been tested on all flavors of PySimpleGUI and it works with no problems at all
# To try something other than tkinter version, just comment out the first import and uncomment the one you want
""" """
DESIGN PATTERN - Multithreaded Long Tasks GUI DESIGN PATTERN - Multithreaded Long Tasks GUI
@ -13,18 +10,20 @@ import PySimpleGUI as sg
The PySimpleGUI code, and thus the underlying GUI framework, runs as the primary, main thread The PySimpleGUI code, and thus the underlying GUI framework, runs as the primary, main thread
The "long work" is contained in the thread that is being started. The "long work" is contained in the thread that is being started.
A queue.Queue is used by the threads to communicate with main GUI code July 2020 - Note that this program has been updated to use the new Window.write_event_value method.
This method has not yet been ported to the other PySimpleGUI ports and is thus limited to the tkinter ports for now.
Internally to PySimpleGUI, a queue.Queue is used by the threads to communicate with main GUI code
The PySimpleGUI code is structured just like a typical PySimpleGUI program. A layout defined, The PySimpleGUI code is structured just like a typical PySimpleGUI program. A layout defined,
a Window is created, and an event loop is executed. a Window is created, and an event loop is executed.
What's different is that within this otherwise normal PySimpleGUI Event Loop, there is a check for items
in the Queue. If there are items found, process them by making GUI changes, and continue.
This design pattern works for all of the flavors of PySimpleGUI including the Web and also repl.it This design pattern works for all of the flavors of PySimpleGUI including the Web and also repl.it
You'll find a repl.it version here: https://repl.it/@PySimpleGUI/Async-With-Queue-Communicationspy You'll find a repl.it version here: https://repl.it/@PySimpleGUI/Async-With-Queue-Communicationspy
""" """
def long_operation_thread(seconds, gui_queue): def long_operation_thread(seconds, window):
""" """
A worker thread that communicates with the GUI through a queue A worker thread that communicates with the GUI through a queue
This thread can block for as long as it wants and the GUI will not be affected This thread can block for as long as it wants and the GUI will not be affected
@ -34,7 +33,7 @@ def long_operation_thread(seconds, gui_queue):
""" """
print('Starting thread - will sleep for {} seconds'.format(seconds)) print('Starting thread - will sleep for {} seconds'.format(seconds))
time.sleep(seconds) # sleep for a while time.sleep(seconds) # sleep for a while
gui_queue.put('** Done **') # put a message into queue for GUI window.write_event_value('-THREAD-', '** DONE **') # put a message into queue for GUI
def the_gui(): def the_gui():
@ -44,7 +43,6 @@ def the_gui():
Returns when the user exits / closes the window Returns when the user exits / closes the window
""" """
sg.theme('Light Brown 3') sg.theme('Light Brown 3')
gui_queue = queue.Queue() # queue used to communicate between the gui and the threads
layout = [[sg.Text('Long task to perform example')], layout = [[sg.Text('Long task to perform example')],
[sg.Output(size=(70, 12))], [sg.Output(size=(70, 12))],
@ -57,29 +55,17 @@ def the_gui():
# --------------------- EVENT LOOP --------------------- # --------------------- EVENT LOOP ---------------------
while True: while True:
event, values = window.read(timeout=100) event, values = window.read()
if event in (sg.WIN_CLOSED, 'Exit'): if event in (sg.WIN_CLOSED, 'Exit'):
break break
elif event.startswith('Do'): elif event.startswith('Do'):
try: seconds = int(values['-SECONDS-'])
seconds = int(values['-SECONDS-']) print('Thread ALIVE! Long work....sending value of {} seconds'.format(seconds))
print('Thread ALIVE! Long work....sending value of {} seconds'.format(seconds)) threading.Thread(target=long_operation_thread, args=(seconds, window,), daemon=True).start()
threading.Thread(target=long_operation_thread,
args=(seconds, gui_queue,), daemon=True).start()
except Exception as e:
print('Error starting work thread. Bad seconds input: "%s"' %
values['-SECONDS-'])
elif event == 'Click Me': elif event == 'Click Me':
print('Your GUI is alive and well') print('Your GUI is alive and well')
# --------------- Check for incoming messages from threads --------------- elif event == '-THREAD-':
try: print('Got a message back from the thread: ', values[event])
message = gui_queue.get_nowait()
except queue.Empty: # get_nowait() will get exception when Queue is empty
message = None # break from the loop if no more messages are queued up
# if message received from queue, display the message in the Window
if message:
print('Got a message back from the thread: ', message)
# if user exits the window, then close the window and exit the GUI func # if user exits the window, then close the window and exit the GUI func
window.close() window.close()

View File

@ -1,5 +1,4 @@
#!/usr/bin/python3 #!/usr/bin/python3
import queue
import threading import threading
import time import time
import PySimpleGUI as sg import PySimpleGUI as sg
@ -41,13 +40,13 @@ import PySimpleGUI as sg
# Create one of these functions for EVERY long-running call you want to make # Create one of these functions for EVERY long-running call you want to make
def long_function_wrapper(work_id, gui_queue): def long_function_wrapper(work_id, window):
# LOCATION 1 # LOCATION 1
# this is our "long running function call" # this is our "long running function call"
# sleep for a while as a simulation of a long-running computation # sleep for a while as a simulation of a long-running computation
time.sleep(5) time.sleep(5)
# at the end of the work, before exiting, send a message back to the GUI indicating end # at the end of the work, before exiting, send a message back to the GUI indicating end
gui_queue.put('{} ::: done'.format(work_id)) window.write_event_value('-THREAD DONE-', work_id)
# at this point, the thread exits # at this point, the thread exits
return return
@ -56,8 +55,6 @@ def long_function_wrapper(work_id, gui_queue):
def the_gui(): def the_gui():
sg.theme('Light Brown 3') sg.theme('Light Brown 3')
# queue used to communicate between the gui and long-running code
gui_queue = queue.Queue()
layout = [[sg.Text('Multithreaded Work Example')], layout = [[sg.Text('Multithreaded Work Example')],
[sg.Text('Click Go to start a long-running function call')], [sg.Text('Click Go to start a long-running function call')],
@ -71,7 +68,7 @@ def the_gui():
work_id = 0 work_id = 0
while True: while True:
# wait for up to 100 ms for a GUI event # wait for up to 100 ms for a GUI event
event, values = window.read(timeout=100) event, values = window.read()
if event in (sg.WIN_CLOSED, 'Exit'): if event in (sg.WIN_CLOSED, 'Exit'):
break break
if event == 'Go': # clicking "Go" starts a long running work item by starting thread if event == 'Go': # clicking "Go" starts a long running work item by starting thread
@ -81,23 +78,18 @@ def the_gui():
# STARTING long run by starting a thread # STARTING long run by starting a thread
thread_id = threading.Thread( thread_id = threading.Thread(
target=long_function_wrapper, target=long_function_wrapper,
args=(work_id, gui_queue,), args=(work_id, window,),
daemon=True) daemon=True)
thread_id.start() thread_id.start()
work_id = work_id+1 if work_id < 19 else 0 work_id = work_id+1 if work_id < 19 else 0
# --------------- Read next message coming in from threads ---------------
try:
message = gui_queue.get_nowait() # see if something has been posted to Queue
except queue.Empty: # get_nowait() will get exception when Queue is empty
message = None # nothing in queue so do nothing
# if message received from queue, then some work was completed # if message received from queue, then some work was completed
if message is not None: if event == '-THREAD DONE-':
# LOCATION 3 # LOCATION 3
# this is the place you would execute code at ENDING of long running task # this is the place you would execute code at ENDING of long running task
# You can check the completed_work_id variable # You can check the completed_work_id variable
# to see exactly which long-running function completed # to see exactly which long-running function completed
completed_work_id = int(message[:message.index(' :::')]) completed_work_id = values[event]
window['-OUTPUT2-'].update( window['-OUTPUT2-'].update(
'Complete Work ID "{}"'.format(completed_work_id)) 'Complete Work ID "{}"'.format(completed_work_id))
window[completed_work_id].update(text_color='green') window[completed_work_id].update(text_color='green')

View File

@ -1,20 +1,25 @@
import PySimpleGUI as sg
import threading import threading
import time import time
import PySimpleGUI as sg
""" """
Threaded Demo - Uses Window.write_event_value communications Threaded Demo - Uses Window.write_event_value communications
Requires PySimpleGUI.py version 4.24.0.17 Requires PySimpleGUI.py version 4.25.0 and later
Demo of threads using a new way of communicating with threads that is done in a non-polled way. This is a really important demo to understand if you're going to be using multithreading in PySimpleGUI.
No longer do you need to run your event loop with a timeout value in order to multi-thread.
Now you can pend on your read forever and use a special call that threads can call that will add a new item to the queue Older mechanisms for multi-threading in PySimpleGUI relied on polling of a queue. The management of a communications
of items queue is now performed internally to PySimpleGUI.
The importance of using the new window.write_event_value call cannot be emphasized enough. It will hav a HUGE impact, in
a positive way, on your code to move to this mechanism as your code will simply "pend" waiting for an event rather than polling.
Copyright 2020 PySimpleGUI.org
""" """
THREAD_EVENT = '-THEAD-' THREAD_EVENT = '-THREAD-'
def the_thread(window): def the_thread(window):
""" """
@ -25,7 +30,8 @@ def the_thread(window):
i = 0 i = 0
while True: while True:
time.sleep(1) time.sleep(1)
window.write_event_value(THREAD_EVENT, i) sg.cprint(f'thread info (in thread) = {threading.current_thread().name}', c='white on purple')
window.write_event_value('-THREAD-', i)
i += 1 i += 1
@ -47,6 +53,7 @@ def main():
while True: # Event Loop while True: # Event Loop
event, values = window.read() event, values = window.read()
sg.cprint(event, values) sg.cprint(event, values)
sg.cprint(f'thread info = {threading.current_thread().name}')
if event == sg.WIN_CLOSED or event == 'Exit': if event == sg.WIN_CLOSED or event == 'Exit':
break break
if event == 'Start': if event == 'Start':