1

I try to code a to-do-list. When I click on the button self.button_check to change the color from a white entry to green it works until I create a second entry. The Problem is the new entry with the new check.button also look for the variable self.check_var which is used for the first. My Question is now: Is there a way to check the current bg color of an entry, to change if the entry is white or green instead of using the self.check_var variable?

Sry for obvious mistakes, I just started coding

import tkinter as tk
from tkinter.constants import ANCHOR, CENTER, X

class App():

    def __init__(self):
        self.window = tk.Tk()
        self.window.title("To-Do-List")
        self.window.geometry("700x700")

        self.x_but, self.y_but = 0.05, 0.2 
        self.x_ent, self.y_ent = 0.05, 0.2
        self.x_but2 = 0.3
        self.check_var = True

        self.start_frame()
        self.grid1()
        self.window.mainloop()
        
    def start_frame(self):
        self.label1 = tk.Label(text="To Do List", font=("", 30))
        self.label1.place(relx=0.5, rely=0.05, anchor=CENTER)

    def grid1(self):
        self.button1 = tk.Button(text="Create", command= self.create_field)
        self.button1.place(relx = self.x_but, rely= self.y_but)

    def create_field(self):
        self.y_but += 0.05
        self.button1.place(relx= self.x_but, rely= self.y_but)

        self.entry1 = tk.Entry()
        self.entry1.place(relx= self.x_ent, rely= self.y_ent)
        x = self.entry1
        

        self.button_check = tk.Button(text="✅", height= 1,command=lambda x=self.entry1: self.check(x))
        self.button_check.place(relx= self.x_but2, rely=self.y_ent)
       



        self.y_ent += 0.05
    
    def check(self,ent):
        if self.check_var:
            ent.configure(bg="Green")
            self.check_var = False

        else:
            ent.configure(bg="White")
            self.check_var = True

    

app = App()
antal
  • 45
  • 4

2 Answers2

2

You can assign the value to the Entry itself:

def create_field(self):
    self.y_but += 0.05
    self.button1.place(relx= self.x_but, rely= self.y_but)
    
    # you don't need to use `self.` here necessarily, the instance attribute will get
    # overwritten anyways when you create a new `Entry`, same for button below
    entry = tk.Entry()
    entry.place(relx= self.x_ent, rely= self.y_ent)

    # create an attribute for the `Entry` and set it to True
    # when you pass the `Entry` to the function, you will be able to 
    # access this attribute too
    entry.check_var = True

    button_check = tk.Button(text="✅", height= 1,command=lambda x=entry: self.check(x))
    button_check.place(relx= self.x_but2, rely=self.y_ent)

    self.y_ent += 0.05

Then change your check method:

# add this decorator because the method doesn't require `self` anymore
@staticmethod
def check(ent):
    # here you can access the `Entry`'s attribute `check_var`
    # which was created in the other method
    if ent.check_var:
        ent.configure(bg="Green")
        ent.check_var = False

    else:
        ent.configure(bg="White")
        ent.check_var = True

Also I suggest that you use pack instead of place, as it is much easier to use in such cases (kind of, may be harder to adapt now). And you don't need to use the constants from tkinter, they just contain a string of the same name, so you can easily just use the string instead, for example: anchor='center'.

