pythontkintertkinter-entrytkinter.checkbutton

How to disable an Entry on tkinter when a Checkbutton is unchecked using for loop and dictionary


I'm working on a Windows application for my work and my knowledge on Python is very limited, so I didn't know how to figure this out :

I want to make each entry widget inside the frame disabled when its checkbutton is unchecked and enabled when its checkbutton is checked and make them all checked by default. I tried this code that I took part of it from this tutorial but it didn't work as expected.

from tkinter import *

class Principal():
    def __init__(self):
        self.root= Tk()
        self.root.geometry('800x800') 

        self.prixArticlesFrame = LabelFrame(self.root, text="Prix des articles", width=2000)
        self.prixArticlesFrame.place(relx = 0.75, rely = 0.15,anchor = N)

        self.DefautArticlesCheckButton={
            "Deblai" : IntVar(),
            "Remblai" : IntVar(),
            "Blocage" : IntVar(),
            "Beton de propreté": IntVar(),
            "Beton armé": IntVar(),
            "Acier à haute adherence": IntVar(),
            "Joint de dilatation": IntVar(),
            
        }
        for index, (key, value) in enumerate(self.DefautArticlesCheckButton.items()):
            self.cur_check = Checkbutton(self.prixArticlesFrame, 
            text=key,variable=self.DefautArticlesCheckButton[key], onvalue=1, 
            offvalue=0,command=self.ArticlesEntryState) 
            self.cur_check.grid(row=index, column=0, padx=10, pady=10, sticky="W")

        self.DefautArticlesEntries={
            "Deblai" : StringVar(),
            "Remblai" : StringVar(),
            "Blocage" : StringVar(),
            "Beton de propreté": StringVar(),
            "Beton armé": StringVar(),
            "Acier à haute adherence": StringVar(),
            "Joint de dilatation": StringVar(),
            
        }

        for index, (key, value) in enumerate(self.DefautArticlesEntries.items()):
            self.cur_entry = 'DefautArticlesEntries' +key
            
            self.cur_entry=Entry(self.prixArticlesFrame,width=10,
                          textvariable=self.DefautArticlesEntries[key])
            self.cur_entry.grid(row=index,column=2,padx=10)
            self.cur_entry.config(state=NORMAL)
        self.root.mainloop()
    
    def ArticlesEntryState(self):
        for index, (key, value) in enumerate(self.DefautArticlesCheckButton.items()):
            
            if self.DefautArticlesCheckButton[key].get() == 1:
                self.cur_entry.grid(row=index,column=2,padx=10)
                self.cur_entry.config(state=NORMAL)

            elif self.DefautArticlesCheckButton[key].get() == 0:
                self.cur_entry.config(state=DISABLED)

app = Principal()

here is what I got in gif image

What I have done wrong? Thanks


Solution

  • Since you want all buttons checked by default, all variables should have an initial value that matches their onvalue.

    Besides, both dicts refer to the variables rather than the actual widgets, so each Entry must be bound to the scope of the corresponding IntVar trace callback - otherwise, only the last loop item is referenced:

            for index, (key, value) in enumerate(self.DefautArticlesCheckButton.items()):
                value.set(1)
                check = Checkbutton(self.prixArticlesFrame, text=key, variable=value)
                check.grid(row=index, column=0, padx=10, pady=10, sticky="W")
    
            # ...
    
            for index, (key, value) in enumerate(self.DefautArticlesEntries.items()):
                entry = Entry(self.prixArticlesFrame, width=10, textvariable=value)
                entry.grid(row=index, column=1, padx=10)
    
                var = self.DefautArticlesCheckButton[key]
                handler = lambda *_, e=entry, v=var: e.config(state=NORMAL if v.get() else DISABLED)
                var.trace('w', handler)