1

I have had this script for some time. However, it only runs if I start it from IDLE; if I try to run it from the desktop it doesn't start. It is a clock made with Tkinter.

import time

from tkinter import *

def showTime():

    canvas.delete('text')
    if True:
        actualTime = time.localtime()

        text = canvas.create_text((100,50,),
                                  text =(actualTime[3],actualTime[4],actualTime[5]),
                                  fill="white",
                                  font=("Verdana",20,"bold"),
                                  tag="text")

    root.after(1000,showTime)

if "__main__" == __name__:

    root = Tk()

    root.resizable(False,False)
    root.title("Clock")

    canvas = Canvas(root, width=200, height=100,bg="black",cursor="target")
    canvas.create_rectangle((20,20),(180,80),outline="ghostwhite")
    canvas.pack()

    showTime()
Vibhutha Kumarage
  • 1,372
  • 13
  • 26

1 Answers1

4

You need to start the Tkinter mainloop. Just add

root.mainloop()

to the end of the script after your

showTime()

call.

The reason that your script works in IDLE is that IDLE itself is a Tkinter program, and it already has an event loop going as mentioned in this answer.


BTW, that if True: statement in showTime is pointless.

FWIW, here is your program with the mainloop call added, and a few other minor changes. It's better to avoid "star" imports because they clutter your namespace with the imported names, eg from tkinter import * brings in over 130 names. This can cause collisions with names you define yourself, unless you're intimately familiar with every name that Tkinter defines, but it's especially problematic if you use a star import with another module that happens to use names that Tkinter uses.

import time
import tkinter as tk

def showTime():
    canvas.delete('text')
    actualTime = time.localtime()

    text = canvas.create_text((100, 50),
        text = actualTime[3:6],
        fill="white",
        font=("Verdana", 20, "bold"),
        tag="text")

    root.after(1000, showTime)

if "__main__" == __name__:
    root = tk.Tk()

    root.resizable(False,False)
    root.title("Clock")

    canvas = tk.Canvas(root, width=200, height=100, bg="black", cursor="target")
    canvas.create_rectangle((20, 20), (180, 80), outline="ghostwhite")
    canvas.pack()

    showTime()
    root.mainloop()

As Bryan Oakley mentions in the comments, it's better to just update the text string of the Canvas Text item, rather than trashing the old Text item and building a fresh one every second. So here's a new version derived from your code.

I use time.strftime to build the time string because it gives nicer output than simply slicing the struct_time object returned by time.localtime: it always uses 2 digits for the hours, minutes, and seconds components, and you can easily add colons (or whatever) to the format string. Please see the linked docs for all the format options that strftime provides.

I've also put the GUI into a class. That's not really necessary for such a simple GUI, but it's a good habit to get into because it makes the code more modular.

import tkinter as tk
from time import strftime

class Clock:
    def __init__(self):
        root = tk.Tk()
        root.title("Clock")
        root.resizable(False, False)

        self.canvas = canvas = tk.Canvas(root, width=200, height=100, 
            bg="black", cursor="target")
        canvas.create_rectangle((20, 20), (180, 80), outline="ghostwhite")
        canvas.create_text((100, 50), tag = "text",
            fill="white", font=("Verdana", 20, "bold"))
        canvas.pack()

        self.show_time()
        root.mainloop()

    def show_time(self):
        w = self.canvas
        w.itemconfig("text", text=strftime('%H %M %S'))
        w.after(1000, self.show_time)

if "__main__" == __name__:
    Clock()
Community
  • 1
  • 1
PM 2Ring
  • 54,345
  • 6
  • 82
  • 182
  • 1
    There's no reason to keep creating more and more canvas items. It is more efficient to create one item, then change the text that it displays. – Bryan Oakley Dec 10 '16 at 15:48
  • @BryanOakley Very good point. I only wanted to make a few obvious minor improvements to the OP's code, but I've just added a new version. – PM 2Ring Dec 11 '16 at 06:47