1#!/usr/bin/env python
2
3
4#############################################################################
5##
6## Copyright (C) 2013 Riverbank Computing Limited.
7## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
8## All rights reserved.
9##
10## This file is part of the examples of PyQt.
11##
12## $QT_BEGIN_LICENSE:BSD$
13## You may use this file under the terms of the BSD license as follows:
14##
15## "Redistribution and use in source and binary forms, with or without
16## modification, are permitted provided that the following conditions are
17## met:
18##   * Redistributions of source code must retain the above copyright
19##     notice, this list of conditions and the following disclaimer.
20##   * Redistributions in binary form must reproduce the above copyright
21##     notice, this list of conditions and the following disclaimer in
22##     the documentation and/or other materials provided with the
23##     distribution.
24##   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
25##     the names of its contributors may be used to endorse or promote
26##     products derived from this software without specific prior written
27##     permission.
28##
29## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
40## $QT_END_LICENSE$
41##
42#############################################################################
43
44
45from PyQt5.QtCore import (pyqtProperty, pyqtSignal, pyqtSlot, QPoint, QSize,
46        Qt, QTime, QTimer)
47from PyQt5.QtGui import QBrush, QColor, QPainter, QPen, QPolygon
48from PyQt5.QtWidgets import QApplication, QWidget
49
50
51class PyAnalogClock(QWidget):
52    """PyAnalogClock(QWidget)
53
54    Provides an analog clock custom widget with signals, slots and properties.
55    The implementation is based on the Analog Clock example provided with both
56    Qt and PyQt.
57    """
58
59    # Emitted when the clock's time changes.
60    timeChanged = pyqtSignal(QTime)
61
62    # Emitted when the clock's time zone changes.
63    timeZoneChanged = pyqtSignal(int)
64
65    def __init__(self, parent=None):
66
67        super(PyAnalogClock, self).__init__(parent)
68
69        self.timeZoneOffset = 0
70
71        timer = QTimer(self)
72        timer.timeout.connect(self.update)
73        timer.timeout.connect(self.updateTime)
74        timer.start(1000)
75
76        self.setWindowTitle("Analog Clock")
77        self.resize(200, 200)
78
79        self.hourHand = QPolygon([
80            QPoint(7, 8),
81            QPoint(-7, 8),
82            QPoint(0, -40)
83        ])
84        self.minuteHand = QPolygon([
85            QPoint(7, 8),
86            QPoint(-7, 8),
87            QPoint(0, -70)
88        ])
89
90        self.hourColor = QColor(0, 127, 0)
91        self.minuteColor = QColor(0, 127, 127, 191)
92
93    def paintEvent(self, event):
94
95        side = min(self.width(), self.height())
96        time = QTime.currentTime()
97        time = time.addSecs(self.timeZoneOffset * 3600)
98
99        painter = QPainter()
100        painter.begin(self)
101        painter.setRenderHint(QPainter.Antialiasing)
102        painter.translate(self.width() / 2, self.height() / 2)
103        painter.scale(side / 200.0, side / 200.0)
104
105        painter.setPen(Qt.NoPen)
106        painter.setBrush(QBrush(self.hourColor))
107
108        painter.save()
109        painter.rotate(30.0 * ((time.hour() + time.minute() / 60.0)))
110        painter.drawConvexPolygon(self.hourHand)
111        painter.restore()
112
113        painter.setPen(self.hourColor)
114
115        for i in range(0, 12):
116            painter.drawLine(88, 0, 96, 0)
117            painter.rotate(30.0)
118
119        painter.setPen(Qt.NoPen)
120        painter.setBrush(QBrush(self.minuteColor))
121
122        painter.save()
123        painter.rotate(6.0 * (time.minute() + time.second() / 60.0))
124        painter.drawConvexPolygon(self.minuteHand)
125        painter.restore()
126
127        painter.setPen(QPen(self.minuteColor))
128
129        for j in range(0, 60):
130            if (j % 5) != 0:
131                painter.drawLine(92, 0, 96, 0)
132            painter.rotate(6.0)
133
134        painter.end()
135
136    def minimumSizeHint(self):
137
138        return QSize(50, 50)
139
140    def sizeHint(self):
141
142        return QSize(100, 100)
143
144    def updateTime(self):
145
146        self.timeChanged.emit(QTime.currentTime())
147
148    # The timeZone property is implemented using the getTimeZone() getter
149    # method, the setTimeZone() setter method, and the resetTimeZone() method.
150
151    # The getter just returns the internal time zone value.
152    def getTimeZone(self):
153
154        return self.timeZoneOffset
155
156    # The setTimeZone() method is also defined to be a slot. The @pyqtSlot
157    # decorator is used to tell PyQt which argument type the method expects,
158    # and is especially useful when you want to define slots with the same
159    # name that accept different argument types.
160
161    @pyqtSlot(int)
162    def setTimeZone(self, value):
163
164        self.timeZoneOffset = value
165        self.timeZoneChanged.emit(value)
166        self.update()
167
168    # Qt's property system supports properties that can be reset to their
169    # original values. This method enables the timeZone property to be reset.
170    def resetTimeZone(self):
171
172        self.timeZoneOffset = 0
173        self.timeZoneChanged.emit(0)
174        self.update()
175
176    # Qt-style properties are defined differently to Python's properties.
177    # To declare a property, we call pyqtProperty() to specify the type and,
178    # in this case, getter, setter and resetter methods.
179    timeZone = pyqtProperty(int, getTimeZone, setTimeZone, resetTimeZone)
180
181
182if __name__ == "__main__":
183
184    import sys
185
186    app = QApplication(sys.argv)
187    clock = PyAnalogClock()
188    clock.show()
189    sys.exit(app.exec_())
190