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?
#1
Exclamation 
Hello good afternoon I am making an interface that can show the data of the xbee connected via usb.

I followed several videos that I have seen on informational pills, which have helped me get here.

But I have come to a problem, which I have not been able to get out.

I want to make a button that when I strip it, it sends a message to all the xbes of the network (that if it does) and this signal will start the reception of data. What I mean is that after removing the button, I will also start receiving data from the xbees.



I want to present this data on screen.

That is why I call a function and that function will give me the voltage date and time data, they are all strings and all those I indicate as StringVar (). And then I print it on some labels.



THE PROBLEM:

The button does not appear to me and the data is not printed on the labels.

You should mention that the button does appear to me if I "tie it to the root" but I did a FRAME to put all the labels there and I wanted to put the button there and it did not appear.


#-----------------------------Se importan las librerias------------------------------

from tkinter import*
from digi.xbee.devices import XBeeDevice

#-----------------------------Se inicializan Variables y puertos-----------------

PORT = "COM5"
BAUD_RATE = 9600
device = XBeeDevice(PORT, BAUD_RATE)


            
#-----------------------------Se crea la pantalla principal--------------------

raiz=Tk()
raiz.title("Sistema de Monitoreo de Paneles Solares del SESLab")
raiz.config(bg="#032539")

width_of_window = 600
height_of_window = 280

screen_width = raiz.winfo_screenwidth()
screen_height = raiz.winfo_screenheight()

x_coordinate = (screen_width/2)-(width_of_window/2)
y_coordinate = (screen_height/2)-(height_of_window/2)

raiz.geometry("%dx%d+%d+%d" % (width_of_window, height_of_window, x_coordinate, y_coordinate ))

miframe=Frame(raiz,width="1200",height="600" )
miframe.pack()
miframe.config(bg="#1C768F")

#-----------------------------Valores por Mostrar en la Pantallas-----------------

voltage=StringVar()
hora=StringVar()
fecha=StringVar()


#------------------------------Cuadros donde se muestran datos-----------------------

cuadroVisual1=Label(miframe, textvariable=fecha, fg="black",justify="center", relief ="ridge", width=20)
cuadroVisual1.pack()
cuadroVisual1.grid(row=1, column= 1, padx= 2, pady=5)

cuadroVisual2=Label(miframe, textvariable=fecha, fg="black",justify="center", relief ="ridge", width=20)
cuadroVisual2.grid(row=2, column= 1, padx= 2, pady=5)

cuadroVisual3=Label(miframe, textvariable=fecha, fg="black",justify="center", relief ="ridge", width=20)
cuadroVisual3.grid(row=4, column= 1, padx= 2, pady=5)

cuadroVisual4=Label(miframe, textvariable=fecha, fg="black",justify="center", relief ="ridge", width=20)
cuadroVisual4.grid(row=5, column= 1, padx= 2, pady=5)

cuadroVisual5=Label(miframe, textvariable=fecha, fg="black",justify="center", relief ="ridge", width=20)
cuadroVisual5.grid(row=6, column= 1, padx= 2, pady=5)

cuadroVisual6=Label(miframe, textvariable=fecha, fg="black",justify="center", relief ="ridge", width=20)
cuadroVisual6.grid(row=7, column= 1, padx= 2, pady=5)

cuadroVisual7=Label(miframe, textvariable=fecha, fg="black",justify="center", relief ="ridge", width=20, anchor= CENTER)
cuadroVisual7.grid(row=9, column= 1, padx= 2, pady=5)


#---------------------------Cuadros donde esta el Texto------------------------

miLabel1=Label(miframe, text="Sistema de monitoreo de Paneles del SESLab", font=18)
miLabel1.grid(row=0, column= 0, padx= 5, pady=5, columnspan= 2)

nombrePanel2=Label(miframe, text="Fecha de medición : ", relief ="raised")
nombrePanel2.grid(row=1, column= 0, padx= 2, pady=5)

nombrePanel3=Label(miframe, text="Hora de medición : ", relief ="raised")
nombrePanel3.grid(row=2, column= 0, padx= 2, pady=5)

nombrePanel4=Label(miframe, text="Voltage de Panel 1 : ", relief ="raised")
nombrePanel4.grid(row=4, column= 0, padx= 2, pady=5)

nombrePanel5=Label(miframe, text="Voltage de Panel 2 : ", relief ="raised")
nombrePanel5.grid(row=5, column= 0, padx= 2, pady=5)

nombrePanel6=Label(miframe, text="Voltage de Panel 3 : ", relief ="raised")
nombrePanel6.grid(row=6, column= 0, padx= 2, pady=5)

nombrePanel7=Label(miframe, text="Promedio : ", relief ="raised")
nombrePanel7.grid(row=7, column= 0, padx= 2, pady=5)