Matiiss
  • 5,970
  • 2
  • 12
  • 29
  • 1
    Even though I think this is an elegant way to acomplish this task. *I just started coding* makes me think this answer needs either a lot more context on lambda, references, decorators and namespaces or it should be more self-explanatory and intuitiv. – Thingamabobs Jan 01 '22 at 20:29
  • @Thingamabobs they used `lambda` themselves, about the decorator, maybe, but it would literally be that one would use that decorator when you don't need to reference the instance or the class, basically when you don't use `self` or `cls` in a method, use `@statimethod`, namespaces? what about that and references, well I kinda said that you would create an attribute for an instance and assign a value to it, what exactly am I supposed to explain more in detail about that? also they have accepted the answer and haven't asked for clarification, so I would think that they understand most of this – Matiiss Jan 01 '22 at 20:32
  • The use of lambda was recommanded in this [question](https://stackoverflow.com/q/70550852/13629335) and wasnt explained well. I'm not sure if they do know what this code does. At this point I doubt there is a lot of knowlege about namespaces and references, explicitly *why* they use the syntax self and why they can leave it this way here. The use of a [static method](https://docs.python.org/3/library/functions.html#staticmethod) isnt necessarily and over all a code styling choice to indicate the use of it. It seems more confusing than helping in this case in my opinion. – Thingamabobs Jan 01 '22 at 20:42
  • @Thingamabobs I very much doubt that I should explain what `lambda` or `self` does, that was not asked about whatsoever and there are many resources explaining both of those things (for example this [question about `self`](https://stackoverflow.com/questions/2709821/what-is-the-purpose-of-the-word-self)). I believe I need to explain only the stuff that I add on top: `@staticmethod` (which isn't necessary but I believe I explained it pretty well (in the answer and in the above comment)) and creating attributes, which can be explained together with the usage of `self` – Matiiss Jan 01 '22 at 20:56
  • @Thingamabobs if they don't understand something in my answer, they can ask about it, they haven't, if they need an explanation in detail, they are better off looking for it on the internet and looking at tutorials, documentation or other questions and answers that can explain those concepts far better and more in detail than I could in my answer or comments because those resources are more refined over time, I don't see how I need to explain to them what they seem to already know – Matiiss Jan 01 '22 at 21:00
  • @Thingamabobs I am not offended, I just don't understand what I need more to explain, I am not about only strictly answering the question, if I see that the code can be easily improved, I will show how to, but here based on their code, the concepts I used seem pretty self-explanatory, the only difference is that instead of using `self.` to access the object itself, I used `entry.` to access another object, and if they understand the concept of `self` (which they can find plenty of information about), they should understand using other names to refer to other objects. Anyways. Happy New Year! – Matiiss Jan 01 '22 at 21:10
  • 1
    For me, a beginner, everything is hard if u learn it for the first time. In my code I used a class method for the first time, because I tried to code it without a class and it was awful long and complicated. I give my best to understand everything and I'm grateful for every tip :) Basically I believe for now that the static method can be used if I don't need a self. That's all I got to know for now. I surely will understand more when I need it later. – antal Jan 01 '22 at 21:17
  • 1
    @antal maybe Matiiss is right and a pointer in the right direction, to challange the people to make their own research, is better than a lot of explanation that no one ever reads till its needed. – Thingamabobs Jan 01 '22 at 21:20
  • I guess I need to learn about attribute now. I don't understand when I can create attributes and what exactly they do. But that's my Job to research :) – antal Jan 01 '22 at 21:23
  • @Matiiss I think I understood your answer now :) So now I want to make a second row of the exact code I did. Do I really need to rewrite the whole code or is there a more efficient way? Could you summarise how I do that? I don't want specific code, just a description how I do it the easiest way. I would be grateful :) – antal Jan 01 '22 at 22:01
  • 1
    @antal you want to start placing entries in a second column? well, you check for when `self.y_ent` reaches a certain value and then you change the value of `self.x_ent`, so in the method that creates entries add an `if` statement for example `if self.y_ent > 0.3: self.x_ent += 0.4 self.y_ent = 0.2` and do the same for button coordinates, but I would suggest for sth like this to use `grid` – Matiiss Jan 01 '22 at 22:05
  • 1
    @antal if you would take the advice from Matiiss to use pack or grid, things can get easy and exciting. [For exampel](https://stackoverflow.com/questions/3085696/adding-a-scrollbar-to-a-group-of-widgets-in-tkinter) – Thingamabobs Jan 01 '22 at 22:07
  • Yes I try to do it with grid, because the if state is executed every time I press the Create button, so the values of self.y_ent etc gets increased every time. Thanks a lot ! – antal Jan 01 '22 at 22:19
2

What I think you could do here is instead of using a global check_var you can store the entry object and check_var as key and value in a dictionary. This way, since it is a to-do app, you can easily get the value by indexing the dictionary it with the entry object.

class App():
    def __init__(self):
        ....
        self.info = {} # Create a dictionary
    
    def create_field(self):
        .....
        self.entry1 = tk.Entry()
        self.entry1.place(relx= self.x_ent, rely= self.y_ent)
        

        self.button_check = tk.Button(text="✅", height= 1,command=lambda x=self.entry1: self.check(x))
        self.button_check.place(relx= self.x_but2, rely=self.y_ent)
       
        self.info[self.entry1] = False # Add the item to the dictionary
    
    def check(self,ent):
        check_var = self.info[ent]
        
        if not check_var: ent.configure(bg="Green")
        else: ent.configure(bg="White")

        self.info[ent] = not check_var

Also this way if you want to expand the app in future, you can just add more information into your dictionary related to the given task, like the task text itself. Perhaps something like:

def create_field(self):
    self.y_but += 0.05
    self.button1.place(relx= self.x_but, rely= self.y_but)

    self.entry1 = tk.Entry()
    self.entry1.place(relx= self.x_ent, rely= self.y_ent)
    
    tk.Button(text="✅", height= 1,command=lambda x=self.entry1: self.check(x)).place(relx= self.x_but2, rely=self.y_ent)
    tk.Button(text="Get details", height= 1,command=lambda x=self.entry1: self.get_details(x)).place(relx= self.x_but2+0.05, rely=self.y_ent)
    tk.Button(text="Update details", height= 1,command=lambda x=self.entry1: self.update_details(x)).place(relx= self.x_but2+0.17, rely=self.y_ent)
    
    data = '', False
    self.info[self.entry1] = data

    self.y_ent += 0.05

def check(self,ent):
    idx       = self.info[ent]
    check_var = idx[1]

    if not check_var:
        ent.configure(bg="Green")
        self.info[ent] = ent.get(),True

    else:
        ent.configure(bg="White")
        self.info[ent] = ent.get(),False
        
def get_details(self,ent):
    idx       = self.info[ent] # Get the details
    text      = idx[0]
    check_var = idx[1]
    print(f'The task is: {text} and it is {"completed" if check_var else "incomplete"}') # Print the details

def update_details(self,ent):
    idx       = self.info[ent] # Get the item
    check_var = idx[1] # Get the flag
    data      = ent.get(), check_var # Make a tuple of data
    
    self.info[ent] = data # Update the info
Delrius Euphoria
  • 14,910
  • 3
  • 15
  • 46