Creating Analog Clock using PyQt5 in Python

In this post, I’ll show how to create an Analog Clock using PyQt5. I am using Python 3.7 throughout all the programs and this should work fine in Python3.8 also.

The analog clock will look like this:

And I will use three main things,

1. QPainter
2. QTimer
3. Qpolygon

First I will code to create the face of the clock, here the face of the clock means the markings for hours, minutes and seconds.

painter = QPainter()
painter.setPen(Qt.NoPen)
painter.setBrush(AnalogClock.hourColor)

painter.save()
painter.rotate(30.0 * (time.hour() + time.minute() / 60.0))
painter.drawConvexPolygon(AnalogClock.hourHand)
painter.restore()

painter.setPen(AnalogClock.hourColor)

for i in range(12):
    painter.drawLine(88, 0, 96, 0)  # markings for hour hand
    painter.rotate(30.0)

painter.setPen(Qt.NoPen)
painter.setBrush(AnalogClock.minuteColor)

painter.save()
painter.rotate(6.0 * (time.minute() + time.second() / 60.0))
painter.drawConvexPolygon(AnalogClock.minuteHand)
painter.restore()

painter.setPen(AnalogClock.minuteColor)

for j in range(60):
    if (j % 5) != 0:
        painter.drawLine(92, 0, 96, 0)  # markings for minute hand
    painter.rotate(6.0)

painter.setPen(Qt.NoPen)
painter.setBrush(AnalogClock.secondColor)

painter.save()
painter.rotate(360 * (time.minute() + time.second() / 60.0))
painter.drawConvexPolygon(AnalogClock.secondHand)
painter.restore()

now, in the above code, I have used painter.drawConvexPolygon() where the inside parameters are the dimension of the clock hands respectively. I have defined them above using QPolygon() class.

secondHand = QPolygon([
        QPoint(7, 8),
        QPoint(-7, 8),
        QPoint(0, -95)
    ])
    hourHand = QPolygon([
        QPoint(7, 8),
        QPoint(-7, 8),
        QPoint(0, -50)
    ])

    minuteHand = QPolygon([
        QPoint(7, 8),
        QPoint(-7, 8),
        QPoint(0, -70)
    ])

Now, you have to make little bit calculations in the painter.rotate() part to make the clock look accurate.

Like for hour hand, you want to rotate it 30(360/12.0) times in an hour, i.e. 30 * (hour + minute)
For minute hand,  you wan tot rotate it 6 times in a minute, i.e 6*(minute + second/60)
And for second’s hand, you want to rotate it 360 degrees in 1 minute,360*(minute + second/60)

Thus I have implied that logic in the painter.rotate() part.

Now you have to create the main function and instantiate the AnalogClock class to run and display the clock properly.

I have summed up the total code and this looks like this:

from PyQt5.QtCore import QPoint, Qt, QTime, QTimer
from PyQt5.QtGui import QColor, QPainter, QPolygon
from PyQt5.QtWidgets import QApplication, QWidget
class AnalogClock(QWidget):
    secondHand = QPolygon([
        QPoint(7, 8),
        QPoint(-7, 8),
        QPoint(0, -95)
    ])
    hourHand = QPolygon([
        QPoint(7, 8),
        QPoint(-7, 8),
        QPoint(0, -50)
    ])
    minuteHand = QPolygon([
        QPoint(7, 8),
        QPoint(-7, 8),
        QPoint(0, -70)
    ])
    hourColor = QColor(127, 0, 127)
    minuteColor = QColor(0, 100, 250, 200)
    secondColor = QColor(195, 0, 0, 150)
    def __init__(self, parent=None):
        super(AnalogClock, self).__init__(parent)
        timer = QTimer(self)
        timer.timeout.connect(self.update)
        timer.start(1000)
        self.setWindowTitle("Analog Clock")
        self.resize(600, 600)
    def paintEvent(self, event):
        side = min(self.width(), self.height())
        time = QTime.currentTime()
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)
        painter.translate(self.width() / 2, self.height() / 2)
        painter.scale(side / 200, side / 200)
        painter.setPen(Qt.NoPen)
        painter.setBrush(AnalogClock.hourColor)
        painter.save()
        painter.rotate(30.0 * (time.hour() + time.minute() / 60.0)) # 360/12.0 times per hour
        print(f'hour:{time.hour()}, minute:{time.minute()}, second:{time.second()}')
        painter.drawConvexPolygon(AnalogClock.hourHand)
        painter.restore()
        painter.setPen(AnalogClock.hourColor)
        for i in range(12):
            painter.drawLine(88, 0, 96, 0)
            painter.rotate(30.0)
        painter.setPen(Qt.NoPen)
        painter.setBrush(AnalogClock.minuteColor)
        painter.save()
        painter.rotate(6.0 * (time.minute() + time.second() / 60.0))  # 6 times a minute
        painter.drawConvexPolygon(AnalogClock.minuteHand)
        painter.restore()
        painter.setPen(AnalogClock.minuteColor)
        for j in range(60):
            if (j % 5) != 0:
                painter.drawLine(92, 0, 96, 0)
            painter.rotate(6.0)
        painter.setPen(Qt.NoPen)
        painter.setBrush(AnalogClock.secondColor)
        painter.save()
        painter.rotate(360 * (time.minute() + time.second() / 60.0))  # 360 times in a minute
        painter.drawConvexPolygon(AnalogClock.secondHand)
        painter.restore()
if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    clock = AnalogClock()
    clock.show()
    sys.exit(app.exec_())

 

You can play around with the colors of the clock hand and can change the theme of the clock. I have used the RGBA color format. Where a stands for the strength of the alpha channel. It ranges from [0,255]. 255 means opaque.

One response to “Creating Analog Clock using PyQt5 in Python”

  1. JonP says:

    Tuhin,
    I think this is good programming:
    doing so much with so little code.

    I am using the code for my autopilot compass which has three hands,
    wind heading, boat heading, next waypoint heading.

    I just made all the hands equal length and rotated each hand badsed on the headings. Works brilliantly.

    Thanks for the example…

Leave a Reply

Your email address will not be published. Required fields are marked *