nombrePanel8=Label(miframe, text="Panel con Menor Voltaje : ", relief ="raised")
nombrePanel8.grid(row=9, column= 0, padx= 2, pady=5)

#------------------------Función que manda un mensaje Broadcast a Xbees---------
#------------------------Mensaje recibido----------------------------------------

def codigoIniciar():
    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:
                ##direccion.set(xbee_message.remote_device.get_64bit_addr()) 
                data = xbee_message.data.decode()
                voltage.set(data.split(",")[0])
                hora.set(data.split(",")[1])
                #fecha.set(data.split(",")[2])
                cuadroVisual1["text"]=data.split(",")[2]
                
    finally:
        if device is not None and device.is_open():
            device.close()        


#--------------------Boton que llama a la función CodigoIniciar--------------

botonEnvio=Button(miframe, text=" Iniciar " ,command=codigoIniciar)
botonEnvio.grid(row=10, column= 10, padx= 5, pady=5, columnspan= 2, )
botonEnvio.config(bg="#FA991C")
botonEnvio.pack()

#----------------Loop de la ventana  --------------

raiz.mainloop()

Attached Files

Thumbnail(s)
   
Reply
#2
you cant use both pack and grid in same container,
Try removing line 45, and change all other packs (within the miframe) to grid as well.
or all grids to pack, but not both.
IgnacioMora23 likes this post
Reply
#3
(May-28-2021, 11:20 PM)Larz60+ Wrote: you cant use both pack and grid in same container,
Try removing line 45, and change all other packs (within the miframe) to grid as well.
or all grids to pack, but not both.


Ready, the button appears. Thank you.

The idea of the button is that when I write it, it sends a message and opens the "port" or "xbee" so that it receives a message "infinite times".
As you can see in the image that just uploaded, when I click the button, the message is sent and in a couple of milliseconds I get the first information about the incoming frame. Which is good.

But when I want to get from that same data frame, the time or the date, my system stops working. I don't know if you know why this is happening?

I am simulating this programming, sending the frames from an Xbee to an Arduino, so on the right of the image you can see the arduino serial monitor. And on the left of the image the interface that I designed.

Another problem I have is that the value is not updated and only the first value I read is frozen.


#-----------------Se importan las librerias--------------------------
 
from tkinter import*
from digi.xbee.devices import XBeeDevice
 
#------------------Se inicializan Variables y puertos-----------------
 
PORT = "COM5"
BAUD_RATE = 9600
device = XBeeDevice(PORT, BAUD_RATE)
 
#------------------- Se crea la pantalla principal--------------------
 
raiz=Tk()
raiz.title("Sistema de Monitoreo de Paneles Solares del SESLab")
raiz.config(bg="#919F89")

width_of_window = 600
height_of_window = 280

screen_width = raiz.winfo_screenwidth()
screen_height = raiz.winfo_screenheight()

x_coordinate = (screen_width/2)-(width_of_window/2)
y_coordinate = (screen_height/2)-(height_of_window/2)

raiz.geometry("%dx%d+%d+%d" % (width_of_window, height_of_window, x_coordinate, y_coordinate ))

 
miframe=Frame(raiz,width="1200",height="600" )
miframe.pack()
miframe.config(bg="#98A7AC")
 
#-----------------Valores por Mostrar en la Pantallas-----------------

voltage = StringVar()
 
#--------------- Cuadros donde se muestran datos-----------------------
 
cuadroVisual1= Entry(miframe, textvariable = voltage)
cuadroVisual1.grid(row=1, column= 1, padx=1, pady=5)
cuadroVisual1.config(fg="black",justify="center")

 
cuadroVisual2= Entry(miframe, textvariable = voltage)
cuadroVisual2.grid(row=2, column= 1, padx=1, pady=5)
cuadroVisual2.config(fg="black",justify="center")
 
cuadroVisual3= Entry(miframe, textvariable = voltage)
cuadroVisual3.grid(row=4, column= 1, padx=1, pady=5)
cuadroVisual3.config(fg="black",justify="center")
 
cuadroVisual4= Entry(miframe, textvariable=voltage)
cuadroVisual4.grid(row=5, column= 1, padx=1, pady=5)
cuadroVisual4.config(fg="black",justify="center")
 
cuadroVisual5= Entry(miframe, textvariable=voltage)
cuadroVisual5.grid(row=6, column= 1, padx=1, pady=5)
cuadroVisual5.config(fg="black",justify="center")
 
cuadroVisual6= Entry(miframe, textvariable=voltage)
cuadroVisual6.grid(row=7, column= 1, padx=1, pady=5)
cuadroVisual6.config(fg="black",justify="center")
 
cuadroVisual7= Entry(miframe, textvariable=voltage)
cuadroVisual7.grid(row=9, column= 1, padx=1, pady=5)
cuadroVisual7.config(fg="black",justify="center")
 
