Variables not being found despite being defined?

Question:

I am quite new to Python. I am trying to use tkinter for a basic program I am trying to set up. The initial code looks like this…

import tkinter as tk
from datetime import datetime as dt

# Create the main window, Title, and the size
root = tk.Tk()
root.title('My App')
root.geometry('800x600')
root.configure(bg='light blue')

# create a label for date and time
date_label = tk.Label(root, text='', font=('Times New Roman', 14))
date_label.place(relx=0.11, rely=0.0, anchor='ne')
date_label.config(bg='light blue')

# Define a function to update date and time
def update_date_time():
    now = dt.now()
    current_time = now.strftime('%I:%M %p')
    current_date = now.strftime('%d/%m/%Y')

    # Update the date and time label
    date_label.config(text=f"{current_date}n{current_time}")

    # Schedule the function to be called after 1 second
    root.after(1000, update_date_time)

# Call the update_date_time function to start updating the label
update_date_time()

# Create entry widget for text
input = tk.Text(root, width=25, height=5)
input.place(relx=0.5, rely=0.5, anchor='center')

# Start the main loop of the application
root.mainloop()

This works as intended, however I felt it looked messy, and I have essentially only added to widgets.
Thus (this is where my beginner will show) I wanted to organize my widgets by classes as such…

import tkinter as tk
from datetime import datetime as dt

# Create the main window, Title, and the size
root = tk.Tk()
root.title('My App')
root.geometry('800x600')
root.configure(bg='light blue')

class TimeWgt():
    # create a label for date and time
    date_label = tk.Label(root, text='', font=('Times New Roman', 14))
    date_label.place(relx=0.11, rely=0.0, anchor='ne')
    date_label.config(bg='light blue')

    # Define a function to update date and time
    def update_date_time():
        now = dt.now()
        current_time = now.strftime('%I:%M %p')
        current_date = now.strftime('%d/%m/%Y')

        # Update the date and time label
        date_label.config(text=f"{current_date}n{current_time}")

        # Schedule the function to be called after 1 second
        root.after(1000, update_date_time)

    # Call the update_date_time function to start updating the label
    update_date_time()

# Create entry widget for text
input = tk.Text(root, width=25, height=5)
input.place(relx=0.5, rely=0.5, anchor='center')

# Start the main loop of the application
root.mainloop()

After creating the class for TimeWgt, it says date_label is no longer defined, as nor my update_date_time function.

I am sure that this is just through my ignorance of code, and is a simple fix. Just wondering why it does this, and if there are suggestion on how to organize code better to make it more readable. Thank you!

P.S. was having issues with datetime.now module, which is why datetime is imported in the manner that it is.

Asked By: Ambre

||

Answers:

Scope date_label to the instance of the TimeWgt class like so, using self:

class TimeWgt:
    def __init__(self, root):  # add the '__init__' method
        # this special method runs automatically whenever this class is instantiated!
        self.root = root
        # create a label for date and time
        self.date_label = tk.Label(self.root, text='', font=('Times New Roman', 14))
        self.date_label.place(relx=0.11, rely=0.0, anchor='ne')
        self.date_label.config(bg='light blue')

        # Call the update_date_time function to start updating the label
        self.update_date_time()

    # Define a function to update date and time
    def update_date_time(self):  # add 'self' to let this method access class attributes
        self.now = dt.now()
        self.current_time = self.now.strftime('%I:%M %p')
        self.current_date = self.now.strftime('%d/%m/%Y')

        # Update the date and time label
        self.date_label.config(text=f"{self.current_date}n{self.current_time}")

        # Schedule the function to be called after 1 second
        self.root.after(1000, self.update_date_time)

self here refers to the current instance of the TimeWgt class

Then you’ll want to instantiate the class like so

time_widget = TimeWgt(root)

where root is referring to root = tk.Tk() as you’vf defined globally


As an aside, the datetime import is correct! from datetime import datetime is importing the class called datetime from the module datetime. Per convention, the class should really be named Datetime (with an uppercase "D"), but alas…

Answered By: JRiggles

You need to be careful with class objects in OOP (Object Oriented programming). The structure of a class is the following:

class ClassName:
    def __init__(self, ...):  # '...' --> implies the initial argument of the object
        ...  # here is the initial code

    def foo(self, params):  # Here is some method of the class
        ...

obj = ClassName(initial-params)  # initializate the object
obj.foo(params)  # call the method 'foo' from the object

I suggest to you to search more about when to use an object or whether use normal functions in your code. Anyways here is the nearest approach I could make to your code with not changing to much.

class TimeWgt:

    def __init__(self, tk_obj, text, font):
        self.tk_obj = tk_obj  # save root object into a class variable
        # create a label for date and time
        self.date_label = tk.Label(tk_obj, text=text, font=font)
        self.date_label.place(relx=0.11, rely=0.0, anchor='ne')
        self.date_label.config(bg='light blue')

    # Define a function to update date and time
    def update_date_time(self):
        now = dt.now()
        current_time = now.strftime('%I:%M %p')
        current_date = now.strftime('%d/%m/%Y')

        # Update the date and time label
        self.date_label.config(text=f"{current_date}n{current_time}")

        # Schedule the function to be called after 1 second
        self.tk_obj.after(1000, self.update_date_time)



obj = TimeWgt(root, text='', font=('Times New Roman', 14))
# Call the update_date_time function to start updating the label
obj.update_date_time()
Answered By: luisch444

There is an easier way.

Put the Label widget outside of class. This way you can be called date_label anywhere outside or inside class.

  • Commented out line15 and 16.
  • In line 29 put this root.after(1000, update_date_time) outside of
    function.

You’ re ready to go.

Snippet:

import tkinter as tk
from datetime import datetime as dt

# Create the main window, Title, and the size
root = tk.Tk()
root.title('My App')
root.geometry('800x600')
root.configure(bg='light blue')

date_label = tk.Label(root, text='', font=('Times New Roman', 14))
date_label.place(relx=0.11, rely=0.0, anchor='ne')

class TimeWgt():
    # create a label for date and time
    #date_label = tk.Label(root, text='', font=('Times New Roman', 14))
    #date_label.place(relx=0.11, rely=0.0, anchor='ne')
    date_label.config(bg='light blue')

    # Define a function to update date and time
    def update_date_time():
        now = dt.now()
        current_time = now.strftime('%I:%M %p')
        current_date = now.strftime('%d/%m/%Y')

        # Update the date and time label
        date_label.config(text=f"{current_date}n{current_time}")

        # Schedule the function to be called after 1 second
    root.after(1000, update_date_time)

    # Call the update_date_time function to start updating the label
    update_date_time()

# Create entry widget for text
input = tk.Text(root, width=25, height=5)
input.place(relx=0.5, rely=0.5, anchor='center')

# Start the main loop of the application

app = TimeWgt()
app.update_date_time 
root.mainloop()

Screenshot:

enter image description here

Answered By: toyota Supra
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.