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