Newer
Older
#Reference for Threading and GUI architecture: https://maldus512.medium.com/how-to-setup-correctly-an-application-with-python-and-tkinter-107c6bc5a45
import queue
import matplotlib.pyplot as plt
import numpy as np
from tkinter import TOP, BOTH, X, LEFT, RIGHT
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from types import SimpleNamespace
from enum import Enum
from threading import Thread
from hardware_interface import Hardware_interface
TIME_OUT = 0.2 #Time out used for refreshing the User Interface
class Messages(Enum):
HRSETALARM = 0
#Method in charge of updating the UI. It is called from another thread and uses queue for communication
def refreshUI(guiRef, model, guiQueue):
msg = None
while True:
try:
msg = guiQueue.get(timeout = TIME_OUT)
if msg == Messages.HRSETALARM:
model.count += 1
guiRef.entry["state"] = 'normal'
guiRef.entry.delete(0,"end")
guiRef.entry["state"] = 'disabled'
#guiRef.entry_two.delete(0,"end")
#guiRef.entry_two.insert(0,"Prueba")
except queue.Empty:
pass
#Other tasks...
#print("Han pasado 200 milisegundos")
#Getting data from Bluetooth
dataAvailable, data = btInterface.get_data()
if(dataAvailable == True):
guiRef.entry_two.delete(0,"end")
guiRef.entry_two.insert(0, data)
#Class for the User Interface
class Application(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.master = master
#self.master.geometry('1000x1000')
self.master.title('Smart Watch')
self.pack(fill=BOTH, expand=True)
self.create_widgets()
def create_widgets(self):
#FRAME 1
self.frame_one = tk.Frame(self.master)
self.frame_one.pack(fill=X)
self.label_title = tk.Label(self.frame_one, text = "Smart Watch")
self.label_title.pack(side=LEFT)
self.frame_two = tk.Frame(self.master)
self.frame_two.pack(fill=X)
figure = plt.Figure(figsize=(6,5), dpi=100)
ax = figure.add_subplot(111)
chart_type = FigureCanvasTkAgg(figure, self.frame_two)
chart_type.get_tk_widget().pack()
x = np.arange(10)
y = 2.5 * np.sin(x / 20 * np.pi)
ax.plot(x, y, color='tab:blue')
ax.set_xlim([0, 10])
ax.set_ylim([-1, 1])
ax.set_title('PPG - RED')
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
#FRAME 3
self.frame_three = tk.Frame(self.master)
self.frame_three.pack(fill=X)
self.label_subtitle_two = tk.Label(self.frame_three, text = "Valores Instantáneos")
self.label_subtitle_two.pack(side=LEFT)
#FRAME 4
self.frame_four = tk.Frame(self.master)
self.frame_four.pack(fill=X)
self.label_heartrate = tk.Label(self.frame_four, text = "Heart Rate")
self.label_heartrate.pack(side=LEFT)
#TODO Entry must be enabled before updating it
self.entry_heartrate = tk.Entry(self.frame_four, state='disabled')
self.entry_heartrate.pack(side=LEFT)
self.label_heartrate_units_one = tk.Label(self.frame_four, text = "bpm")
self.label_heartrate_units_one.pack(side=LEFT)
self.label_heartrate_alarm = tk.Label(self.frame_four, text = "ALARMA")
self.label_heartrate_alarm.pack(side=LEFT)
self.label_heartrate_minimum = tk.Label(self.frame_four, text = "Mínimo")
self.label_heartrate_minimum.pack(side=LEFT)
self.entry_heartrate_minimum = tk.Entry(self.frame_four)
self.entry_heartrate_minimum.pack(side=LEFT)
self.label_heartrate_units_two = tk.Label(self.frame_four, text = "bpm")
self.label_heartrate_units_two.pack(side=LEFT)
self.label_heartrate_maximum = tk.Label(self.frame_four, text = "Máximo")
self.label_heartrate_maximum.pack(side=LEFT)
self.entry_heartrate_maximum = tk.Entry(self.frame_four)
self.entry_heartrate_maximum.pack(side=LEFT)
self.label_units_heartrate_three = tk.Label(self.frame_four, text = "bpm")
self.label_units_heartrate_three.pack(side=LEFT)
self.button_heartrate = tk.Button(self.frame_four, text = "Configurar alarma")
#self.button_heartrate = tk.Button(self.frame_four, text = "Configurar alarma", command = self.configure_heartrate_alarm)
self.button_heartrate.pack(side=LEFT)
#FRAME 5
self.frame_five = tk.Frame(self.master)
self.frame_five.pack(fill=X)
self.quit = tk.Button(self.frame_five, text="QUIT", fg="red", command=self.master.destroy)
self.quit.pack(side=RIGHT)
def configure_heartrate_alarm(self):
self.entry_heartrate["state"] = 'normal'
self.entry_heartrate.delete(0,"end")
self.entry_heartrate.insert(0,"88")
self.entry_heartrate["state"] = 'disabled'
print("Alarma HR!")
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
#Method that provides reference to the user interface. It uses a queue to communicate the UI with the exterior. It returns references to
#inner objetcs so the exterior can communicate with it. More references can be given in "SimpleNameSpace"
def gui_reference(self, root, queue):
entry = self.entry_heartrate
entry["state"] = 'normal'
entry.delete(0,"end")
entry.insert(0,"10")
entry["state"] = 'disabled'
self.button_heartrate["command"] = lambda : queue.put(Messages.HRSETALARM)
#Just for testing purposes to emit reference to another widget
entry_two = self.entry_heartrate_minimum
return SimpleNamespace(entry=entry, entry_two = entry_two)
if __name__ == '__main__':
root = tk.Tk()
app = Application(master=root)
#Queue used to communicate the UI with the exterior
guiQueue = queue.Queue()
guiRef = app.gui_reference(root, guiQueue)
model = SimpleNamespace(count=0)
#Bluetooth object for communication with MSP432
btInterface = Hardware_interface()
#btInterface.print_device_list()
#btInterface.start_server()
btInterface.start_client()
#Thread used to separate the use interface with the front end. It is used to not slow down the GUI when processing information
t = Thread(target=refreshUI, args=(guiRef, model, guiQueue,))
t.daemon = True
t.start()
#Uncomment to show the GUI