Python Forum
"Disabled" Buttons Still Clickable.
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
"Disabled" Buttons Still Clickable.
#1
Sad 
Hello. I've never been good a programming, and I know I'm trying to run before learning to walk, but it's kind of how I work by learning when I have a need to learn something, it's because I have a goal in mind. I've been up and down this issue for months and totally stuck at this one. It's likely simple fix for those that are experienced, but I just can't find a solution that I can make work, or even understand. Heck, my buddy did much of the code as mine was much worse. Functional, but not efficient, but at least I could understand mine cause I'm not good with loops and variables, despite that pretty much being the basics of programming...

I'm working on making some accessory controls for my truck. Part of it is controls for the winch and I want to implement a few safety systems in software. I did the in-out safety interlock done, but the next one is a two-setup system so I need to activate the "Winch Controls" before the "Feed In" and "Feed Out" options are available. This is to avoid accidental activation. While it does look like it works, the buttons can be activated despite being disabled, then it's free-for-all that seems to even bypass the IN/OUT safety interlock. I've seen the clickable-yet-disabled buttons being is a known issue,. but I just don't understand how to fix it.

If someone could give me a hand I would be greatly appreciated. Even more of a help would be if you could do the code in a manner that's more simplistic for me to understand as opposed to being the more efficient way. It would really help me learn.




# ************************************************************************** #
# ****                                                                  **** #
# ******************** Crappy code by Michel Fortin  *********************** #
# ****                                                                       #
# ********************* Better code by Fred Poirier  *********************** #
# ****                                                                       #
# ************************************************************************** #
# ****                                                                  **** #
# ********** Based from code by www.TheEngineeringProjects.com ************* #
# ****                                                                  **** #
# **************** How to Create a GUI in Raspberry Pi 3 ******************* #
# ****                                                                  **** #
# ************************************************************************** #

# Importing Libraries

import RPi.GPIO as GPIO
import time
from tkinter import *
import tkinter.font
import tkinter as tk


# Raspberry Pi 4 Pin Settings

#Création de la liste des sorties
SWITCH = [0 for i in range(14)]
Button_Press = [0 for i in range(14)]
Button_N = [0 for i in range(14)]
TextSW = [0 for i in range(14)]
Output_State = [0 for i in range(14)]
SBC = [0]
WinchStat = [0]

for i in range(14):
    SWITCH[i] = (i)
    GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BCM) # We are accessing GPIOs according to their physical location
    GPIO.setup(SWITCH[i], GPIO.OUT) # We have set our LED pin mode to output
    GPIO.output(SWITCH[i], GPIO.LOW) # When it will start then LED will be OFF

# GUI basic settings

Gui = Tk()
Gui.title("---")
Gui.config(background= "#818589")
Gui.minsize(800,600)
Font1 = tkinter.font.Font(family = 'Helvetica', size = 18, weight = 'bold')

# GUI Controls


def SW_ON(Param1, Param2, Param3, Param4, Param5, Param6):
    if (Output_State[Param1] == 0):
        GPIO.output(SWITCH[Param1], GPIO.HIGH) #Switch ON
        Button_Press[Param1]["fg"] = "GREEN"
        TextSW[Param1] = Label(Gui,text=' ON ', font = Font1, bg = Param2, fg='BLACK', padx = 0)
        TextSW[Param1].grid(row=Param3,column=Param4)
        Output_State[Param1] = 1             
        if (Param6 == 1) :
          Button_Press[Param1+1]["state"] = "normal"
          TextSW[7] = Label(Gui,text=SBC[0], font = Font1, bg = 'Orange', fg='BLACK', padx = 20)
          TextSW[7].grid(row=4,column=2)
        if (Param6 == 3) :
          Button_Press[Param1+1]["state"] = "normal"
          Button_Press[Param1+2]["state"] = "normal"
                 
    else :
        GPIO.output(SWITCH[Param1], GPIO.LOW) #Switch OFF
        Button_Press[Param1]["fg"] = "BLACK"
        TextSW[Param1] = Label(Gui,text='_____', font = Font1, bg='#818589', fg='#818589', padx = 1)
        TextSW[Param1].grid(row=Param3,column=Param4)
        Output_State[Param1] = 0
        if (Param6 == 1):
            Button_Press[Param1+1]["state"] = "disabled"
        TextSW[7] = Label(Gui,text='_____', font = Font1, bg='#818589', fg='#818589', padx = 1)
        TextSW[7].grid(row=4,column=2)
        if (Param6 == 2):
            Button_Press[Param1+1]["state"] = "disabled"
        TextSW[3] = Label(Gui,text='_____', font = Font1, bg='#818589', fg='#818589', padx = 1)
        TextSW[3].grid(row=2,column=2)
        if (Param6 == 3) :
          Button_Press[Param1+1]["state"] = "disabled"
          Button_Press[Param1+2]["state"] = "disabled"

