-1

I have code that requires a live clock in a customtkinter window. I'm not sure how to make it live/update every second or minute. I've checked a number of examples on python clocks, creating a digital clock in python 3.7, Digital clock in status bar in python 3 and tkinter, and although the programs they show run, I can't get the clock to update, just to display one time. In the example below it's the black 4:47 PM that I want to update.

enter image description here

Here's the code I'm using.

from configparser import ConfigParser
import sys
#import tkinter
#import tkinter.messagebox
from tkinter.messagebox import askyesno
import customtkinter
#import time
#import shutil
import datetime
from datetime import datetime, time
from datetime import timedelta
from threading import Timer
import subprocess

customtkinter.set_appearance_mode("System")  #   Modes: "System" (standard), "Dark", "Light"
customtkinter.set_default_color_theme("blue")  #   Themes: "blue" (standard), "green", "dark-blue"

normal_text_color = "gray10"
disabled_text_color = "light slate gray"
alert_color = "red"
start_color = "green"
manual_color = "blue"
option_color1 = "purple3"
option_color2 = "purple1"

class App(customtkinter.CTk):
    def __init__(self):
        super().__init__()

        global normal_text_color
        global disabled_text_color
        global alert_color
        global start_color
        global manual_color
        global option_color1
        global option_color2

        r = 0
        c = 0
        stick = ""
        padderx = 0

# configure window
        self.title("test")
        self.geometry(f"{750}x{385}")

# configure grid layout (4x4)
        self.grid_columnconfigure(1, weight=1)
        self.grid_columnconfigure((2, 3), weight=0)
        self.grid_rowconfigure((0, 1, 2, 5), weight=1)

# create sidebar frame with widgets
        self.sidebar_frame = customtkinter.CTkFrame(self, width=140, corner_radius=0)
        self.sidebar_frame.grid(row=0, column=0, rowspan=9, sticky="nsew")
        self.sidebar_frame.grid_rowconfigure(9, weight=1)
        self.logo_label = customtkinter.CTkLabel(self.sidebar_frame, text="Controls", font=customtkinter.CTkFont(size=20, weight="bold"))
        self.logo_label.grid(row=0, column=0, padx=20, pady=(20, 10))

        self.sidebar_button_1 = customtkinter.CTkButton(self.sidebar_frame, command=lambda: self.sidebar_button_event(1))
        self.sidebar_button_1.grid(row=1, column=0, padx=20, pady=10)

        self.sidebar_button_2 = customtkinter.CTkButton(self.sidebar_frame, command=lambda: self.sidebar_button_event(2))
        self.sidebar_button_2.grid(row=2, column=0, padx=20, pady=10)

        self.logo_label = customtkinter.CTkLabel(self.sidebar_frame, text="Options", font=customtkinter.CTkFont(size=20, weight="bold"))
        self.logo_label.grid(row=3, column=0, padx=20, pady=0)

        self.sidebar_button_3 = customtkinter.CTkButton(self.sidebar_frame, command=lambda: self.sidebar_button_event(3))
        self.sidebar_button_3.grid(row=4, column=0, padx=20, pady=20)

        self.sidebar_button_4 = customtkinter.CTkButton(self.sidebar_frame, command=lambda: self.sidebar_button_event(4))
        self.sidebar_button_4.grid(row=5, column=0, padx=20, pady=20)

        self.sidebar_button_5 = customtkinter.CTkButton(self.sidebar_frame, command=lambda: self.sidebar_button_event(5))
        self.sidebar_button_5.grid(row=6, column=0, padx=20, pady=10)

# create current_view for operations
        self.current_view = customtkinter.CTkFrame(self, width=100, fg_color="transparent")
        self.current_view.grid(row=0, column=1, rowspan=9, sticky="nsew")
        self.current_view.grid_columnconfigure((0, 1, 2, 3), weight=1)
        self.current_view.grid_rowconfigure(5, weight=1)

# Show current operation
        self.current_label = customtkinter.CTkLabel(self.current_view, text="Current Operations", font=customtkinter.CTkFont(size=20, weight="bold"))
        self.current_label.grid(row=0, column=0, sticky="e", padx=20, pady=(20, 10))

#   set operation labels
        self.operations()
        self.operation_values()

# create feeding log textbox

        font=('Courier', 14)

        self.log_view = customtkinter.CTkTextbox(self, font=font, width=100)
        self.log_view.grid(row=8, column=1, padx=(20, 10), pady=(20, 10), sticky="nsew")
        self.log_view.tag_config("normal", foreground=normal_text_color)
        self.log_view.tag_config("start", foreground=start_color)
        self.log_view.tag_config("warn", foreground=alert_color)
        self.log_view.tag_config("disable", foreground=disabled_text_color)
        self.log_view.tag_config("manual", foreground=manual_color)
        self.log_view.tag_config("option1", foreground=option_color1)
        self.log_view.tag_config("option2", foreground=option_color2)

#   Configure the button names
        self.sidebar_button_1.configure(text="A")
        self.sidebar_button_2.configure(text="B")
        self.sidebar_button_3.configure(text="C")
        self.sidebar_button_4.configure(text="D")
        self.sidebar_button_4.configure(state="disabled")
        self.sidebar_button_5.configure(text="Quit")


