tkinter: Analog clock refreshing - how does “after

2019-08-15 02:34发布

问题:

Recently, I'm beginner in python, and I've programmed an analog clock on Tkinter, which can display time every second. But this clock uses a while 1 infinite loop to refresh the time, that totally blocks the rest of the program. Because I want to use it in an another program, as a parallel function, I had to reprogram the refreshing method.

I've found something about the after() function which can (I think) help me, but I get a RuntimeError: maximum recursion depth exceeded in comparison and I don't understand why. Is the drawing of my clock too heavy for python? I'll put the code here and hope someone understand my problem and can help me.

(PS 1: I don't take care about the code before "principle function" and I don't think the problem is there, but I'll still post it because I'm not sure at all that this part is debugged.

PS 2: I'm really sorry about my English which may be a problem, so tell me if you don't understand.)

#list of imported modules
from tkinter import *
from math import cos, sin
from time import gmtime, sleep

#defining of secondary functions

def drawcircle(Alpha,Beta,Rayon,Couleur,can): #draw a circle base on center coord radius and color
    x1,y1,x2,y2=Alpha-Rayon, Beta-Rayon, Alpha+Rayon, Beta+Rayon
    can.create_oval(x1,y1,x2,y2,fill=Couleur)

def drawPetAig(CoordA, CoordZ, Taille, Omega, can): #function to drawn the second hand of the clock
    Pi = 3.141592
    Omega = ((Omega/60)+1)*30
    can.create_line(CoordA + (Taille/3) * cos(Pi*(Omega/180)), CoordZ + (Taille/3) * sin(Pi*(Omega/180)), CoordA - (Taille/8) * cos(Pi*(Omega/180)), CoordZ - (Taille/8) * sin(Pi*(Omega/180)) )

def drawGrdAig(CoordA, CoordZ, Taille, Omega, can): #function to draw the minute hand, based on center coord and minutes.
    Pi = 3.141592
    Omega = (Omega-15)*6
    can.create_line(CoordA + (Taille/1.5) * cos(Pi*(Omega/180)), CoordZ + (Taille/1.5) * sin(Pi*(Omega/180)), CoordA - (Taille/6) * cos(Pi*(Omega/180)), CoordZ - (Taille/6) * sin(Pi*(Omega/180)))

def drawSecAig(CoordA, CoordZ, Taille, Omega, can): #function to draw the hour hand
    Pi = 3.141592
    Omega = (Omega-15) *6
    can.create_line(CoordA + (Taille/1.5) * cos(Pi*(Omega/180)), CoordZ + (Taille/1.5) * sin(Pi*(Omega/180)), CoordA - (Taille/6) * cos(Pi*(Omega/180)), CoordZ - (Taille/6) * sin(Pi*(Omega/180)), fill = "red")

