Python Forum
Errors when trying to disable tkinter checkbutton
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Errors when trying to disable tkinter checkbutton
#1
    Hello,

    I am having an issue disabling a tkinter checkbutton. I am able to get the value, but when trying to disable it I keep getting the below errors. I have tried StringVar instead of IntVar, but get similar errors. Not sure what am doing wrong. Any help is greatly appreciated!

    

####################
class checkboxes():
####################

    #===================
    def __init__(self, master):
    #
    #===================

        self.master = master



    #===================
    def addCheckBox(self, txt, w, h, x, y, fnt, clr, cmd):
    #
    #===================

        self.var1 = tk.IntVar()

        self.chkbx= Checkbutton(self.master, width=w, height=h, text=txt, variable=self.var1, onvalue=1, offvalue=0, font=fnt, bg=clr, command=cmd)
        self.chkbx.config(state="normal") #this works here only
        self.chkbx.place(x=x, y=y)
        return self.var1

    #===================
    def chkbxClick(self):
    #
    #===================

        print (self)
        print(self, chkbxItemCard.get())

        if chkbxItemCard.get() == 1 or chkbxPicklist.get() == 1 or chkbxQuickPick.get() == 1:
            btnConvert["state"] = "normal"

        if chkbxItemCard.get() == 0 and chkbxPicklist.get() == 0 and chkbxQuickPick.get() == 0:
            btnConvert["state"] = "disabled"

    #      chkbxItemCard.config(state="disabled") #AttributeError: 'IntVar' object has no attribute 'config'
    #      chkbxItemCard["state"] = "disabled" #TypeError: 'IntVar' object does not support item assignment



        chkbxItemCard = checkboxes.addCheckBox(master, 'TestItemCard', 30, 1, 45, 470, 'Aerial, 15', 'AntiqueWhite1', lambda: checkboxes.chkbxClick("chkItemCard")) #self, txt, w, h, x, y, fnt, clr, cmd
Reply
#2
Please put python wrappers around code, not your entire post. Put error wrappers around error messages. Do not include them as comments inside your code.

And please don't do this.
    #===================
    def chkbxClick(self):
    #
    #===================
You may think it makes your code easier to read, but it does not for coders who follow PEP8 style guidelines. You should follow PEP8 style guidelines too.

https://pep8.org/

The code you posted is not the code that generated the errors. It generates completely different errors.

The error message says you are trying to disable an IntVar. An IntVar is not CheckButton. You cannot disable an IntVar. Removing the comment clutter might help you see that you are trying to disable an IntVar instead of a CheckButton.
Reply
#3
Apologies for the code layout.

Lines 46 or 47 are the lines causing the error regardless of where they are used. My assumption is how I am returning the CheckButton after creation with the IntVar. If I return the self.chkbx then my .get command doesn't work.
Reply
#4
The method checkboxes.addCheckBox returns an IntVar. It does not return a Checkbutton. You cannot disable an IntVar.

Your method checkboxes.chkbxClick is a mess. You must have a lot of global variables because all of the variables used here are undefined.

My guess is somewhere you do this:
root = Tk()
boxes = checkboxes(root)
chkbxItemCard = boxes.addCheckBox(..., cmd=boxes.chkbxClick)
chkbxItemCard is an IntVar. When you try to disable the IntVar you get an error because IntVar does not disable.

What is the checkboxes class supposed to do? As is, it doesn't appear to do anything other than define two functions. Here's an example that creates a Checkbox class that is a tk.Checkbutton with some extra features.
import tkinter as tk

class Checkbox(tk.Checkbutton):
    """I add an IntVar to a check button"""
    var_types = {bool:tk.BooleanVar, int:tk.IntVar, float:tk.DoubleVar}

    def __init__(self, parent, x, y, onvalue=1, offvalue=0, **kwargs):
        self.variable = self.var_types.get(type(onvalue), tk.StringVar)(value=offvalue)
        super().__init__(parent, variable=self.variable, onvalue=onvalue, offvalue=offvalue, **kwargs)
        self.place(x=x, y=y)

    def value(self):
        return self.variable.get()

    def set_value(self, value):
        self.variable.set(value)

class App(tk.Tk):
    def __init__(self, title=None, geometry="250x120", **kwargs):
        super().__init__(**kwargs)
        if geometry is not None:
            self.geometry(geometry)
        if title is not None:
            self.wm_title(title)

        self.printer = Checkbox(self,10, 10, text="Printer")
        self.printer.config(command=lambda: print(self.printer.value()))

        self.setter = Checkbox(self, 10, 50, text="Setter")
        self.setter.config(command=lambda: self.printer.set_value(self.setter.value()))

        self.enabler = Checkbox(self, 10, 90, text="Disabler", onvalue="disabled", offvalue="normal")
        self.enabler.config(command=lambda: self.printer.config(state=self.enabler.value()))

app = App("Checkboxes")
app.mainloop()
Reply
#5
Yeah I know its a mess, new to Python.

chkbxClick disables a button if none of the checkboxes are selected.
Thanks for the example and the explanation, appreciate it!
Reply
#6
I modified my example to enable/disable a button based on the value of multiple checkboxes.
import tkinter as tk

class Checkbox(tk.Checkbutton):
    """I add an IntVar to a check button"""
    var_types = {bool:tk.BooleanVar, int:tk.IntVar, float:tk.DoubleVar}

    def __init__(self, parent, x, y, onvalue=1, offvalue=0, **kwargs):
        self.variable = self.var_types.get(type(onvalue), tk.StringVar)(value=offvalue)
        super().__init__(parent, variable=self.variable, onvalue=onvalue, offvalue=offvalue, **kwargs)
        self.place(x=x, y=y)

    def value(self):
        return self.variable.get()

    def set_value(self, value):
        self.variable.set(value)