# Cycling Mode Count for Front Spot Light Modes        
def SWITCH3() :
    GPIO.output(SWITCH[3], GPIO.HIGH)
    SBC[0] += 1
    if (SBC[0] == 7) :
        SBC[0] = 1   
    TextSW[3] = Label(Gui,text=SBC[0], font = Font1, bg = 'Orange', fg='BLACK', padx = 20)
    TextSW[3].grid(row=2,column=2)        
        
# Cycling Mode Count for Rear Signal Bar Modes          
def SWITCH7() :
    GPIO.output(SWITCH[7], GPIO.HIGH)
    SBC[0] += 1
    if (SBC[0] == 9) :
        SBC[0] = 1   
    TextSW[7] = Label(Gui,text=SBC[0], font = Font1, bg = 'Orange', fg='BLACK', padx = 20)
    TextSW[7].grid(row=4,column=2)
    
    
    
    
    
#Conditions to momentary press winch buttons, plus opposing button interlock    

def button12_pressed():
    
    Button_Press[13]["state"] = "disabled"
    GPIO.output(SWITCH[12], GPIO.HIGH)
    print('Winch IN')
    
def button12_released():
    Button_Press[13]["state"] = "normal"
    Button_Press[12]["state"] = "normal"
    GPIO.output(SWITCH[12], GPIO.LOW)
    print('Winch OFF (IN)')
         
def button13_pressed():
    Button_Press[12]["state"] = "disabled"
    GPIO.output(SWITCH[13], GPIO.HIGH)
    print('Winch OUT')
     
def button13_released():
    Button_Press[12]["state"] = "normal"
    Button_Press[13]["state"] = "normal"
    GPIO.output(SWITCH[13], GPIO.LOW)
    print('Winch OFF (Out)')




# Function for Buttons started here

#Switch 1 - Foward Roof Mounted Light Bar Controls
Button_Press[1] = Button(Gui, text='Front Light Bar', font = Font1, command=lambda a=1, b='Yellow', c=2, d=0, e='Front Light Bar', f=0: SW_ON(a, b, c, d, e, f), bg='bisque2', height = 1, width = 16)
Button_Press[1].grid(row=1,column=0)
#Switch 2 - Brush Buard Spot Lights - Main Power
Button_Press[2] = Button(Gui, text='Front Spot Lights', font = Font1, command=lambda a=2, b='Orange', c=2, d=1, e='Front Spot Lights', f=2: SW_ON(a, b, c, d, e, f), bg='bisque2', height = 1, width = 16)
Button_Press[2].grid(row=1,column=1)
#Switch 3 - Brush Buard Spot Lights - Mode Type Selection
Button_Press[3] = Button(Gui, text='Front Spot Mode', font = Font1, command = SWITCH3, fg='Green', bg='bisque2', height = 1, width = 16, state = 'disabled')
Button_Press[3].grid(row=1,column=2)

#Column Spacer
Text0 = Label(Gui,text='', font = Font1, bg='#818589', fg='green', padx = 0)
Text0.grid(row=2,column=0)

#Switch 5 - Rear LED Light Bar
Button_Press[5] = Button(Gui, text='Rear Light Bar', font = Font1, command=lambda a=5, b='Yellow', c=4, d=0, e='Rear Light Bar', f=0: SW_ON(a, b, c, d, e, f), bg='bisque2', height = 1, width = 16)
Button_Press[5].grid(row=3,column=0)
#Switch 6 - Rear Facing Signal Bar - Main Power
Button_Press[6] = Button(Gui, text='Rear Signal Bar', font = Font1, command=lambda a=6, b='Orange', c=4, d=1, e='Rear Signal Bar', f=1: SW_ON(a, b, c, d, e, f), bg='bisque2', height = 1, width = 16)
Button_Press[6].grid(row=3,column=1)
#Switch 7 - Rear Facing Signal Bar - Mode Type Selection
Button_Press[7] = Button(Gui, text='Signal Bar Mode', font = Font1, command = SWITCH7, fg='Green', bg='bisque2', height = 1, width = 16, state = 'disabled')
Button_Press[7].grid(row=3,column=2)

