#Reference for Threading and GUI architecture: https://maldus512.medium.com/how-to-setup-correctly-an-application-with-python-and-tkinter-107c6bc5a45 import queue import tkinter as tk 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.insert(0,"Valor") guiRef.entry["state"] = 'disabled' btInterface.send_data("Alarma HR") #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) #FRAME 2 - GRAPH 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') #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!") #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() btInterface.send_data("Inicio...") #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 app.mainloop()