May-30-2021, 11:37 AM
I have made it so that there is a variable to set the number of voltage panels
In the
NUMBER_OF_VOLTAGE_PANELS
it uses this to automatically alter the GUI to have the right amount of widgets to display the data, it defaults to having at least 1 so the code won't break.In the
App
update
method you will need to alter the following lines to set the actual data from the xbee.self.data.set_date('30/05/2021') self.data.set_time('12:00') self.data.set_voltages((0.12, 3.8, 1.3))
self.data.set_voltages
takes a tuple of length to match the NUMBER_OF_VOLTAGE_PANELS
import dataclasses import functools import statistics import tkinter as tk from concurrent import futures from digi.xbee.devices import XBeeDevice PORT = "COM5" BAUD_RATE = 9600 device = XBeeDevice(PORT, BAUD_RATE) NUMBER_OF_VOLTAGE_PANELS = 3 DATE = 'Fecha de mediciĆ³n' TIME = 'Hora de mediciĆ³n' VOLTAGE = 'Voltage de Panel' AVERAGE = 'Promedio' LOWEST = 'Panel con Menor Voltaje' @dataclasses.dataclass class FloatData: label: str value: float @dataclasses.dataclass class StringData: label: str value: str @dataclasses.dataclass class Data: date: StringData = dataclasses.field( default=StringData(DATE, ''), init=False) time: StringData = dataclasses.field( default=StringData(TIME, ''), init=False) voltages: list[FloatData] = dataclasses.field( default_factory=list, init=False) average: FloatData = dataclasses.field( default=FloatData(AVERAGE, 0), init=False) lowest: FloatData = dataclasses.field( default=FloatData(LOWEST, 0), init=False) def __post_init__(self): end_range = max(2, NUMBER_OF_VOLTAGE_PANELS+1) for number in range(1, end_range): self.voltages.append(FloatData(f'{VOLTAGE} {number}', 0)) def set_date(self, date: str): self.date.value = date def set_time(self, time: str): self.time.value = time def set_voltages(self, voltages: tuple[float]): for float_data, voltage in zip(self.voltages, voltages): float_data.value = voltage self.calculations() def calculations(self): volatages = [float_data.value for float_data in self.voltages] self.average.value = round(statistics.mean(volatages), 2) self.lowest.value = min(volatages) thread_pool_executor = futures.ThreadPoolExecutor(max_workers=1) def tk_after(target): @functools.wraps(target) def wrapper(self, *args, **kwargs): args = (self,) + args self.after(0, target, *args, **kwargs) return wrapper def submit_to_pool_executor(executor): '''Decorates a method to be sumbited to the passed in executor''' def decorator(target): @functools.wraps(target) def wrapper(*args, **kwargs): result = executor.submit(target, *args, **kwargs) result.add_done_callback(executor_done_call_back) return result return wrapper return decorator def executor_done_call_back(future): exception = future.exception() if exception: raise exception class App(tk.Tk): def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self.title("Sistema de Monitoreo de Paneles Solares del SESLab") self.config(bg="#919F89") self.data = Data() width_of_window = 600 height_of_window = 280 screen_width = self.winfo_screenwidth() screen_height = self.winfo_screenheight() x_coordinate = (screen_width/2)-(width_of_window/2) y_coordinate = (screen_height/2)-(height_of_window/2) self.geometry((f'{width_of_window}x{height_of_window}+' f'{x_coordinate:.0f}+{y_coordinate:.0f}')) self.form_frame = FormFrame(self.data, self) self.form_frame.pack() self.btn = tk.Button( self, bg="#E2BBAC", text="Iniciar ", command=self.on_btn) self.btn.pack() @tk_after def btn_enable(self, enable: bool = True): self.btn.config(state='normal' if enable else 'disabled') def on_btn(self): self.btn_enable(False) self.update() @submit_to_pool_executor(thread_pool_executor) def update(self): device.open() DATA_TO_SEND = "Hola XBee!" device.send_data_broadcast(DATA_TO_SEND) try: device.flush_queues() while True: xbee_message = device.read_data() if xbee_message is not None: data = xbee_message.data.decode() self.data.set_date('30/05/2021') self.data.set_time('12:00') self.data.set_voltages((0.12, 3.8, 1.3)) self.form_frame.update() finally: if device is not None and device.is_open(): device.close() self.btn_enable() class FormFrame(tk.Frame): def __init__(self, data: Data, *args, **kwargs): self.data = data kwargs['width'] = '1200' kwargs['height'] = '600' super().__init__(*args, **kwargs) self.config(bg="#98A7AC") self.str_variables = {} label1 = tk.Label( self, text="Sistema de monitoreo de Paneles del SESLab", font=18) label1.grid(row=0, column=0, padx=5, pady=5, columnspan=2) self.data_objs = [self.data.date, self.data.time] self.data_objs += [obj for obj in self.data.voltages] self.data_objs += [self.data.average, self.data.lowest] for row_index, data_obj in enumerate(self.data_objs, 1): label = tk.Label(self, text=f'{data_obj.label} : ') label.grid(row=row_index, column=0, padx=2, pady=5) str_variable = tk.StringVar() entry = tk.Entry(self, textvariable=str_variable) entry.grid(row=row_index, column=1, padx=1, pady=5) entry.config(fg="black", justify="center", state='readonly') self.str_variables[data_obj.label] = str_variable @tk_after def update(self): for data_obj in self.data_objs: self.str_variables[data_obj.label].set(data_obj.value) if __name__ == '__main__': app = App() app.mainloop()