Changed multi-threaded demos to use new Window.write_event_value method
This commit is contained in:
parent
934cea5881
commit
04dfa364d5
6 changed files with 109 additions and 139 deletions
|
@ -1,10 +1,4 @@
|
|||
#!/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 time
|
||||
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
|
||||
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,
|
||||
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
|
||||
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
|
||||
:param thread_name: Text name used for displaying info
|
||||
: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:
|
||||
"""
|
||||
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
|
||||
time.sleep(run_freq/1000) # sleep for a while
|
||||
# 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
|
||||
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
|
||||
:param thread_name: Text name used for displaying info
|
||||
: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:
|
||||
"""
|
||||
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
|
||||
time.sleep(run_freq/1000) # sleep for a while
|
||||
# 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
|
||||
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
|
||||
:param thread_name: Text name used for displaying info
|
||||
: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:
|
||||
"""
|
||||
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
|
||||
time.sleep(run_freq/1000) # sleep for a while
|
||||
# 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
|
||||
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')],
|
||||
[sg.Text('', size=(15, 1), key='-OUTPUT-')],
|
||||
[sg.Output(size=(40, 6))],
|
||||
[sg.Multiline(size=(40, 26), key='-ML-', autoscroll=True)],
|
||||
[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 ---------------------
|
||||
while True:
|
||||
# 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'):
|
||||
break
|
||||
# --------------- Loop through all messages coming in from threads ---------------
|
||||
while True: # loop executes until runs out of messages in Queue
|
||||
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)
|
||||
sg.cprint(event, values[event], c=colors[event])
|
||||
# if user exits the window, then close the window and exit the GUI func
|
||||
window.close()
|
||||
|
||||
|
@ -142,16 +140,5 @@ def the_gui(gui_queue):
|
|||
## ## ## ## #### ## ##
|
||||
|
||||
if __name__ == '__main__':
|
||||
# -- Create a Queue to communicate with 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')
|
||||
the_gui()
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue