Added tons of comments, new itertools use, uses 3 threads now
This commit is contained in:
parent
1b88993244
commit
bed31c7580
|
@ -1,8 +1,20 @@
|
||||||
from queue import Queue
|
#!/usr/bin/python3
|
||||||
from threading import Thread
|
|
||||||
from time import sleep
|
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# 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
|
||||||
import PySimpleGUI as sg
|
import PySimpleGUI as sg
|
||||||
|
# import PySimpleGUIQt as sg
|
||||||
|
# import PySimpleGUIWx as sg
|
||||||
|
# import PySimpleGUIWeb as sg
|
||||||
|
|
||||||
"""
|
"""
|
||||||
DESIGN PATTERN - Multithreaded GUI
|
DESIGN PATTERN - Multithreaded GUI
|
||||||
|
@ -21,60 +33,87 @@ import PySimpleGUI as sg
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
######## ## ## ######## ######## ### ########
|
||||||
|
## ## ## ## ## ## ## ## ## ##
|
||||||
|
## ## ## ## ## ## ## ## ## ##
|
||||||
|
## ######### ######## ###### ## ## ## ##
|
||||||
|
## ## ## ## ## ## ######### ## ##
|
||||||
|
## ## ## ## ## ## ## ## ## ##
|
||||||
|
## ## ## ## ## ######## ## ## ########
|
||||||
|
|
||||||
def worker_thread(thread_name, run_freq, gui_queue):
|
def worker_thread(thread_name, run_freq, gui_queue):
|
||||||
"""
|
"""
|
||||||
A worker thrread that communicates with the GUI
|
A worker thrread that communicates with the GUI
|
||||||
These threads can call functions that block withouth affecting the GUI (a good thing)
|
These threads can call functions that block withouth 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 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 gui_queue: Queue used to communicate with the GUI
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
print('Starting thread - ', thread_name)
|
print('Starting thread - {} that runds every {} ms'.format(thread_name, run_freq))
|
||||||
i = 0
|
for i in itertools.count(): # loop forever, keeping count in i as it loops
|
||||||
while True:
|
time.sleep(run_freq/1000) # sleep for a while
|
||||||
sleep(run_freq/1000)
|
gui_queue.put('{} - {}'.format(thread_name, i)) # put a message into queue for GUI
|
||||||
gui_queue.put('{} - {}'.format(thread_name, i))
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
|
|
||||||
|
###### ## ## ####
|
||||||
|
## ## ## ## ##
|
||||||
|
## ## ## ##
|
||||||
|
## #### ## ## ##
|
||||||
|
## ## ## ## ##
|
||||||
|
## ## ## ## ##
|
||||||
|
###### ####### ####
|
||||||
|
|
||||||
def the_gui(gui_queue):
|
def the_gui(gui_queue):
|
||||||
"""
|
"""
|
||||||
starts and executes the GUI. Returns when the user exits / closes the window
|
Starts and executes the GUI
|
||||||
reads data from a Queue and displays the data
|
Reads data from a Queue and displays the data to the window
|
||||||
|
Returns when the user exits / closes the window
|
||||||
|
(that means it does NOT return until the user exits the window)
|
||||||
:param gui_queue: Queue the GUI should read from
|
:param gui_queue: Queue the GUI should read from
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
layout = [ [sg.Text('Your GUI Window')],
|
layout = [ [sg.Text('Multithreaded Window Example')],
|
||||||
[sg.Text('', size=(15,1), key='_OUTPUT_')],
|
[sg.Text('', size=(15,1), key='_OUTPUT_')],
|
||||||
[sg.Button('Exit')],]
|
[sg.Button('Exit')],]
|
||||||
|
|
||||||
window = sg.Window('Window Title').Layout(layout)
|
window = sg.Window('Multithreaded Window').Layout(layout)
|
||||||
|
# --------------------- EVENT LOOP ---------------------
|
||||||
while True: # Event Loop
|
while True:
|
||||||
event, values = window.Read(timeout=100) # wait for up to 200 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
|
||||||
#--------------- Handle stuff coming in from threads ---------------
|
#--------------- Loop through all messages coming in from threads ---------------
|
||||||
while True: # loop executes until runs out of messages in Queue
|
while True: # loop executes until runs out of messages in Queue
|
||||||
try: # see if something has been posted to Queue
|
try: # see if something has been posted to Queue
|
||||||
message = gui_queue.get_nowait()
|
message = gui_queue.get_nowait()
|
||||||
except: # will get exception when Queue is empty
|
except queue.Empty: # get_nowait() will get exception when Queue is empty
|
||||||
break # break from the loop if no more messages are queued up
|
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 received from queue, display the message in the Window
|
||||||
if message:
|
if message:
|
||||||
window.Element('_OUTPUT_').Update(message)
|
window.Element('_OUTPUT_').Update(message)
|
||||||
window.Refresh()
|
window.Refresh() # do a refresh because could be showing multiple messages before next Read
|
||||||
|
|
||||||
# if user exits the window, then close the window and exit
|
# if user exits the window, then close the window and exit the GUI func
|
||||||
window.Close()
|
window.Close()
|
||||||
|
|
||||||
|
|
||||||
|
## ## ### #### ## ##
|
||||||
|
### ### ## ## ## ### ##
|
||||||
|
#### #### ## ## ## #### ##
|
||||||
|
## ### ## ## ## ## ## ## ##
|
||||||
|
## ## ######### ## ## ####
|
||||||
|
## ## ## ## ## ## ###
|
||||||
|
## ## ## ## #### ## ##
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
#-- Create a Queue to communicate with GUI --
|
#-- Create a Queue to communicate with GUI --
|
||||||
gui_queue = Queue() # queue used to communicate between the gui and the worker
|
gui_queue = queue.Queue() # queue used to communicate between the gui and the threads
|
||||||
#-- Start worker threads, one runs twice as often as the other
|
#-- Start worker threads, one runs twice as often as the other
|
||||||
Thread(target=worker_thread, args=('Thread 1', 500, gui_queue,), daemon=True).start()
|
threading.Thread(target=worker_thread, args=('Thread 1', 500, gui_queue,), daemon=True).start()
|
||||||
Thread(target=worker_thread, args=('Thread 2', 200, gui_queue,), daemon=True).start()
|
threading.Thread(target=worker_thread, args=('Thread 2', 200, gui_queue,), daemon=True).start()
|
||||||
|
threading.Thread(target=worker_thread, args=('Thread 3', 1000, gui_queue,), daemon=True).start()
|
||||||
#-- Start the GUI passing in the Queue --
|
#-- Start the GUI passing in the Queue --
|
||||||
the_gui(gui_queue)
|
the_gui(gui_queue)
|
||||||
|
print('Exiting Program')
|
Loading…
Reference in New Issue