Better commenting and message passing with "Work ID" checks
This commit is contained in:
parent
8c20619b2a
commit
6acdac5a96
|
@ -5,6 +5,11 @@ import time
|
||||||
import PySimpleGUI as sg
|
import PySimpleGUI as sg
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
You want to look for 3 points in this code, marked with comment "LOCATION X".
|
||||||
|
1. Where you put your call that takes a long time
|
||||||
|
2. Where the trigger to make the call takes place in the event loop
|
||||||
|
3. Where the completion of the call is indicated in the event loop
|
||||||
|
|
||||||
Demo on how to add a long-running item to your PySimpleGUI Event Loop
|
Demo on how to add a long-running item to your PySimpleGUI Event Loop
|
||||||
If you want to do something that takes a long time, and you do it in the
|
If you want to do something that takes a long time, and you do it in the
|
||||||
main event loop, you'll quickly begin to see messages from windows that your
|
main event loop, you'll quickly begin to see messages from windows that your
|
||||||
|
@ -17,109 +22,75 @@ import PySimpleGUI as sg
|
||||||
is spun off, allowed to work, and then gets back to the GUI when it's done working
|
is spun off, allowed to work, and then gets back to the GUI when it's done working
|
||||||
on that task.
|
on that task.
|
||||||
|
|
||||||
If you have multiple long tasks to run, then you'll want a more sophisticated
|
Every time you start up one of these long-running functions, you'll give it an "ID".
|
||||||
format to your messages going back to the GUI so you'll know which task finished
|
When the function completes, it will send to the GUI Event Loop a message with
|
||||||
|
the format:
|
||||||
|
work_id ::: done
|
||||||
|
This makes it easy to parse out your original work ID
|
||||||
|
|
||||||
|
You can hard code these IDs to make your code more readable. For example, maybe
|
||||||
|
you have a function named "update_user_list()". You can call the work ID "user list".
|
||||||
|
Then check for the message coming back later from the work task to see if it starts
|
||||||
|
with "user list". If so, then that long-running task is over.
|
||||||
|
|
||||||
You want to look for 3 points in this code.
|
|
||||||
1. Where you put your call that takes a long time
|
|
||||||
2. Where the trigger to make the call takes place in the event loop
|
|
||||||
3. Where the completion of the call is indicated in the event loop
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Put your....
|
# ############################# User callable CPU intensive code #############################
|
||||||
|
# Put your long running code inside this "wrapper"
|
||||||
###### ######## ## ##
|
# NEVER make calls to PySimpleGUI from this thread (or any thread)!
|
||||||
## ## ## ## ## ##
|
# Create one of these functions for EVERY long-running call you want to make
|
||||||
## ## ## ## ##
|
def long_function_wrapper(work_id, gui_queue):
|
||||||
## ######## ## ##
|
print('Thread starting - {} '.format(work_id))
|
||||||
## ## ## ##
|
|
||||||
## ## ## ## ##
|
|
||||||
###### ## #######
|
|
||||||
|
|
||||||
#### ## ## ######## ######## ## ## ###### #### ## ## ########
|
|
||||||
## ### ## ## ## ### ## ## ## ## ## ## ##
|
|
||||||
## #### ## ## ## #### ## ## ## ## ## ##
|
|
||||||
## ## ## ## ## ###### ## ## ## ###### ## ## ## ######
|
|
||||||
## ## #### ## ## ## #### ## ## ## ## ##
|
|
||||||
## ## ### ## ## ## ### ## ## ## ## ## ##
|
|
||||||
#### ## ## ## ######## ## ## ###### #### ### ########
|
|
||||||
|
|
||||||
###### ####### ######## ########
|
|
||||||
## ## ## ## ## ## ##
|
|
||||||
## ## ## ## ## ##
|
|
||||||
## ## ## ## ## ######
|
|
||||||
## ## ## ## ## ##
|
|
||||||
## ## ## ## ## ## ##
|
|
||||||
###### ####### ######## ########
|
|
||||||
|
|
||||||
# Here in this thread
|
|
||||||
|
|
||||||
def worker_thread(thread_name, gui_queue):
|
|
||||||
print('Starting thread - {} '.format(thread_name))
|
|
||||||
# LOCATION 1
|
# LOCATION 1
|
||||||
# this is our "long running function call"
|
# this is our "long running function call"
|
||||||
time.sleep(5) # sleep for a while
|
time.sleep(5) # sleep for a while
|
||||||
print('Ending thread - {} '.format(thread_name))
|
print('Thread Ending - {} '.format(work_id))
|
||||||
|
|
||||||
# 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
|
||||||
# in this case, we're using a simple string
|
gui_queue.put('{} ::: done'.format(work_id))
|
||||||
gui_queue.put('{} - done'.format(thread_name)) # put a message into queue for GUI
|
|
||||||
|
|
||||||
|
|
||||||
######## ## ## ####
|
############################# Begin GUI code #############################
|
||||||
## ## ## ## ##
|
|
||||||
## ## ## ##
|
|
||||||
## #### ## ## ##
|
|
||||||
## ## ## ## ##
|
|
||||||
## ## ## ## ##
|
|
||||||
######## ######### ####
|
|
||||||
|
|
||||||
def the_gui():
|
def the_gui():
|
||||||
gui_queue = queue.Queue() # queue used to communicate between the gui and the threads
|
gui_queue = queue.Queue() # queue used to communicate between the gui and long-running code
|
||||||
|
|
||||||
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('', size=(25, 1), key='_OUTPUT_')],
|
[sg.Text('', size=(25, 1), key='_OUTPUT_')],
|
||||||
# [sg.Output(size=(40,6))],
|
[sg.Text('', size=(25, 1), key='_OUTPUT2_')],
|
||||||
[sg.Button('Go'), sg.Button('Popup'), sg.Button('Exit')], ]
|
[sg.Button('Go'), sg.Button('Popup'), sg.Button('Exit')], ]
|
||||||
|
|
||||||
window = sg.Window('Multithreaded Window').Layout(layout)
|
window = sg.Window('Multithreaded Window').Layout(layout)
|
||||||
# --------------------- EVENT LOOP ---------------------
|
# --------------------- EVENT LOOP ---------------------
|
||||||
count = 0
|
work_id = 0
|
||||||
while True:
|
while True:
|
||||||
event, values = window.Read(timeout=100) # wait for up to 100 ms for a GUI event
|
event, values = window.Read(timeout=100) # wait for up to 100 ms for a GUI event
|
||||||
if event is None or event == 'Exit':
|
if event is None or event == '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
|
||||||
window.Element('_OUTPUT_').Update('Starting long work %s'%count)
|
window.Element('_OUTPUT_').Update('Starting long work %s'%work_id)
|
||||||
# LOCATION 2
|
# LOCATION 2
|
||||||
# STARTING long run by starting a thread
|
# STARTING long run by starting a thread
|
||||||
threading.Thread(target=worker_thread, args=('Thread %s'%count, gui_queue,), daemon=True).start()
|
threading.Thread(target=long_function_wrapper, args=(work_id, gui_queue,), daemon=True).start()
|
||||||
count += 1
|
work_id += 1
|
||||||
# --------------- Read next message coming in from threads ---------------
|
# --------------- Read next message coming in from threads ---------------
|
||||||
try:
|
try:
|
||||||
message = gui_queue.get_nowait() # see if something has been posted to Queue
|
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
|
except queue.Empty: # get_nowait() will get exception when Queue is empty
|
||||||
message = None # nothing in queue so do nothing
|
message = None # nothing in queue so do nothing
|
||||||
|
|
||||||
# if message received from queue, display the message in the Window
|
# if message received from queue, then some work was completed
|
||||||
if message is not None:
|
if message is not None:
|
||||||
# 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
|
||||||
window.Element('_OUTPUT_').Update(message)
|
# You can check the completed_work_id variable to see exactly which long-running function completed
|
||||||
|
completed_work_id = message[:message.index(' :::')]
|
||||||
|
window.Element('_OUTPUT2_').Update('Complete Work ID "{}"'.format(completed_work_id))
|
||||||
if event == 'Popup':
|
if event == 'Popup':
|
||||||
sg.Popup('This is a popup showing that the GUI is running')
|
sg.Popup('This is a popup showing that the GUI is running')
|
||||||
# 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()
|
||||||
|
|
||||||
|
############################# Main #############################
|
||||||
## ## ### #### ## ##
|
|
||||||
### ### ## ## ## ### ##
|
|
||||||
#### #### ## ## ## #### ##
|
|
||||||
## ### ## ## ## ## ## ## ##
|
|
||||||
## ## ######### ## ## ####
|
|
||||||
## ## ## ## ## ## ###
|
|
||||||
## ## ## ## #### ## ##
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
the_gui()
|
the_gui()
|
||||||
|
|
Loading…
Reference in New Issue