Python Forum
[Tkinter] Anyone know what happened to my button?
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[Tkinter] Anyone know what happened to my button?
#15
(May-30-2021, 11:37 AM)Yoriz Wrote: I have made it so that there is a variable to set the number of voltage panels 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()

Morning Yoriz

Thanks for that. I had a problem adding the Panel voltage. I think I know that the problem happens because when the frame is sent, the voltage is sent as a string, and then when it arrives here at the PC, I need to convert that voltage to an integer to be able to handle them in the operation to sacrifice the average.

I don't know why it stopped working when I told him that
data.split (",") [0] be an integer by appending int forward
int (data.split (",") [0]) .

The other thing I wanted to tell you is how the program realizes that the frame that enters the PC is from the Panel that I assign as Panel 1 or 2 or 3. Now it is very easy because I can only test one antenna at a time but when put this system to work, I am going to receive 3 frames of data from 3 different sensors.

So I'm going to tell you that Xbee made a library for Xbee, well as you have seen there are several functions here that have served to send, receive and subtract the data from the Xbee.

There is a function called xbee_message.remote_device.get_64bit_addr ()

This function will give me the direction of the sensor in this way I will know according to how I place the sensors in the field, to which sensor each direction belongs.

Next this is the result of printing in the console the code that gives me the address of the sensor, the data frame and the separated data.

30.00,09:18:36,30/05/2021 This is the data frame that all Xbee will send to the PC.

Xbee address 0013A20040D7B01E >> 30.00,09:18:36,30/05/2021
voltage 30.00
hour 09:18:36
date 30/05/2021

Regarding the direction of the devices as I have told you, at the time of the test I will do it with 3 Xbee. Each Xbee on the back has its address.
For example sensor 1 would be
0013A20040D7B01E
sensor 2 would be
0013A200414EF124
sensor 3 would be
0013A20040D7AEA4

What I thought is that if in the future I add more devices to the network. what you should do is modify the code and add more sensor addresses before placing them.

In the graphical interface, it does not matter if only sensor 1 takes the time and date, but from all the others it takes the voltage from them, which would be taken from data data.split (",") [0]

Attached Files

Thumbnail(s)
   
Reply


Messages In This Thread
RE: Anyone know what happened to my button? - by IgnacioMora23 - May-30-2021, 03:34 PM

Possibly Related Threads…
Thread Author Replies Views Last Post
  [PySimpleGui] How to alter mouse click button of a standard submit button? skyerosebud 3 4,952 Jul-21-2019, 06:02 PM
Last Post: FullOfHelp

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020