class App(tk.Tk):
    def __init__(self, title=None, geometry="250x120", **kwargs):
        super().__init__(**kwargs)
        if geometry is not None:
            self.geometry(geometry)
        if title is not None:
            self.wm_title(title)

        self.checkboxes = [
            Checkbox(self,10, 10, text="A"),
            Checkbox(self,10, 50, text="B"),
            Checkbox(self,10, 90, text="C")
        ]

        self.button = tk.Button(self, text="Push Me")
        self.button.place(x=100, y=10)

        for checkbox in self.checkboxes:
            checkbox.config(command = lambda: self.enable_control(self.button, self.checkboxes))

        self.enable_control(self.button, self.checkboxes)

    def enable_control(self, control, checkboxes):
        if any(checkbox.value() == 1 for checkbox in checkboxes):
            control.configure(state="normal")
        else:
            control.configure(state="disabled")

app = App("Checkboxes")
app.mainloop()
This might be even closer to what you want to do. The ConditionAction class is a more generic version of your checkboxes class. Here it is used to set the state of a button based on the value of three checkboxes.
import tkinter as tk
import operator as op

class Checkbox(tk.Checkbutton):
    """I add an IntVar to a check button"""
    var_types = {bool:tk.BooleanVar, int:tk.IntVar, float:tk.DoubleVar}

    def __init__(self, parent, x, y, onvalue=1, offvalue=0, **kwargs):
        self.variable = self.var_types.get(type(onvalue), tk.StringVar)(value=offvalue)
        super().__init__(parent, variable=self.variable, onvalue=onvalue, offvalue=offvalue, **kwargs)
        self.place(x=x, y=y)

    def value(self):
        return self.variable.get()

    def set_value(self, value):
        self.variable.set(value)

class ConditionAction:
    """
    I call a function based on one or more conditions.
    User provides functions for True(optional) and False (optional),
    and multiple conditions.  The True function is called if any
    of the conditions are True, else the False function is called.
    """
    def __init__(self, true_action=None, false_action=None):
        self.true_action = true_action
        self.false_action = false_action
        self.conditions = []

    def add_condition(self, condition):
        self.conditions.append(condition)
        return self

    def evaluate(self):
        for condition in self.conditions:
            if condition():
                if self.true_action is not None:
                    self.true_action()
                break
        else:
            if self.false_action is not None:
                self.false_action()

class App(tk.Tk):
    def __init__(self, title=None, geometry="250x120", **kwargs):
        super().__init__(**kwargs)
        if geometry is not None:
            self.geometry(geometry)
        if title is not None:
            self.wm_title(title)

        self.checkboxes = [
            Checkbox(self,10, 10, text="A"),
            Checkbox(self,10, 50, text="B"),
            Checkbox(self,10, 90, text="C")
        ]

        self.button = tk.Button(self, text="Push Me")
        self.button.place(x=100, y=10)

        action = ConditionAction(
            true_action = lambda:self.button.config(state="normal"),
            false_action = lambda:self.button.config(state="disabled"))
        for cb in self.checkboxes:
            action.add_condition(lambda x=cb: op.eq(x.value(), 1))
            cb.config(command=action.evaluate)
        action.evaluate()

app = App("Checkboxes")
app.mainloop()
Doing this generically or with a custom function will depend on how often you need to do it. If you have hundreds of similar dependencies it makes sense to write a generic tool. If you only need this once or twice it makes sense to write a function specific to each case.
BashBedlam likes this post
Reply
#7
Thanks deanhystad, that is exactly what I am trying to do! Now to try to understand it all. Really appreciate it!
Reply
#8
I used .place(x, y) to mimic your code. You should stop using .place() and use .pack() or .grid() instead.

Become a lazy typer. You should never type this many characters
        if chkbxItemCard.get() == 1 or chkbxPicklist.get() == 1 or chkbxQuickPick.get() == 1:
            btnConvert["state"] = "normal"
 
        if chkbxItemCard.get() == 0 and chkbxPicklist.get() == 0 and chkbxQuickPick.get() == 0:
            btnConvert["state"] = "disabled"
When you can type this instead:
if any(cb.get() == 1 for cb in (chkbxitemCard, chkbxPicklist, chkbxQyickPick)):
    btnConvert["state"] = "normal"
else:
    btnConver["state"] = "disabled"
Not only is it less typing, it has less ability to hide errors and is easier to read. My way makes it obvious that the two possibilities are any checked or none checked. Your way I have to look at each of the comparisons and each of the logic operations

Don't use mixed case variable names unless forced. Python style guidelines associate special meanings with capital letters, Try to follow the style laid out in https://pep8.org/. Start using it early and it will become second nature. Once you are used to the style you'll find it really helps you read Python code.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  an easy way to disable exception handling Skaperen 6 5,469 Jun-02-2019, 10:38 PM
Last Post: Gribouillis
  disable a block of code Skaperen 5 13,274 Aug-20-2018, 07:55 AM
Last Post: Skaperen
  gnureadline: disable temporarily? klaymen 1 2,467 May-08-2018, 11:16 AM
Last Post: Larz60+
  Checkbutton code not working ToddRyler 4 3,183 Dec-24-2017, 12:34 AM
Last Post: Larz60+

Forum Jump:

User Panel Messages

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