# ClockHeavyHandedQt.py (c) Kari Laitinen # http://www.naturalprogramming.com # 2011-10-03 File created. # 2011-10-03 Last modification. # This is a modified version of ClockQt.py. # In this version the second hand moves as if it were # a somewhat heavy hand. import sys from PyQt4.QtCore import * from PyQt4.QtGui import * from time import sleep from datetime import datetime import math class ThreadThatRunsTheClock( QThread ) : def __init__( self ) : QThread.__init__( self ) self.thread_must_be_run = True self.mutex = QMutex() def stop_this_thread( self ) : self.thread_must_be_run = False def run( self ) : while self.thread_must_be_run == True : self.mutex.lock() # locking may not be necessary # Here we emit a signal whenever we want the clock to # be redrawn. The other thread will handle this signal # so that the method redraw_clock() will # be called. self.emit( SIGNAL( "clock_redraw_request" ) ) self.mutex.unlock() sleep( 0.04 ) class ClockWindow( QWidget ) : HOUR_HAND_LENGTH = 152 MINUTE_HAND_LENGTH = 188 SECOND_HAND_LENGTH = 200 HAND_STATE_UNSPECIFIED = 0 HAND_BETWEEN_MOVEMENTS = 1 HAND_MOVING_FORWARD = 2 HAND_MOVING_BACK = 3 def __init__( self, parent=None ) : QWidget.__init__( self, parent ) self.setGeometry( 100, 100, 600, 530 ) self.setWindowTitle( "ANIMATING A GRAPHICAL CLOCK" ) self.names_of_days_of_week = ( "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" ) self.names_of_months = ( "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ) self.second_hand_state = ClockWindow.HAND_BETWEEN_MOVEMENTS self.thread_that_runs_the_clock = ThreadThatRunsTheClock() self.connect( self.thread_that_runs_the_clock, SIGNAL( "clock_redraw_request" ), self.redraw_clock ) self.thread_that_runs_the_clock.start() def redraw_clock( self ) : self.update() def paintEvent( self, event ) : painter = QPainter() painter.begin( self ) date_and_time_now = datetime.today() current_year = date_and_time_now.year current_day = date_and_time_now.day month_index = date_and_time_now.month - 1 # The weekday() method of class datetime returns the day of the # week as an integer, where Monday is 0 and Sunday is 6. # The "Python week" thus begins with Monday. index_of_day_of_week = date_and_time_now.weekday() current_month = self.names_of_months[ month_index ] current_day_of_week = self.names_of_days_of_week[ index_of_day_of_week ] current_hours = date_and_time_now.hour current_minutes = date_and_time_now.minute current_seconds = date_and_time_now.second current_milliseconds = date_and_time_now.microsecond / 1000 clock_center_point_x = 300 clock_center_point_y = 280 date_string_to_screen = "%s, %s %d, %d" % ( current_day_of_week, current_month, current_day, current_year ) time_string_to_screen = "%d:%02d:%02d" % \ ( current_hours, current_minutes, current_seconds ) painter.drawText( 20, 20, date_string_to_screen ) painter.drawText( 20, 40, time_string_to_screen ) painter.setBrush( Qt.black ) # The following loop prints dots on the clock circle. dot_index = 0 while dot_index < 60 : dot_angle = dot_index * math.pi / 30 - math.pi / 2 dot_position_x = int( math.cos( dot_angle ) * \ ClockWindow.SECOND_HAND_LENGTH + clock_center_point_x ) dot_position_y = int( math.sin( dot_angle ) * \ ClockWindow.SECOND_HAND_LENGTH + clock_center_point_y ) dot_diameter = 4 if ( dot_index % 5 ) == 0 : # Every 5th dot on the clock circle is a larger dot. dot_diameter = 8 painter.drawEllipse( dot_position_x - dot_diameter / 2, dot_position_y - dot_diameter / 2, dot_diameter, dot_diameter ) dot_index = dot_index + 1 # For every hand of the clock, we'll first calculte the # angle of the hand in question. Then we can calculate the # the coordinates of the end point of the hand, and finally # we'll draw the hand. black_pen_for_hour_and_minute_hands = QPen( Qt.black, 12 ) black_pen_for_hour_and_minute_hands.setCapStyle( Qt.RoundCap ) green_pen_for_hour_and_minute_hands = QPen( Qt.green, 6 ) green_pen_for_hour_and_minute_hands.setCapStyle( Qt.RoundCap ) hour_hand_angle = ( float( current_hours * 30 ) + \ float( current_minutes / 2 ) ) \ * math.pi / 180 - math.pi /2 hour_hand_end_x = int( math.cos( hour_hand_angle ) * \ ClockWindow.HOUR_HAND_LENGTH + clock_center_point_x ) hour_hand_end_y = int( math.sin( hour_hand_angle) * \ ClockWindow.HOUR_HAND_LENGTH + clock_center_point_y ) painter.setPen( black_pen_for_hour_and_minute_hands ) painter.drawLine( clock_center_point_x, clock_center_point_y, hour_hand_end_x, hour_hand_end_y ) painter.setPen( green_pen_for_hour_and_minute_hands ) painter.drawLine( clock_center_point_x, clock_center_point_y, hour_hand_end_x, hour_hand_end_y ) minute_hand_angle = ( float( current_minutes ) + \ float( current_seconds ) / 60.0 ) \ * math.pi / 30 - math.pi /2 minute_hand_end_x = int( math.cos( minute_hand_angle ) * \ ClockWindow.MINUTE_HAND_LENGTH + clock_center_point_x ) minute_hand_end_y = int( math.sin( minute_hand_angle) * \ ClockWindow.MINUTE_HAND_LENGTH + clock_center_point_y ) painter.setPen( black_pen_for_hour_and_minute_hands ) painter.drawLine( clock_center_point_x, clock_center_point_y, minute_hand_end_x, minute_hand_end_y ) painter.setPen( green_pen_for_hour_and_minute_hands ) painter.drawLine( clock_center_point_x, clock_center_point_y, minute_hand_end_x, minute_hand_end_y ) painter.setPen( Qt.black ) # Let's print an 10-point dot in the center of the clock. painter.drawEllipse( clock_center_point_x - 5, clock_center_point_y - 5, 10, 10 ) painter.setPen( QPen( Qt.red, 4 ) ) # red color for the second hand if self.second_hand_state == ClockWindow.HAND_BETWEEN_MOVEMENTS : second_hand_angle = float( current_seconds ) \ * math.pi / 30 - math.pi /2 if current_milliseconds > 500 : self.second_hand_state = ClockWindow.HAND_MOVING_FORWARD elif self.second_hand_state == ClockWindow.HAND_MOVING_FORWARD : if current_milliseconds > 500 : # When the current milliseconds has a value in range 500 ... 999, # we'll make the clock behave as the millisecond value were # 0 ... 1188. This way the second hand goes beyond the next second # and later on backs to correct position. second_hand_angle = ( float( current_seconds ) + \ float( current_milliseconds - 500 ) / 420.0 ) \ * math.pi / 30 - math.pi /2 else : second_hand_angle = ( float( current_seconds ) + \ float( current_milliseconds ) / 1000.0 ) \ * math.pi / 30 - math.pi /2 self.second_hand_state = ClockWindow.HAND_BETWEEN_MOVEMENTS second_hand_end_x = int( math.cos( second_hand_angle ) * \ ClockWindow.SECOND_HAND_LENGTH + clock_center_point_x ) second_hand_end_y = int( math.sin( second_hand_angle) * \ ClockWindow.SECOND_HAND_LENGTH + clock_center_point_y ) painter.drawLine( clock_center_point_x, clock_center_point_y, second_hand_end_x, second_hand_end_y ) painter.end() def exit_this_window( self ) : self.thread_that_runs_the_clock.stop_this_thread() self.close() #is this necessary??? # Here is the main program: def main( args ) : this_application = QApplication( args ) application_window = ClockWindow() application_window.show() # The following statement specifies that when the # "lastWindowClosed()" signal is emitted by QApplication, # the Python method exit_this_window is called for the # PictureShowWindow object. this_application.connect( this_application, SIGNAL( "lastWindowClosed()" ), application_window.exit_this_window ) this_application.exec_() if __name__== "__main__" : main( sys.argv )