#   sets the operation type label only
    def operations(self):
        global normal_text_color
        global disabled_text_color
        global alert_color
        global start_color
        global start_up
        global manual_color

# set Auto Feed label
        r = 1
        c = 0
        stick = "e"
        padderx = 20

        current_text_color = normal_text_color
        text_text = "Auto:"
        self.update_labels(current_text_color, text_text, r, c, stick, padderx )

#   next Feeding
        r += 1

        current_text_color = normal_text_color
        text_text = "Begin Next:"
        self.update_labels(current_text_color, text_text, r, c, stick, padderx )

#   hourly feeding
        r += 1

        current_text_color = normal_text_color
        text_text = "Hourly:"
        self.update_labels(current_text_color, text_text, r, c, stick, padderx )


#   how many feeding times per day.
        r += 1

        current_text_color = normal_text_color
        text_text = "Per Day:"
        self.update_labels(current_text_color, text_text, r, c, stick, padderx )


#   sets the operation type value only
    def operation_values(self):
        global normal_text_color
        global disabled_text_color
        global alert_color
        global start_color
        global start_up
        global manual_color

        r = 1
        c = 1
        stick = "w"
        padderx = 0

#   auto feed
        text_text = "On "
        current_text_color = start_color
        self.update_labels(current_text_color, text_text, r, c, stick, padderx)

#   when is the next feeding?
        r += 1
        text_text = "5:35 PM"
        current_text_color = start_color

        self.update_labels(current_text_color, text_text, r, c, stick, padderx )

#   hourly feeding
        r += 1
        text_text = "Off    "
        current_text_color = alert_color
        self.update_labels(current_text_color, text_text, r, c, stick, padderx )

#   if auto_feed is on, and hourly_feed is off, then display the number of times per day fed
        r += 1
        text_text = "Off"
        current_text_color = alert_color
        self.update_labels(current_text_color, text_text, r, c, stick, padderx )



#   updates a label.
    def update_labels(self, set_text_color, set_text, r, c, stick, pad_it ):

        self.current_label = customtkinter.CTkLabel(self.current_view, text_color=set_text_color,
                                                    text=set_text, font=customtkinter.CTkFont(size=20, weight="bold"))
        self.current_label.grid(row=r, column=c, padx=pad_it, sticky=stick)


#   Handle the buttons
    def sidebar_button_event(self, buttonNum):

        if buttonNum == 5:
            sys.exit(0)

    def clock(self):
        now = datetime.now()
        text_text = now.strftime("%m/%d/%Y")
        self.current_label = customtkinter.CTkLabel(self.current_view, text=text_text,
                                                    font=customtkinter.CTkFont(size=20, weight="bold"))
        self.current_label.grid(row=0, column=2, sticky="w", padx=20, pady=(0, 0))
        text_text = now.strftime("%-I:%M %p")
        self.current_label = customtkinter.CTkLabel(self.current_view, text=text_text,
                                                    font=customtkinter.CTkFont(size=20, weight="bold"))
        self.current_label.grid(row=2, column=2, sticky="w", padx=20, pady=(0, 0))


if __name__ == "__main__":
    app = App()
    app.clock()
    app.mainloop()

Thanks, any help will be greatly appreciated.

Greg W
  • 31
  • 6
  • Please try to make a much smaller [mcve]. Before figuring out how to do it in a large program, figure out how to do it in a smaller program. – Bryan Oakley Aug 22 '23 at 22:30
  • (1) None of your `global` statements are necessary. You do not need `global` for a value you are going to USE. (2) I do not see where you have created a once-per-second timer with `app.after`. How did you expect the time to update? – Tim Roberts Aug 22 '23 at 22:45
  • This is a smaller example for testing. The original code is several thousand lines long. – Greg W Aug 22 '23 at 23:05
  • 1. Probably, just ignore them please. 2. I don't know how to create a timer that won't interfere with the operation of the buttons. I tried app.after ` App.after(1000,self.clock)` but keep receiving this error. AttributeError: 'int' object has no attribute 'tk' – Greg W Aug 22 '23 at 23:05

2 Answers2

1

I'm not sure that it is the best solution, but if you add this to the end of your clock() function it starts working:

self.current_view.after(1, self.clock)

Explain IT
  • 41
  • 4
0

Since clock() function has been called once, the clock will not be updated as expected.

You need to used .after() to update the clock periodically:

class App(customtkinter.CTk):
    def __init__(self):
        ...
        # create the date and time labels for clock display
        self.current_date_label = customtkinter.CTkLabel(self.current_view,
                                                         font=customtkinter.CTkFont(size=20, weight="bold"))
        self.current_date_label.grid(row=0, column=2, sticky="w", padx=20, pady=(0, 0))
        self.current_time_label = customtkinter.CTkLabel(self.current_view,
                                                         font=customtkinter.CTkFont(size=20, weight="bold"))
        self.current_time_label.grid(row=2, column=2, sticky="w", padx=20, pady=(0, 0))

    ...

    def clock(self):
        now = datetime.now()
        self.current_date_label.configure(text=now.strftime("%m/%d/%Y"))
        self.current_time_label.configure(text=now.strftime("%I:%M %p"))
        self.after(1000, self.clock) # update the clock one second later
acw1668
  • 40,144
  • 5
  • 22
  • 34