Python Forum
tkinter toggle buttons not working
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
tkinter toggle buttons not working
#11
The way I used grid was a hack to make the code look more like the guizero code. Had I started with tkinter I would probably have the button helper function set the grid position and set any padding.
import tkinter as tk
  
path = ""
  
class RelayButton(tk.Button):
    """A button that toggles a relay"""
    def __init__(self, parent, url="", on_image=None, off_image=None, **kwargs):
        super().__init__(parent, image=off_image, command=self.toggle)
        self.url = url
        self._on = False
        self.on_image = on_image
        self.off_image = off_image
  
    @property
    def on(self):
        """Return state of the relay"""
        return self._on
  
    @on.setter
    def on(self, on):
        """Set state of the relay"""
        self._on = on
        if on:
            # requests.post(f"{self.url}cm?cmnd=Power On")
            self["image"] = self.on_image
        else:
            # requests.post(f"{self.url}cm?cmnd=Power Off")
            self["image"] = self.off_image
   
    def toggle(self):
        """Toggle state of the relay"""
        self.on = not self.on
  
def new_button(name, url, parent, row, column, **kwargs):
    """Convenience functon that creates a RelayButton"""
    on_image = tk.PhotoImage(file=f"{path}{name}_on.png")
    off_image = tk.PhotoImage(file=f"{path}{name}_off.png")
    button = RelayButton(parent, url, on_image, off_image, **kwargs)
    button.grid(row=row, column=column, padx=5)
    return button
  
def main():
    app = tk.Tk()
    buttons = [
        new_button('living_room', "http://192.168.0.101/", app, 0, 0),
        new_button('kitchen', "http://192.168.0.113/", app, 0, 1))
    ]
    for b in buttons:
        b.on = False
    app.mainloop()
   
if __name__ == "__main__":
  main()
Reply
#12
Thanks deanhystad... this is working great. My search continues for getting the actual status of the relays. Thanks again for your time on this, I really appreciate it.
Reply
#13
Hi deanhystad (or other great helpers available). With this code you supplied, at what portion of it can I add things like labels? For instance I want to add a clock to my window. I also plan to add things like door/window sensor status. When I attempt to add a label to the code, it does not show up. Thanks.
Reply
#14
How are you adding the label? Post some code that demonstrates failing to put a label in a window.
Reply
#15
Ok, so I got it to show up finally. I think it must have been hiding behind the button (oops!). Now what I want to do is update the label info with the information sent back from the Sonoff switch. As you can see here, I put in a 'def getTemp()' and in 'def main()' I started with adding a button to get the request, which works. I would rather just have it update the label on open with that information and refresh every few minutes.

** I just edited the post here. I did get the clock to show up on the screen. Here is my complete code now. I just need to figure out the label updating for getting the temperature reading.

import tkinter as tk
from tkinter import *
from tkinter import messagebox
import requests
import http.client
import time
   
path = ""

SONOFF_Temp  = 'http://192.168.0.113/cm?cmnd=status+10'
sonoff_url = 'NOT_INIT'


class RelayButton(tk.Button):
    """A button that toggles a relay"""
    def __init__(self, parent, url="", on_image=None, off_image=None, **kwargs):
        super().__init__(parent, image=off_image, command=self.toggle)
        self.url = url
        self._on = False
        self.on_image = on_image
        self.off_image = off_image
        self.label = Label(text="", fg="Red", font=("Helvetica", 18))
        self.label.place(x=600,y=20)
        self.update_clock()
        
    @property
    def on(self):
        """Return state of the relay"""
        return self._on
   
    @on.setter
    def on(self, on):
        """Set state of the relay"""
        self._on = on

        if on:
            requests.post(f"{self.url}cm?cmnd=Power On")
            self["image"] = self.on_image
        else:
            requests.post(f"{self.url}cm?cmnd=Power Off")
            self["image"] = self.off_image
    
    def toggle(self):
        """Toggle state of the relay"""
        self.on = not self.on
        
    def update_clock(self):
        now = time.strftime("%H:%M:%S")
        self.label.configure(text=now)
        self.after(1000, self.update_clock)
   
def new_button(name, url, parent, row, column, **kwargs):
    """Convenience functon that creates a RelayButton"""
    on_image = tk.PhotoImage(file=f"{path}{name}_on.png")
    off_image = tk.PhotoImage(file=f"{path}{name}_off.png")
    button = RelayButton(parent, url, on_image, off_image, **kwargs)
    button.grid(row=row, column=column, padx=10, pady=10)
    return button


def getTemp():
    sonoff_url = SONOFF_Temp
    sonR1 = requests.get(sonoff_url)
    messagebox.showinfo("Temp", sonR1.text)

   
def main():
    app = tk.Tk()
    app.title('Home Relay Controls')
    tempbtn = Button(app, text='GET TEMP', command=getTemp)
    tempbtn.grid(row=1, column=2, padx=(250, 20))
    buttons = [
        new_button('living_room', "http://192.168.0.101/", app, 0, 0),
        new_button('front_porch', "http://192.168.0.113/", app, 0, 1)
    ]
    
    for b in buttons:
        b.on = False
    app.mainloop()
    
    
if __name__ == "__main__":
    main()
Reply
#16
Use tkinter .after(milliseconds, func) to call a function sometime in the future.
Reply
#17
(Jan-17-2022, 11:20 PM)deanhystad Wrote: Use tkinter .after(milliseconds, func) to call a function sometime in the future.

I got this formatted pretty well so far... Now I am trying to format the string to eliminate all the parts I don't want. This is the string that I want to get only the temperature portion from.

{"StatusSNS":{"Time":"2022-01-18T01:10:45","DS18B20":{"Id":"0316471BECFF","Temperature":70.8},"TempUnit":"F"}}
   
Reply
#18
This is a dictionary as a json string. You could convert the string to a dictionary using json.loads(). Then you could use the TempUnit and also the Time. The structure is kind of awkward.
import json

x = json.loads('{"StatusSNS":{"Time":"2022-01-18T01:10:45","DS18B20":{"Id":"0316471BECFF","Temperature":70.8},"TempUnit":"F"}}')
status = x["StatusSNS"]
timestamp = status["Time"]
device_names = list(status.keys())[1:-1]
temp_units = status["TempUnit"]
for name in device_names:
    device = status[name]
    print(f'ID={name}, Temperature={device["Temperature"]} {temp_units}')
Reply
#19
[quote="deanhystad" pid='152246' dateline='1642473379']
This is a dictionary as a json string. You could convert the string to a dictionary using json.loads(). Then you could use the TempUnit and also the Time. The structure is kind of awkward.

I must have something wrong here in 'def getTemp' I get "device_names" is not defined?

import tkinter as tk
from tkinter import *
from tkinter import messagebox
import requests
import http.client
import time
import json
   
path = ""

SONOFF_Temp  = 'http://192.168.0.113/cm?cmnd=status+10'
sonoff_url = 'NOT_INIT'


class RelayButton(tk.Button):
    """A button that toggles a relay"""
    def __init__(self, parent, url="", on_image=None, off_image=None, **kwargs):
        super().__init__(parent, image=off_image, command=self.toggle)
        self.url = url
        self._on = False
        self.on_image = on_image
        self.off_image = off_image
        self.label = Label(text="", fg="Black", font=("Helvetica", 18))
        self.label.place(x=10,y=20)
        self.update_clock()
        self.templabel = Label(text="temp info", fg="Black", font=("Helvetica", 8))
        self.templabel.place(x=10, y=60)
        
    @property
    def on(self):
        """Return state of the relay"""
        return self._on
   
    @on.setter
    def on(self, on):
        """Set state of the relay"""
        self._on = on

        if on:
            requests.post(f"{self.url}cm?cmnd=Power On")
            self["image"] = self.on_image
        else:
            requests.post(f"{self.url}cm?cmnd=Power Off")
            self["image"] = self.off_image
    
    def toggle(self):
        """Toggle state of the relay"""
        self.on = not self.on
        
    def update_clock(self):
        now = time.strftime("%I:%M:%S")
        self.label.configure(text=now)
        self.after(1000, self.update_clock)
        self.after(5000, self.getTemp)
        
    def getTemp(self):
        sonoff_url = SONOFF_Temp
        sonR1 = requests.get(sonoff_url)
        x = json.loads(sonR1.text)
        status = x["StatusSNS"]
        timestamp = status["Time"]
        device_names = list(status.keys())[1:-1]
        temp_units = status["TempUnit"]
    for name in device_names:
        device = status[name]
        print(f'ID={name}, Temperature={device["Temperature"]} {temp_units}')
        #self.templabel.configure(text=newstr["Temperature"])
   
def new_button(name, url, parent, row, column, **kwargs):
    """Convenience functon that creates a RelayButton"""
    on_image = tk.PhotoImage(file=f"{path}{name}_on.png")
    off_image = tk.PhotoImage(file=f"{path}{name}_off.png")
    button = RelayButton(parent, url, on_image, off_image, **kwargs)
    button.grid(row=row, column=column, padx=50, pady=110)
    return button


   
def main():
    app = tk.Tk()
    app.title('Home Relay Controls')
    buttons = [
        new_button('living_room', "http://192.168.0.101/", app, 0, 0),
        new_button('front_porch', "http://192.168.0.113/", app, 0, 1)
    ]
    
    for b in buttons:
        b.on = False
    app.mainloop()
    
    
if __name__ == "__main__":
    main()
Reply
#20
Indentation in line 64 pushes the for loop outside the scope of the getTemp() method.
Look st your update clock method. How often is getTemp called. Think before you automatically say "Once every 5 seconds"
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Using Tkinter inside function not working Ensaimadeta 5 5,073 Dec-03-2023, 01:50 PM
Last Post: deanhystad
  [Tkinter] Radio Buttons Working Bassackwards gw1500se 6 2,328 Dec-07-2021, 07:13 PM
Last Post: menator01
  [Tkinter] Have tkinter button toggle on and off a continuously running function AnotherSam 5 5,029 Oct-01-2021, 05:00 PM
Last Post: Yoriz
  TkInter Binding Buttons ifigazsi 5 4,424 Apr-06-2020, 08:30 AM
Last Post: ifigazsi
  python file(.py) not working with my tkinter project DeanAseraf1 9 7,182 Mar-22-2020, 10:58 PM
Last Post: ifigazsi
  Tkinter scaling windows conten to or with its size not working Detzi 5 4,488 Jan-12-2020, 12:42 PM
Last Post: Detzi
  Issue on tkinter with buttons Reldaing 1 2,451 Jan-07-2020, 08:21 AM
Last Post: berckut72
  Need tkinter help with clicking buttons pythonprogrammer 2 2,475 Jan-03-2020, 04:43 AM
Last Post: joe_momma
  [Tkinter] Mouse click event not working on multiple tkinter window evrydaywannabe 2 3,771 Dec-16-2019, 04:47 AM
Last Post: woooee
  Tkinter Buttons action d3fi 1 2,010 Nov-20-2019, 09:16 PM
Last Post: Larz60+

Forum Jump:

User Panel Messages

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