#Column Spacer
Text0 = Label(Gui,text='', font = Font1, bg='#818589', fg='green', padx = 0)
Text0.grid(row=4,column=0)

#Switch 8 - Activation Front TRE Electric Differential Locker - M205 @ 3.36
Button_Press[8] = Button(Gui, text='Front Diff Lock', font = Font1, command=lambda a=8, b='#899499', c=6, d=0, e='Front Diff Lock', f=0: SW_ON(a, b, c, d, e, f), bg='bisque2', height = 1, width = 16)
Button_Press[8].grid(row=5,column=0)
#Switch 9 - Activation Rear TRE Electric Differential Locker - R230 @ 3.36
Button_Press[9] = Button(Gui, text='Rear Diff Lock', font = Font1, command=lambda a=9, b='#899499', c=6, d=1, e='Rear Diff Lock', f=0: SW_ON(a, b, c, d, e, f), bg='bisque2', height = 1, width = 16)
Button_Press[9].grid(row=5,column=1)
#Switch 10 - Remote Power Signal for Xantrex 2000W A/C Pure Sine Wave Inverter under the passenger seat
Button_Press[10] = Button(Gui, text='A/C Inverter', font = Font1, command=lambda a=10, b='#899499', c=6, d=2, e='A/C Inverter', f=0: SW_ON(a, b, c, d, e, f), bg='bisque2', height = 1, width = 16)
Button_Press[10].grid(row=5,column=2)

#Column Spacer
Text0 = Label(Gui,text='', font = Font1, bg='#818589', fg='green', padx = 0)
Text0.grid(row=6,column=0)

#Switch 11 - Main Power for In-Cab Winch Controls
Button_Press[11] = Button(Gui, text='Winch Controls', font = Font1, command=lambda a=11, b='Red', c=8, d=0, e='Winch Controls', f=3: SW_ON(a, b, c, d, e, f), bg='bisque2', height = 1, width = 16)
Button_Press[11].grid(row=7,column=0)


#Switch 12 - Winch Signal - Feed Line In
Button_Press[12] = Button(Gui, text='Winch Feed IN', font = Font1, command = GPIO.output(SWITCH[12], GPIO.HIGH), bg='bisque2', height = 1, width = 16, state = "disabled")
Button_Press[12].grid(row=7,column=1)
Button_Press[12].bind("<ButtonPress>", lambda event : button12_pressed())
Button_Press[12].bind("<ButtonRelease>", lambda event : button12_released())


#Switch 13 - Winch Signal - Feed Line Out
Button_Press[13] = Button(Gui, text='Winch Feed OUT', font = Font1, command = GPIO.output(SWITCH[13], GPIO.HIGH), bg='bisque2', height = 1, width = 16, state = "disabled")
Button_Press[13].grid(row=7,column=2)
Button_Press[13].bind("<ButtonPress>", lambda event : button13_pressed())
Button_Press[13].bind("<ButtonRelease>", lambda event : button13_released())



Text0 = Label(Gui,text='', font = Font1, bg='#818589', fg='green', padx = 0)
Text0.grid(row=8,column=0)

Text3 = Label(Gui,text='-----', font = Font1, bg = '#000000', fg='#FFFFFF', padx = 10, pady = 10)
Text3.grid(row=9,columnspan=4)

Gui.mainloop()
Reply
#2
I don't know if this is what you're after but, a quick example of using radiobuttons to disable buttons.

import tkinter as tk 


root = tk.Tk()
root.geometry('800x400+350+350')

# Set the default value to off for correct radiobutton selected to be on
state = tk.StringVar(value='off')

# Function for changing button state
def changestate(change):
    if change == 'on':
        button1['state'] = 'disabled'
        button2['state'] = 'disabled'

    else:
        button1['state'] = 'normal'
        button2['state'] = 'normal'

# Container for radiobuttons
top = tk.LabelFrame(root, text='Safety Toggle')
top.pack(side='top', padx=5, pady=5, fill='x')

# Radiobuttons to toggle button state. Set values to have a default setting
on = tk.Radiobutton(top, text='ON', variable=state, value='off')
on['command'] = lambda: changestate('on')
on.pack(side='left', padx=5, pady=5)

off = tk.Radiobutton(top, text='OFF', variable=state, value='on')
off['command'] = lambda: changestate('off')
off.pack(side='left', padx=5, pady=5)

# Container for buttons
buttons = tk.LabelFrame(root, text='Buttons')
buttons.pack(side='top', padx=5, pady=5, fill='x')

