143 lines
6.3 KiB
Python
143 lines
6.3 KiB
Python
|
import PySimpleGUI as sg
|
||
|
import random
|
||
|
import time
|
||
|
import queue
|
||
|
|
||
|
"""
|
||
|
Demo - Multi-threaded "Data Pump" Design Pattern
|
||
|
|
||
|
Send data to your PySimpleGUI program through a Python Queue, enabling integration with many
|
||
|
different types of data sources.
|
||
|
|
||
|
A thread gets data from a queue object and passes it over to the main event loop.
|
||
|
The external_thread is only used here to generaate random data. It's not part of the
|
||
|
overall "Design Pattern".
|
||
|
|
||
|
The thread the_thread IS part of the design pattern. It reads data from the thread_queue and sends that
|
||
|
data over to the PySimpleGUI event loop.
|
||
|
|
||
|
Copyright 2022 PySimpleGUI
|
||
|
"""
|
||
|
|
||
|
gsize = (400, 400) # size of the graph
|
||
|
|
||
|
THREAD_KEY = '-THREAD-'
|
||
|
THREAD_INCOMING_DATA = '-INCOMING DATA-'
|
||
|
THREAD_EXITNG = '-THREAD EXITING-'
|
||
|
THREAD_EXTERNAL_EXITNG = '-EXTERNAL THREAD EXITING-'
|
||
|
|
||
|
# This queue is where you will send your data that you want to eventually arrive as an event
|
||
|
thread_queue = queue.Queue()
|
||
|
|
||
|
# M""""""""M dP dP
|
||
|
# Mmmm mmmM 88 88
|
||
|
# MMMM MMMM 88d888b. 88d888b. .d8888b. .d8888b. .d888b88
|
||
|
# MMMM MMMM 88' `88 88' `88 88ooood8 88' `88 88' `88
|
||
|
# MMMM MMMM 88 88 88 88. ... 88. .88 88. .88
|
||
|
# MMMM MMMM dP dP dP `88888P' `88888P8 `88888P8
|
||
|
# MMMMMMMMMM
|
||
|
#
|
||
|
# MP""""""`MM oo dP dP oo
|
||
|
# M mmmmm..M 88 88
|
||
|
# M. `YM dP 88d8b.d8b. dP dP 88 .d8888b. d8888P dP 88d888b. .d8888b.
|
||
|
# MMMMMMM. M 88 88'`88'`88 88 88 88 88' `88 88 88 88' `88 88' `88
|
||
|
# M. .MMM' M 88 88 88 88 88. .88 88 88. .88 88 88 88 88 88. .88
|
||
|
# Mb. .dM dP dP dP dP `88888P' dP `88888P8 dP dP dP dP `8888P88
|
||
|
# MMMMMMMMMMM .88
|
||
|
# d8888P
|
||
|
# M""""""'YMM dP MP""""""`MM
|
||
|
# M mmmm. `M 88 M mmmmm..M
|
||
|
# M MMMMM M .d8888b. d8888P .d8888b. M. `YM .d8888b. dP dP 88d888b. .d8888b. .d8888b.
|
||
|
# M MMMMM M 88' `88 88 88' `88 MMMMMMM. M 88' `88 88 88 88' `88 88' `"" 88ooood8
|
||
|
# M MMMM' .M 88. .88 88 88. .88 M. .MMM' M 88. .88 88. .88 88 88. ... 88. ...
|
||
|
# M .MM `88888P8 dP `88888P8 Mb. .dM `88888P' `88888P' dP `88888P' `88888P'
|
||
|
# MMMMMMMMMMM MMMMMMMMMMM
|
||
|
#
|
||
|
|
||
|
def external_thread(thread_queue:queue.Queue):
|
||
|
"""
|
||
|
Represents some external source of data.
|
||
|
You would not include this code as a starting point with this Demo Program. Your data is assumed to
|
||
|
come from somewhere else. The important part is that you add data to the thread_queue
|
||
|
:param thread_queue:
|
||
|
:return:
|
||
|
"""
|
||
|
i = 0
|
||
|
while True:
|
||
|
time.sleep(.01)
|
||
|
point = (random.randint(0,gsize[0]), random.randint(0,gsize[1]))
|
||
|
radius = random.randint(10, 40)
|
||
|
thread_queue.put((point, radius))
|
||
|
i += 1
|
||
|
|
||
|
|
||
|
# M""""""""M dP dP MM""""""""`M
|
||
|
# Mmmm mmmM 88 88 MM mmmmmmmM
|
||
|
# MMMM MMMM 88d888b. 88d888b. .d8888b. .d8888b. .d888b88 M' MMMM .d8888b. 88d888b.
|
||
|
# MMMM MMMM 88' `88 88' `88 88ooood8 88' `88 88' `88 MM MMMMMMMM 88' `88 88' `88
|
||
|
# MMMM MMMM 88 88 88 88. ... 88. .88 88. .88 MM MMMMMMMM 88. .88 88
|
||
|
# MMMM MMMM dP dP dP `88888P' `88888P8 `88888P8 MM MMMMMMMM `88888P' dP
|
||
|
# MMMMMMMMMM MMMMMMMMMMMM
|
||
|
#
|
||
|
# MM"""""""`YM MP""""""`MM MM'"""""`MM MM""""""""`M dP
|
||
|
# MM mmmmm M M mmmmm..M M' .mmm. `M MM mmmmmmmM 88
|
||
|
# M' .M M. `YM M MMMMMMMM M` MMMM dP .dP .d8888b. 88d888b. d8888P .d8888b.
|
||
|
# MM MMMMMMMM MMMMMMM. M M MMM `M MM MMMMMMMM 88 d8' 88ooood8 88' `88 88 Y8ooooo.
|
||
|
# MM MMMMMMMM M. .MMM' M M. `MMM' .M MM MMMMMMMM 88 .88' 88. ... 88 88 88 88
|
||
|
# MM MMMMMMMM Mb. .dM MM. .MM MM .M 8888P' `88888P' dP dP dP `88888P'
|
||
|
# MMMMMMMMMMMM MMMMMMMMMMM MMMMMMMMMMM MMMMMMMMMMMM
|
||
|
|
||
|
|
||
|
def the_thread(window:sg.Window, thread_queue:queue.Queue):
|
||
|
"""
|
||
|
The thread that communicates with the application through the window's events.
|
||
|
Waits for data from a queue and sends that data on to the event loop
|
||
|
:param window:
|
||
|
:param thread_queue:
|
||
|
:return:
|
||
|
"""
|
||
|
|
||
|
while True:
|
||
|
data = thread_queue.get()
|
||
|
window.write_event_value((THREAD_KEY, THREAD_INCOMING_DATA), data) # Data sent is a tuple of thread name and counter
|
||
|
|
||
|
|
||
|
def main():
|
||
|
|
||
|
layout = [ [sg.Text('My Simulated Data Pump')],
|
||
|
[sg.Multiline(size=(60, 20), k='-MLINE-')],
|
||
|
[sg.Graph(gsize, (0, 0), gsize, k='-G-', background_color='gray')],
|
||
|
[sg.Button('Go'), sg.Button('Exit')] ]
|
||
|
|
||
|
window = sg.Window('Simulated Data Pump', layout, right_click_menu=sg.MENU_RIGHT_CLICK_EDITME_VER_EXIT)
|
||
|
|
||
|
graph = window['-G-'] # type: sg.Graph
|
||
|
|
||
|
while True: # Event Loop
|
||
|
event, values = window.read()
|
||
|
# print(event, values)
|
||
|
if event == sg.WIN_CLOSED or event == 'Exit':
|
||
|
break
|
||
|
if event == 'Go':
|
||
|
window.start_thread(lambda: the_thread(window, thread_queue), (THREAD_KEY, THREAD_EXITNG))
|
||
|
window.start_thread(lambda: external_thread(thread_queue), (THREAD_KEY, THREAD_EXTERNAL_EXITNG))
|
||
|
# Events coming from the Thread
|
||
|
elif event[0] == THREAD_KEY:
|
||
|
if event[1] == THREAD_INCOMING_DATA:
|
||
|
point, radius = values[event]
|
||
|
graph.draw_circle(point, radius=radius, fill_color='green')
|
||
|
window['-MLINE-'].print(f'Drawing at {point} radius {radius}', c='white on red')
|
||
|
elif event[1] == THREAD_EXITNG:
|
||
|
window['-MLINE-'].print('Thread has exited')
|
||
|
elif event[1] == THREAD_EXTERNAL_EXITNG:
|
||
|
window['-MLINE-'].print('Data Pump thread has exited')
|
||
|
if event == 'Edit Me':
|
||
|
sg.execute_editor(__file__)
|
||
|
elif event == 'Version':
|
||
|
sg.popup_scrolled(__file__, sg.get_versions(), location=window.current_location(), keep_on_top=True, non_blocking=True)
|
||
|
|
||
|
window.close()
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
main()
|