#----------------Cuadros donde esta el Texto "Voltage de Panel 1"--------
 
miLabel=Label(miframe, text="Sistema de monitoreo de Paneles del SESLab", font=18)
miLabel.grid(row=0, column= 0, padx= 5, pady=5, columnspan= 2)
 
nombrePanel=Label(miframe, text="Fecha de medición : ")
nombrePanel.grid(row=1, column= 0, padx= 2, pady=5)
 
nombrePanel=Label(miframe, text="Hora de medición : ")
nombrePanel.grid(row=2, column= 0, padx= 2, pady=5)
 
nombrePanel=Label(miframe, text="Voltage de Panel 1 : ")
nombrePanel.grid(row=4, column= 0, padx= 2, pady=5)
 
nombrePanel=Label(miframe, text="Voltage de Panel 2 : ")
nombrePanel.grid(row=5, column= 0, padx= 2, pady=5)
 
nombrePanel=Label(miframe, text="Voltage de Panel 3 : ")
nombrePanel.grid(row=6, column= 0, padx= 2, pady=5)
 
nombrePanel=Label(miframe, text="Promedio : ")
nombrePanel.grid(row=7, column= 0, padx= 2, pady=5)
 
nombrePanel=Label(miframe, text="Panel con Menor Voltaje : ")
nombrePanel.grid(row=9, column= 0, padx= 2, pady=5)
 
#----------------Función que manda un mensaje Broadcast a Xbees---------
 
def codigoIniciar():
    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:
                ##direccion.set(xbee_message.remote_device.get_64bit_addr()) 
                data = xbee_message.data.decode()
                voltage.set(data.split(",")[0])
                #hora.set(data.split(",")[1])
                #fecha.set(data.split(",")[2])
                
    finally:
        if device is not None and device.is_open():
            device.close()        


#----------------Boton que llama a la función CodigoIniciar--------------
 
botonEnvio=Button(raiz, bg="#E2BBAC", text="Iniciar " ,command=codigoIniciar)
botonEnvio.pack()
 
#----------------Loop de la ventana  --------------
 
raiz.mainloop()

Attached Files

Thumbnail(s)
   
Reply
#4
Because I don't have an xbee available to play with, can't run the code, so unable to diagnose.
I actually do have a bunch of older xbee's that are in a tote in my garage, but it would take a few months to find them.

I think that you should unconditionally close the device in your finally clause.
If for any reason it was not closed, the next time the codigoIniciar() function was called, you would be trying to open an already open device, I have no idea what (if any) problem that would cause.
Reply
#5
There is a while True loop that could be blocking the GUI event loop depdnign on how long it takes.
If it is blocking see [Tkinter] How to deal with code that blocks the mainloop, freezing the gui
IgnacioMora23 likes this post
Reply
#6
(May-29-2021, 02:18 PM)Yoriz Wrote: There is a while True loop that could be blocking the GUI event loop depdnign on how long it takes.
If it is blocking see [Tkinter] How to deal with code that blocks the mainloop, freezing the gui


If I believe that it is. Because it exists, the main code of the GUI is a loop. And I want that after removing the button, it remains in its internal loop receiving messages. So I feel that you are correct. I must pause the GUI Loop so that my program can work better, because it gets stuck.


In fact it already shows the value of the voltage, but when I call the other variable for example the time or the date, it no longer copies it in its space.

I also look for the data to be updated in each loop, it only stays stuck in the first data haha it is frustrating.
Reply
#7
(May-29-2021, 01:38 PM)Larz60+ Wrote: Because I don't have an xbee available to play with, can't run the code, so unable to diagnose.
I actually do have a bunch of older xbee's that are in a tote in my garage, but it would take a few months to find them.

I think that you should unconditionally close the device in your finally clause.
If for any reason it was not closed, the next time the codigoIniciar() function was called, you would be trying to open an already open device, I have no idea what (if any) problem that would cause.

Maybe, I think it is the general loop en the GUI. Because I only want to call one One time the codigoIniciar() .
Reply
#8
Try this, I can't test it so fingers crossed, I don't know if data.split(",") will be in the right order for self.form_frame.set_str_variables
import functools
import tkinter as tk
from concurrent import futures

from digi.xbee.devices import XBeeDevice

