# Examples of the math.sin() and math.cos() trig functions in making a clock # Al Sweigart al@inventwithpython.com # You can learn more about Pygame with the # free book "Making Games with Python & Pygame" # # http://inventwithpython.com/pygame # import sys, pygame, time, math from pygame.locals import * # set up a bunch of constants BRIGHTBLUE = ( 0, 50, 255) WHITE = (255, 255, 255) DARKRED = (128, 0, 0) RED = (255, 0, 0) YELLOW = (255, 255, 0) BLACK = ( 0, 0, 0) HOURHANDCOLOR = DARKRED MINUTEHANDCOLOR = RED SECONDHANDCOLOR = YELLOW NUMBERBOXCOLOR = BRIGHTBLUE BGCOLOR = WHITE WINDOWWIDTH = 640 # width of the program's window, in pixels WINDOWHEIGHT = 480 # height in pixels WIN_CENTERX = int(WINDOWWIDTH / 2) WIN_CENTERY = int(WINDOWHEIGHT / 2) CLOCKNUMSIZE = 40 # size of the clock number's boxes CLOCKSIZE = 200 # general size of the clock # This function retrieves the x, y coordinates based on a "tick" mark, which ranges between 0 and 60 # A "tick" of 0 is at the top of the circle, 30 is at the bottom, 45 is at the "9 o'clock" position, etc. # The "stretch" is how far from the origin the x, y return values will be # "originx" and "originy" will be where the center of the circle is (almost always the center of the window) def getTickPosition(tick, stretch=1.0, originx=WIN_CENTERX, originy=WIN_CENTERY): # uncomment to have a "rotating clock" feature. # This works by pushing the "tick" amount forward #tick += (time.time() % 15) * 4 # The cos() and sin() tick -= 15 # ensure that tick is between 0 and 60 tick = tick % 60 tick = 60 - tick # the argument to sin() or cos() needs to range between 0 and 2 * math.pi # Since tick is always between 0 and 60, (tick / 60.0) will always be between 0.0 and 1.0 # The (tick / 60.0) lets us break up the range between 0 and 2 * math.pi into 60 increments. x = math.cos(2 * math.pi * (tick / 60.0)) y = -1 * math.sin(2 * math.pi * (tick / 60.0)) # "-1 *" because in Pygame, the y coordinates increase going down (the opposite of how they normally go in mathematics) # sin() and cos() return a number between -1.0 and 1.0, so multiply to stretch it out. x *= stretch y *= stretch # Then do the translation (i.e. sliding) of the x and y points. # NOTE: Always do the translation addition AFTER doing the stretch. x += originx y += originy return x, y # these next 2 lines are used by the "pulsing clock" feature (see below) originalClockSize = CLOCKSIZE PULSESIZE = 20 # standard pygame setup code pygame.init() DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT)) pygame.display.set_caption('Trig Clock') fontObj = pygame.font.Font('freesansbold.ttf', 26) # render the Surface objects that have the clock numbers written on them clockNumSurfs = [fontObj.render('%s' % (i), True, BGCOLOR, NUMBERBOXCOLOR) for i in [12] + list(range(1, 12))] # Put 12 at the front of the list since clocks start at 12, not 1. while True: # main application loop # event handling loop for quit events for event in pygame.event.get(): if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE): pygame.quit() sys.exit() # fill the screen to draw from a blank state DISPLAYSURF.fill(BGCOLOR) # draw the numbers and number boxes of the clock for i in range(12): # set up the Rect objects for the numbers and the number boxes clockNumRect = clockNumSurfs[i].get_rect() clockNumRect.center = getTickPosition(i * 5, CLOCKSIZE) clockNumBoxRect = clockNumSurfs[i].get_rect() clockNumBoxRect.size = (CLOCKNUMSIZE, CLOCKNUMSIZE) clockNumBoxRect.center = getTickPosition(i * 5, CLOCKSIZE) # draw the numbers and the number boxes pygame.draw.rect(DISPLAYSURF, NUMBERBOXCOLOR, clockNumBoxRect) DISPLAYSURF.blit(clockNumSurfs[i], clockNumRect) # get the current time now = time.localtime() now_hour = now[3] % 12 # now[3] ranges from 0 to 23, so we mod 12. now_minute = now[4] now_second = now[5] + (time.time() % 1) # add the fraction of a second we get from time.time() to make a smooth-moving seconds hand # Uncomment this if you don't want the second hand to move smoothly: #now_second = now[5] # draw the hour hand x, y = getTickPosition(now_hour * 5 + (now_minute * 5 / 60.0), CLOCKSIZE * 0.6) pygame.draw.line(DISPLAYSURF, HOURHANDCOLOR, (WIN_CENTERX, WIN_CENTERY), (x, y), 8) # draw the minute hand x, y = getTickPosition(now_minute + (now_second / 60.0), CLOCKSIZE * 0.8) pygame.draw.line(DISPLAYSURF, MINUTEHANDCOLOR, (WIN_CENTERX, WIN_CENTERY), (x, y), 6) # draw the second hand x, y = getTickPosition(now_second, CLOCKSIZE * 0.8) pygame.draw.line(DISPLAYSURF, SECONDHANDCOLOR, (WIN_CENTERX, WIN_CENTERY), (x, y), 2) # draw the second hand's part that sticks out behind x, y = getTickPosition(now_second, CLOCKSIZE * -0.2) # negative stretch makes it go in the opposite direction pygame.draw.line(DISPLAYSURF, SECONDHANDCOLOR, (WIN_CENTERX, WIN_CENTERY), (x, y), 2) # draw border pygame.draw.rect(DISPLAYSURF, BLACK, (0, 0, WINDOWWIDTH, WINDOWHEIGHT), 1) pygame.display.update() # Uncomment this if you want the "pulsing clock" feature: #CLOCKSIZE = originalClockSize + math.sin(2 * math.pi * (time.time() % 1)) * PULSESIZE