# Create buttons with default state of disabled
button1 = tk.Button(buttons, text='Button 1', state='disabled')
button1.pack(side='left', padx=5, pady=5)

button2 = tk.Button(buttons, text='Button 2', state='disabled')
button2.pack(side='left', padx=5, pady=5)

root.mainloop()
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags


Reply
#3
The "<ButtonPresss>" event is a mouse button press, not the button widget press. You can see this if you run the code below. Clicking on a label generates a <ButtonPress> event.
import tkinter as tk


class Window(tk.Tk):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        button = tk.Button(self, text="Push me", state=tk.DISABLED, command=lambda: print("Clicked"))
        button.pack(padx=50, pady=50)
        button.bind("<ButtonPress>", lambda event: print("Pressed"))
        label = tk.Label(self, text="Me too!")
        label.pack(padx=50, pady=50)
        label.bind("<ButtonPress>", lambda event: print("Label Pressed"))


Window().mainloop()
What you want is not a normal button behavior, so you'll have to write it yourself. The code below implements a button that calls a command when pressed and when released. The button cannot be pressed when disabled. The code also shows how you can make a toggle button and a button for stepping through a series of switch values.
import tkinter as tk


FRONT_LIGHT_BAR = 1
FRONT_SPOT_LIGHTS = 2
FRONT_SPOT_MODE = 3
REAR_LED_BAR = 4
REAR_SIGNAL_BAR = 5
REAR_BAR_MODE = 6
WINCH_IN = 7
WINCH_OUT = 8


class OnOffButton(tk.Button):
    """A button that sends a message when pressed and released."""
    def __init__(self, *args, command=None, **kwargs):
        super().__init__(*args, **kwargs)
        self.command = command
        self.bg = self["bg"]
        self.bind("<ButtonPress>", lambda x: self._button_event(True))
        self.bind("<ButtonRelease>", lambda x: self._button_event(False))

    def _button_event(self, value):
        """Called when mouse button pressed when hovering over self."""
        if self["state"] != tk.DISABLED and self.command:
            self["bg"] = self["activebackground"] if value else self.bg
            self.command(value)


class ToggleButton(tk.Button):
    """A button that toggles between on and off."""
    def __init__(self, *args, command=None, **kwargs):
        super().__init__(*args, command=self._clicked, **kwargs)
        self.command = command
        self.bg = self["bg"]
        self._value = False

    def _clicked(self):
        """Called when button is clicked.  Toggle value and call command."""
        self.value = not self.value

    @property
    def value(self):
        """Get toggled state.  True is if button is pressed, False if button is released."""
        return self._value

    @value.setter
    def value(self, value):
        """Set toggled state."""
        self._value = value
        self["bg"] = self["activebackground"] if value else self.bg
        self["relief"] = tk.SUNKEN if value else tk.RAISED
        if self.command:
            self.command(self.value)


class SwitchButton(tk.Button):
    """A button for stepping through a series of values."""
    def __init__(self, *args, text, values, command=None, **kwargs):
        super().__init__(*args, command=self._clicked, **kwargs)
        self.text = text
        self.command = command
        self.values = values
        self.value = values[0]

    def _clicked(self):
        """Called when button is clicked.  Toggle on and call command."""
        self._index = (self._index + 1) % len(self.values)
        self.value = self.values[self._index]

    @property
    def value(self):
        """Get selected value."""
        return self.values[self._index]

    @value.setter
    def value(self, value):
        """Set selected value."""
        self._index = self.values.index(value)
        self["text"] = f"{self.text}: {self.value}"
        if self.command:
            self.command(self.value)


class Window(tk.Tk):
    """Make a window with some buttons for demonstration purposes."""
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.front_light_bar = ToggleButton(
            self, text='Front Light Bar', activebackground="Yellow",
            command=lambda on: self._set_output(on, FRONT_LIGHT_BAR)
        )
        self.front_light_bar.grid(row=0, column=0, sticky="news")

        self.front_spot_lights = ToggleButton(
            self, text='Front Spot Lights', activebackground="Orange",
            command=lambda on: self._set_output(on, FRONT_SPOT_LIGHTS)
        )
        self.front_spot_lights.grid(row=0, column=1, sticky="news")

        self.front_spot_mode = SwitchButton(
            self, text='Front Spot Mode', bg="lightblue", values=list(range(1, 8)),
            command=lambda value: self._set_switch(value, FRONT_SPOT_MODE)
        )
        self.front_spot_mode.grid(row=0, column=2, sticky="news")

        self.rear_led_bar = ToggleButton(
            self, text='Rear LED Bar', activebackground="Yellow",
            command=lambda on: self._set_output(on, FRONT_LIGHT_BAR)
        )
        self.rear_led_bar.grid(row=1, column=0, sticky="news")

        self.rear_signal_bar = ToggleButton(
            self, text='Rear Signal Bar', activebackground="Orange",
            command=lambda on: self._set_output(on, REAR_SIGNAL_BAR)
        )
        self.rear_signal_bar.grid(row=1, column=1, sticky="news")

        self.rear_bar_mode = SwitchButton(
            self, text='Rear Bar Mode', bg="lightblue", values=list(range(1, 10)),
            command=lambda value: self._set_switch(value, FRONT_SPOT_MODE)
        )
        self.rear_bar_mode.grid(row=1, column=2, sticky="news")

        self.enable_winch = ToggleButton(
            self, text='Enable Winch', activebackground="yellow",
            command=self._enable_winch
        )
        self.enable_winch.grid(row=2, column=0, sticky="news")

        self.winch_in = OnOffButton(
            self, text="Winch IN", state=tk.DISABLED, activebackground="red",
            command=lambda on: self._set_output(on, WINCH_IN))
        self.winch_in.grid(row=2, column=1, sticky="news")

        self.winch_out = OnOffButton(
            self, text="Winch OUT", state=tk.DISABLED, activebackground="red",
            command=lambda on: self._set_output(on, WINCH_OUT))
        self.winch_out.grid(row=2, column=2, sticky="news")

    def _set_output(self, on, pin):
        """Set a pin output."""
        print(f"Setting pin {pin} to {on}")

    def _set_switch(self, value, switch):
        """Set a switch output."""
        print(f"Setting switch {switch} to {value}")

    def _enable_winch(self, on):
        """Enable/disable the winch."""
        state = tk.NORMAL if on else tk.DISABLED
        self.winch_in["state"] = state
        self.winch_out["state"] = state


Window().mainloop()
Reply
#4
# Importing Libraries
import RPi.GPIO as GPIO
from tkinter import *
import tkinter.font

# GPIO Setup
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)

# Global Variables
SWITCH = [i for i in range(14)]
Button_Press = [0 for _ in range(14)]
Output_State = [0 for _ in range(14)]
SBC = [0]

# GUI Initialization
Gui = Tk()
Gui.title("---")
Gui.config(background="#818589")
Gui.minsize(800, 600)
Font1 = tkinter.font.Font(family='Helvetica', size=18, weight='bold')

# Function to toggle switch state
def toggle_switch(pin):
    Output_State[pin] = not Output_State[pin]
    GPIO.output(SWITCH[pin], GPIO.HIGH if Output_State[pin] else GPIO.LOW)

# Function to handle button press
def button_pressed(pin):
    Button_Press[pin]["state"] = "disabled"
    toggle_switch(pin)

# Function to handle button release
def button_released(pin):
    Button_Press[pin]["state"] = "normal"
    toggle_switch(pin)

# GUI Controls
for i in range(14):
    SWITCH[i] = i
    GPIO.setup(SWITCH[i], GPIO.OUT)
    GPIO.output(SWITCH[i], GPIO.LOW)

    Button_Press[i] = Button(Gui, text=f'Switch {i}', font=Font1,
                             command=lambda pin=i: button_pressed(pin),
                             bg='bisque2', height=1, width=16)
    Button_Press[i].grid(row=i, column=0)

# Update the GUI
Gui.mainloop()
This code focuses on improving readability, organization, and functionality. Make sure to customize it according to your specific requirements and hardware setup. scratch geometry dash
deanhystad write Mar-13-2024, 07:16 PM:
Please post all code, output and errors (it it's entirety) between their respective tags. Refer to BBCode help topic on how to post. Use the "Preview Post" button to make sure the code is presented as you expect before hitting the "Post Reply/Thread" button.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Debugger Disabled erictan 1 3,999 Apr-30-2020, 02:17 PM
Last Post: pyzyx3qwerty
  Program Functions Disabled Doffer 0 1,681 Sep-14-2019, 07:35 PM
Last Post: Doffer
  How to turn screen output into clickable hyperlinks windros 5 2,756 Jan-22-2019, 05:41 PM
Last Post: windros
  Buttons or Radio Buttons on Tkinter Treeview draems 0 3,405 Oct-31-2017, 04:06 AM
Last Post: draems
  Having my output links become clickable bigmit37 8 29,864 May-25-2017, 08:17 PM
Last Post: bigmit37

Forum Jump:

User Panel Messages

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