1

I am trying to build my first GUI using tkinter for a project I am working on at work. I have multiple pairs of checkbuttons (labeled Rule and Trace in example below) that are dependent on each other. By default, all Rule checkbuttons are selected while all Trace checkbuttons are unselected. If I want to turn off a particular Rule checkbutton, it should turn red indicating it is unselected (or turned off). And if clicked again, it should turn green indicating it is selected (or turned on). I have written a function checkbutton_state to handle just that. Below is a working example of what I am trying to do. But my problem is, this function is applicable only for Rule 1 ON/OFF checkbutton. How can I generalize it so that I can use the same function for all the Rule checkbuttons?

Also, when a Particular rule is turned off, its corresponding Trace checkbutton should be turned off automatically and user should not be able to turn it on. If the rule checkbutton is turned on, then the corresponding checkbutton should be turned off but user should be able to turn it on if required.

I have tried using the 'lambda' method to try and generalize the function but not work. For automatically changing the status of the 'Trace' checkbutton, I have tried using the toggle() function but somehow its not giving me the desired result. I have been trying to find a solution to this the entire day. Any guidance on how I can proceed with this would be really appreciated.

My Python version is 3.7.1 and OS is Windows 10 (64 bit).

# Import requrired libraries/packages
from tkinter import Tk, Checkbutton, IntVar

# Create intance of tkinter
root = Tk()

# Define variables
rule1_on_choice = IntVar()
rule1_trace_choice = IntVar()
rule2_on_choice = IntVar()
rule2_trace_choice = IntVar()

# Create first set of checkbutton
# The 'trace' checkbutton is dependent on the 'on' checkbutton
# If the 'on' checkbutton is turned ON, 'trace' checkbutton should be turned off but user should be able to turn it on again
# If the 'on' checkbutton is turned OFF, 'trace' checkbutton should also be turned off but user should not be able to turn it on
rule1_on_checkbutton = Checkbutton(root, text = "Rule 1 ON", indicatoron = 0, bg = "green", variable = rule1_on_choice, onvalue = 1, offvalue = 0, selectcolor = "green")
rule1_trace_checkbutton = Checkbutton(root, text = "Rule 1 Trace", indicatoron = 0, bg = "red", variable = rule1_trace_choice, onvalue = 1, offvalue = 0, selectcolor = "red")

# Create second set of checkbuttons
rule2_on_checkbutton = Checkbutton(root, text = "Rule 2 ON", indicatoron = 0, bg = "green", variable = rule2_on_choice, onvalue = 1, offvalue = 0, selectcolor = "green")
rule2_trace_checkbutton = Checkbutton(root, text = "Rule 2 Trace", indicatoron = 0, bg = "red", variable = rule2_trace_choice, onvalue = 1, offvalue = 0, selectcolor = "red")

# The 'on' checkbuttons are turned on by default
# The 'trace' checkbuttons are turned off by default
rule1_on_checkbutton.select()
rule1_trace_checkbutton.deselect()
rule2_on_checkbutton.select()
rule2_trace_checkbutton.deselect()

rule1_on_checkbutton.pack()
rule1_trace_checkbutton.pack()
rule2_on_checkbutton.pack()
rule2_trace_checkbutton.pack()

# Function to change text and color of checkbutton
# If a Rule checkbutton is clicked, should turn green whith text showing "ON"
# The same checkbutton is clicked, it should now turn red with text showing "OFF"
def checkbutton_state(event = None):

    if rule1_on_choice.get() == 1:
        rule1_on_checkbutton.configure(text = "Rule 1 OFF", bg = "red", fg = "grey")

    else:
        rule1_on_checkbutton.configure(text = "Rule 1 ON", bg = "green", fg = "white")

# Binding function to Rule 1 'on' checkbutton
rule1_on_checkbutton.bind("<Button-1>", checkbutton_state)

root.mainloop()
1

1 Answer 1

2

It is quite difficult to fully generalize your function without using lambda. You could do something like:

def checkbutton_state(event=None):
    if rule1_on_choice.get() == 1:
        event.widget.configure(text = event.widget['text'][0:6] + " OFF", bg = "red", fg = "grey")

    else:
        event.widget.configure(text = event.widget['text'][0:6] + " ON", bg = "green", fg = "white")

# Binding function to Rule 1 'on' checkbutton
rule1_on_checkbutton.bind("<Button-1>", checkbutton_state)
rule2_on_checkbutton.bind("<Button-1>", checkbutton_state)

If you also want to generalize "rule1_on_choice", then you have to go for lambda, specially for the algorithm you are talking about in the second part of your question. The following code completely generalizes checkbutton_state:

# Using lambda
def checkbutton_state_lambda(event, rule_on_choice, text_desc):
    if rule_on_choice.get() == 1:
        event.widget.configure(text = text_desc + " OFF", bg = "red", fg = "grey")

    else:
        event.widget.configure(text = text_desc + " ON", bg = "green", fg = "white")

# Binding function to Rule 1 'on' checkbutton
rule1_on_checkbutton.bind("<Button-1>", lambda event, a=rule1_on_choice, b="Rule 1": checkbutton_state_lambda(event, a, b))
rule2_on_checkbutton.bind("<Button-1>", lambda event, a=rule2_on_choice, b="Rule 2": checkbutton_state_lambda(event, a, b))

To disable a button, you should set state to DISABLED. You can pass as an argument to the "Rule" button (when clicked) the "Trace" button and modify its state accordingly.

PS: I would better go for a class-oriented philosophy, where you have more freedom with the use of self.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for taking the time to look into my question. I tried the lambda method and it really works. Is there any way to configure multiple widgets? I mean, in place of event.widget.configure, is it possible to do something like 'event.widget1.configure', 'event.widget2.configure' within the if block?
@Code_Sipra you are welcome. Please consider accepting the answer to close the question. Regarding your question, yes absolutely. You could pass as an argument of the lambda method any widget you require and use it inside the function similarily to "rule_on_choice".
Thank you @David Duran. Exactly solution I was looking for. I have accepted the answer. The mistake I had made when using lambda was I had hardcoded rule1_on_choice. Also, I was not aware we could pass widgets too as you have in your solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.