-
Notifications
You must be signed in to change notification settings - Fork 60
/
Copy pathDemo_Multithreaded_DataPump.py
142 lines (119 loc) · 6.27 KB
/
Demo_Multithreaded_DataPump.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import FreeSimpleGUI 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()