PORT = "COM5"
BAUD_RATE = 9600
device = XBeeDevice(PORT, BAUD_RATE)

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")

        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)
        self.form_frame.pack()
        self.btn = tk.Button(
            self, bg="#E2BBAC", text="Iniciar ", command=self.on_btn)
        self.btn.pack()

    def btn_enable(self, enable=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.form_frame.set_str_variables(data.split(","))
        finally:
            if device is not None and device.is_open():
                device.close()
            self.btn_enable()


class FormFrame(tk.Frame):
    def __init__(self, *args, **kwargs) -> None:
        kwargs['width'] = '1200'
        kwargs['height'] = '600'
        super().__init__(*args, **kwargs)
        self.config(bg="#98A7AC")
        self.str_variables = []

        for row_index in (1, 2, 4, 5, 6, 7, 9):
            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.append(str_variable)

        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)

        for row_index, text in (
            (1, 'Fecha de medición : '), (2, 'Hora de medición : '),
            (4, 'Voltage de Panel 1 : '), (5, 'Voltage de Panel 2 : '),
            (6, 'Voltage de Panel 3 : '), (7, 'Promedio : '),
                (9, 'Panel con Menor Voltaje : ')):
            label = tk.Label(self, text="Fecha de medición : ")
            label.grid(row=row_index, column=0, padx=2, pady=5)

    @tk_after
    def set_str_variables(self, results):
        for str_variable, result in zip(self.str_variables, results):
            str_variable.set(result)


if __name__ == '__main__':
    app = App()
    app.mainloop()
IgnacioMora23 likes this post
Reply
#9
(May-29-2021, 07:36 PM)Yoriz Wrote: Try this, I can't test it so fingers crossed, I don't know if data.split(",") will be in the right order for self.form_frame.set_str_variables
import functools
import tkinter as tk
from concurrent import futures

from digi.xbee.devices import XBeeDevice

PORT = "COM5"
BAUD_RATE = 9600
device = XBeeDevice(PORT, BAUD_RATE)

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")

        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)
        self.form_frame.pack()
        self.btn = tk.Button(
            self, bg="#E2BBAC", text="Iniciar ", command=self.on_btn)
        self.btn.pack()

    def btn_enable(self, enable=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.form_frame.set_str_variables(data.split(","))
        finally:
            if device is not None and device.is_open():
                device.close()
            self.btn_enable()


class FormFrame(tk.Frame):
    def __init__(self, *args, **kwargs) -> None:
        kwargs['width'] = '1200'
        kwargs['height'] = '600'
        super().__init__(*args, **kwargs)
        self.config(bg="#98A7AC")
        self.str_variables = []

        for row_index in (1, 2, 4, 5, 6, 7, 9):
            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.append(str_variable)

        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)

        for row_index, text in (
            (1, 'Fecha de medición : '), (2, 'Hora de medición : '),
            (4, 'Voltage de Panel 1 : '), (5, 'Voltage de Panel 2 : '),
            (6, 'Voltage de Panel 3 : '), (7, 'Promedio : '),
                (9, 'Panel con Menor Voltaje : ')):
            label = tk.Label(self, text="Fecha de medición : ")
            label.grid(row=row_index, column=0, padx=2, pady=5)

    @tk_after
    def set_str_variables(self, results):
        for str_variable, result in zip(self.str_variables, results):
            str_variable.set(result)


if __name__ == '__main__':
    app = App()
    app.mainloop()

OMG Yoriz, Its is working. Only thing it is the name of the labels because all say "Fecha de medicion" and look into the code and the names it is alright.

for row_index, text in (
            (1, 'Fecha de medición : '), (2, 'Hora de medición : '),
            (4, 'Voltage de Panel 1 : '), (5, 'Voltage de Panel 2 : '),
            (6, 'Voltage de Panel 3 : '), (7, 'Promedio : '),
                (9, 'Panel con Menor Voltaje : ')):
            label = tk.Label(self, text="Fecha de medición : ")

Attached Files

Thumbnail(s)
   
Reply
#10
I missed that Tongue
it should be
for row_index, text in (
            (1, 'Fecha de medición : '), (2, 'Hora de medición : '),
            (4, 'Voltage de Panel 1 : '), (5, 'Voltage de Panel 2 : '),
            (6, 'Voltage de Panel 3 : '), (7, 'Promedio : '),
                (9, 'Panel con Menor Voltaje : ')):
            label = tk.Label(self, text=text)

it doesn't actually need two for loop in FormFrame, it can be changed to
class FormFrame(tk.Frame):
    def __init__(self, *args, **kwargs) -> None:
        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)

        for row_index, text in (
            (1, 'Fecha de medición : '), (2, 'Hora de medición : '),
            (4, 'Voltage de Panel 1 : '), (5, 'Voltage de Panel 2 : '),
            (6, 'Voltage de Panel 3 : '), (7, 'Promedio : '),
                (9, 'Panel con Menor Voltaje : ')):
            label = tk.Label(self, text=text)
            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.append(str_variable)

    @tk_after
    def set_str_variables(self, results):
        for str_variable, result in zip(self.str_variables, results):
            str_variable.set(result)
IgnacioMora23 likes this post
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [PySimpleGui] How to alter mouse click button of a standard submit button? skyerosebud 3 2,783 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