Merge pull request #2676 from PySimpleGUI/Dev-latest
Simplified the code. Removed need for at a flag. Shortened quite a bit.
This commit is contained in:
commit
337ba2bc17
|
@ -5,42 +5,38 @@ import PySimpleGUI as sg
|
||||||
|
|
||||||
"""
|
"""
|
||||||
DESIGN PATTERN - Multithreaded Long Tasks GUI using shared global variables
|
DESIGN PATTERN - Multithreaded Long Tasks GUI using shared global variables
|
||||||
|
|
||||||
Presents one method for running long-running operations in a PySimpleGUI environment.
|
Presents one method for running long-running operations in a PySimpleGUI environment.
|
||||||
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. Communicating is done (carefully) using global variables
|
The "long work" is contained in the thread that is being started. Communicating is done (carefully) using global variables
|
||||||
|
|
||||||
There are 2 ways "progress" is being reported to the user. If a the amount of time is known ahead of time and
|
There are 2 ways "progress" is being reported to the user.
|
||||||
the work can be broek down into countable units, then a progress bar is used. If a task is one long chunk
|
You can simulate the 2 different scenarios that happen with worker threads.
|
||||||
of time that cannot be broken down into smaller units, then an animated GIF is shown that spins as
|
1. If a the amount of time is known ahead of time or the work can be broken down into countable units, then a progress bar is used.
|
||||||
long as the task is running.
|
2. If a task is one long chunk of time that cannot be broken down into smaller units, then an animated GIF is shown that spins as
|
||||||
|
long as the task is running.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
thread_done = 1
|
total = 100 # number of units that are used with the progress bar
|
||||||
message = ''
|
message = '' # used by thread to send back a message to the main thread
|
||||||
thread_counter = 0
|
progress = 0 # current progress up to a maximum of "total"
|
||||||
total = 100
|
|
||||||
progress = 0
|
|
||||||
|
|
||||||
def long_operation_thread(seconds):
|
def long_operation_thread(seconds):
|
||||||
"""
|
"""
|
||||||
A worker thread that communicates with the GUI through a global 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
|
||||||
:return:
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
global thread_done, message, thread_counter, progress
|
global message, progress
|
||||||
|
|
||||||
print('Thread started - will sleep for {} seconds'.format(seconds))
|
print('Thread started - will sleep for {} seconds'.format(seconds))
|
||||||
thread_counter += 1
|
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 += total/(seconds*10)
|
|
||||||
|
|
||||||
message = f'***This is a message from the thread {thread_counter} ***'
|
|
||||||
thread_done = True
|
|
||||||
|
|
||||||
|
message = f'*** The thread says.... "I am finished" ***'
|
||||||
|
|
||||||
def the_gui():
|
def the_gui():
|
||||||
"""
|
"""
|
||||||
|
@ -48,53 +44,50 @@ 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 thread_done, message, progress
|
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.Output(size=(80, 12))],
|
||||||
[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-', 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(total, 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 Window', layout)
|
window = sg.Window('Multithreaded Demonstration Window', layout)
|
||||||
|
|
||||||
one_chunk = False
|
thread = None
|
||||||
sg.popup_animated(None)
|
|
||||||
|
|
||||||
# --------------------- EVENT LOOP ---------------------
|
# --------------------- EVENT LOOP ---------------------
|
||||||
while True:
|
while True:
|
||||||
event, values = window.read(timeout=100)
|
event, values = window.read(timeout=100)
|
||||||
if event in (None, 'Exit'):
|
if event in (None, 'Exit'):
|
||||||
break
|
break
|
||||||
elif event.startswith('Do'):
|
elif event.startswith('Do') and not thread:
|
||||||
seconds = float(values['-SECONDS-'])
|
print('Thread Starting! Long work....sending value of {} seconds'.format(float(values['-SECONDS-'])))
|
||||||
one_chunk = values['-ONE CHUNK-']
|
thread = threading.Thread(target=long_operation_thread, args=(float(values['-SECONDS-']),), daemon=True)
|
||||||
print('Thread Starting! Long work....sending value of {} seconds'.format(seconds))
|
thread.start()
|
||||||
threading.Thread(target=long_operation_thread, args=(seconds, ), daemon=True).start()
|
|
||||||
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 ---------------
|
|
||||||
if thread_done is True:
|
|
||||||
print('The thread has finished!')
|
|
||||||
print(f'message = {message}')
|
|
||||||
# reset everything for the next run
|
|
||||||
sg.popup_animated(None) # just in case one's running, stop it
|
|
||||||
thread_done = one_chunk = False
|
|
||||||
message, progress = '', 0
|
|
||||||
window['-PROG-'].update_bar(total, total) # show the bar as maxed out
|
|
||||||
if one_chunk: # flag signifies the thread is taking to take one big chunk of time
|
|
||||||
sg.popup_animated(sg.DEFAULT_BASE64_LOADING_GIF, background_color='white', transparent_color='white', time_between_frames=100)
|
|
||||||
elif progress != 0:
|
|
||||||
window['-PROG-'].update_bar(progress, total) # update the progress bar if non-zero
|
|
||||||
|
|
||||||
# if user exits the window, then close the window and exit the GUI func
|
if thread: # If thread is running
|
||||||
|
if values['-ONE CHUNK-']: # If one big operation, show an animated GIF
|
||||||
|
sg.popup_animated(sg.DEFAULT_BASE64_LOADING_GIF, background_color='white', transparent_color='white', time_between_frames=100)
|
||||||
|
else: # Not one big operation, so update a progress bar instead
|
||||||
|
window['-PROG-'].update_bar(progress, total)
|
||||||
|
thread.join(timeout=0)
|
||||||
|
if not thread.is_alive(): # the thread finished
|
||||||
|
print(f'message = {message}')
|
||||||
|
sg.popup_animated(None) # stop animination in case one is running
|
||||||
|
thread, message, progress = None, '', 0 # reset variables for next run
|
||||||
|
window['-PROG-'].update_bar(0,0) # clear the progress bar
|
||||||
|
|
||||||
window.close()
|
window.close()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
the_gui()
|
the_gui()
|
||||||
print('Exiting Program')
|
print('Exiting Program')
|
||||||
|
|
Loading…
Reference in New Issue