def fondhorloge(CoordA, CoordZ, Taille, can1):  #function drawing the backgroud of the clock
    Pi = 3.141592
    drawcircle(CoordA, CoordZ, Taille + (Taille/10), "grey6",can1) #drawing the surrounding of the clock
    drawcircle(CoordA, CoordZ, Taille, "ivory3",can1)#backgroud
    drawcircle(CoordA, CoordZ, Taille/80, "grey6",can1)#central point/needle articulation
    can1.create_line(CoordA + (Taille - (Taille/15)), CoordZ, CoordA + (Taille - (Taille/5)), CoordZ) #drawing the N/S/E/W decorativ hour position
    can1.create_line(CoordA, CoordZ + (Taille - (Taille/15)), CoordA, CoordZ + (Taille - (Taille/5)))
    can1.create_line(CoordA - (Taille - (Taille/15)), CoordZ, CoordA - (Taille - (Taille/5)), CoordZ)
    can1.create_line(CoordA, CoordZ - (Taille - (Taille/15)), CoordA, CoordZ - (Taille - (Taille/5)))

    #here, this 4*2 line defined the position of the 8 intermediate line between the N/S/E/W decorativ line.
    can1.create_line(CoordA + (Taille/1.05) * cos(Pi*(30/180)), CoordZ + (Taille/1.05) * sin(Pi*(30/180)), CoordA + (Taille/1.20) * cos(Pi*(30/180)), CoordZ + (Taille/1.20) * sin(Pi*(30/180)))
    can1.create_line(CoordA + (Taille/1.05) * cos(Pi*(60/180)), CoordZ + (Taille/1.05) * sin(Pi*(60/180)), CoordA + (Taille/1.20) * cos(Pi*(60/180)), CoordZ + (Taille/1.20) * sin(Pi*(60/180)))

    can1.create_line(CoordA - (Taille/1.05) * cos(Pi*(30/180)), CoordZ - (Taille/1.05) * sin(Pi*(30/180)), CoordA - (Taille/1.20) * cos(Pi*(30/180)), CoordZ - (Taille/1.20) * sin(Pi*(30/180)))
    can1.create_line(CoordA - (Taille/1.05) * cos(Pi*(60/180)), CoordZ - (Taille/1.05) * sin(Pi*(60/180)), CoordA - (Taille/1.20) * cos(Pi*(60/180)), CoordZ - (Taille/1.20) * sin(Pi*(60/180)))

    can1.create_line(CoordA + (Taille/1.05) * cos(Pi*(30/180)), CoordZ - (Taille/1.05) * sin(Pi*(30/180)), CoordA + (Taille/1.20) * cos(Pi*(30/180)), CoordZ - (Taille/1.20) * sin(Pi*(30/180)))
    can1.create_line(CoordA + (Taille/1.05) * cos(Pi*(60/180)), CoordZ - (Taille/1.05) * sin(Pi*(60/180)), CoordA + (Taille/1.20) * cos(Pi*(60/180)), CoordZ - (Taille/1.20) * sin(Pi*(60/180)))

    can1.create_line(CoordA - (Taille/1.05) * cos(Pi*(30/180)), CoordZ + (Taille/1.05) * sin(Pi*(30/180)), CoordA - (Taille/1.20) * cos(Pi*(30/180)), CoordZ + (Taille/1.20) * sin(Pi*(30/180)))
    can1.create_line(CoordA - (Taille/1.05) * cos(Pi*(60/180)), CoordZ + (Taille/1.05) * sin(Pi*(60/180)), CoordA - (Taille/1.20) * cos(Pi*(60/180)), CoordZ + (Taille/1.20) * sin(Pi*(60/180)))

#PRINCIPLE FUNCTION (here the problem starts)

def HORLOGE1(Gamma, Pi, Epsylon):# draw a clock with the center position x/x = gamma/pi and the radius = epsylon

    fondhorloge(Gamma, Pi, Epsylon, can1)# extracting time value
    patate = gmtime()
    heure = patate[3] + 1  # "+1" is changing time from english time to french time :P
    minute = patate[4]
    seconde = patate[5]

    print(heure, minute, seconde) # a simple test to watch what the programm is doing (run it, and you'll see, that this test is done tausend time per second, that why I think the problem is here.)
    drawPetAig(Gamma, Pi, Epsylon, minute, can1)
    drawGrdAig(Gamma, Pi, Epsylon, minute, can1)
    drawSecAig(Gamma, Pi, Epsylon, seconde, can1)
    fen1.after(1000, HORLOGE1(250, 250, 200))

#execution of the main function

fen1 = Tk()
can1 = Canvas(fen1, bg="white", height=500, width=500)
can1.pack()

HORLOGE1(250, 250, 200)

fen1.mainloop()
fen1.destroy()

回答1:

The problem is that in the principle function, HORLOGE1() calls itself immediately while trying to arrange for it to be called again after the specified 1 second delay. This occurs in the last line:

fen1.after(1000, HORLOGE1(250, 250, 200))

A simple way prevent this, would be to use something like this instead:

fen1.after(1000, lambda: HORLOGE1(250, 250, 200))

What this does is create an anonymous function using a lambda expression . The function created accepts/requires no arguments, and all it does is call HORLOGE1(250, 250, 200) when it's called. That function is what is then passed to fen1.after() as the second argument.

The notable thing is that doing this does not actually call HORLOGE1() in the process. The second argument to .after() should almost always be a callable — such as a function or a bound method — rather than the results of calling one, as you are doing.

After making the change, you should see something like this with moving clock hands on it: