1############################################################################# 2## 3## Copyright (C) 2013 Riverbank Computing Limited. 4## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). 5## All rights reserved. 6## 7## This file is part of the examples of PyQt. 8## 9## $QT_BEGIN_LICENSE:LGPL$ 10## Commercial Usage 11## Licensees holding valid Qt Commercial licenses may use this file in 12## accordance with the Qt Commercial License Agreement provided with the 13## Software or, alternatively, in accordance with the terms contained in 14## a written agreement between you and Nokia. 15## 16## GNU Lesser General Public License Usage 17## Alternatively, this file may be used under the terms of the GNU Lesser 18## General Public License version 2.1 as published by the Free Software 19## Foundation and appearing in the file LICENSE.LGPL included in the 20## packaging of this file. Please review the following information to 21## ensure the GNU Lesser General Public License version 2.1 requirements 22## will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 23## 24## In addition, as a special exception, Nokia gives you certain additional 25## rights. These rights are described in the Nokia Qt LGPL Exception 26## version 1.1, included in the file LGPL_EXCEPTION.txt in this package. 27## 28## GNU General Public License Usage 29## Alternatively, this file may be used under the terms of the GNU 30## General Public License version 3.0 as published by the Free Software 31## Foundation and appearing in the file LICENSE.GPL included in the 32## packaging of this file. Please review the following information to 33## ensure the GNU General Public License version 3.0 requirements will be 34## met: http://www.gnu.org/copyleft/gpl.html. 35## 36## If you have questions regarding the use of this file, please contact 37## Nokia at qt-info@nokia.com. 38## $QT_END_LICENSE$ 39## 40############################################################################# 41 42 43import math 44import random 45 46from PyQt5.QtCore import QLineF, QPointF, QRectF, Qt, QTime 47 48from colors import Colors 49from demoitem import DemoItem 50from demoitemanimation import DemoItemAnimation 51from guidecircle import GuideCircle 52from guideline import GuideLine 53from letteritem import LetterItem 54 55 56class TickerPostEffect(object): 57 def tick(self, adjust): 58 pass 59 60 def transform(self, item, pos): 61 pass 62 63 64class PostRotateXY(TickerPostEffect): 65 def __init__(self, speedx, speedy, curvx, curvy): 66 super(PostRotateXY, self).__init__() 67 68 self.currRotX = 0.0 69 self.currRotY = 0.0 70 71 self.speedx = speedx 72 self.speedy = speedy 73 self.curvx = curvx 74 self.curvy = curvy 75 76 def tick(self, adjust): 77 self.currRotX += self.speedx * adjust 78 self.currRotY += self.speedy * adjust 79 80 def transform(self, item, pos): 81 parent = item.parentItem() 82 center = parent.boundingRect().center() 83 pos.setX(center.x() + (pos.x() - center.x()) * math.cos(self.currRotX + pos.x() * self.curvx)) 84 pos.setY(center.y() + (pos.y() - center.y()) * math.cos(self.currRotY + pos.y() * self.curvy)) 85 86 87class PostRotateXYTwist(PostRotateXY): 88 def transform(self, item, pos): 89 parent = item.parentItem() 90 center = parent.boundingRect().center() 91 pos.setX(center.x() + (pos.x() - center.x()) * math.cos(self.currRotX + pos.y() * self.curvx)) 92 pos.setY(center.y() + (pos.y() - center.y()) * math.cos(self.currRotY + pos.x() * self.curvy)) 93 94 95class TickerEffect(object): 96 Normal, Intro, Outro = range(3) 97 98 def __init__(self, letters): 99 self.postEffect = TickerPostEffect() 100 self.status = TickerEffect.Intro 101 self.letters = letters 102 self.morphSpeed = self.normalMorphSpeed = Colors.tickerMorphSpeed 103 self.moveSpeed = self.normalMoveSpeed = Colors.tickerMoveSpeed 104 self.useSheepDog = True 105 self.morphBetweenModels = not Colors.noTickerMorph 106 107 def setPostEffect(self, effect): 108 self.postEffect = effect 109 110 def slowDownAfterIntro(self, adjust): 111 if self.morphBetweenModels: 112 if self.status == TickerEffect.Intro: 113 dec = 0.1 * adjust 114 self.moveSpeed -= dec 115 if self.moveSpeed < Colors.tickerMoveSpeed: 116 self.moveSpeed = self.normalMoveSpeed 117 self.morphSpeed = self.normalMorphSpeed 118 self.status = TickerEffect.Normal 119 120 def moveLetters(self, adjust): 121 adaptedMoveSpeed = self.moveSpeed * adjust 122 adaptedMorphSpeed = self.morphSpeed * adjust 123 self.postEffect.tick(adjust) 124 125 if self.morphBetweenModels: 126 move_speed = adaptedMoveSpeed 127 morph_speed = adaptedMorphSpeed 128 else: 129 move_speed = Colors.tickerMoveSpeed 130 morph_speed = -1 131 132 for letter in self.letters: 133 letter.guideAdvance(move_speed) 134 letter.guideMove(morph_speed) 135 136 pos = letter.getGuidedPos() 137 self.postEffect.transform(letter, pos) 138 139 if self.useSheepDog: 140 letter.setPosUsingSheepDog(pos, QRectF(0, 0, 800, 600)) 141 else: 142 letter.setPos(pos) 143 144 def tick(self, adjust): 145 self.slowDownAfterIntro(adjust) 146 self.moveLetters(adjust) 147 148 149class EffectWhirlWind(TickerEffect): 150 def __init__(self, letters): 151 super(EffectWhirlWind, self).__init__(letters) 152 153 self.moveSpeed = 50 154 155 for letter in self.letters: 156 letter.setGuidedPos(QPointF(0, 100)) 157 158 159class EffectSnake(TickerEffect): 160 def __init__(self, letters): 161 super(EffectSnake, self).__init__(letters) 162 163 self.moveSpeed = 40 164 165 for i, letter in enumerate(self.letters): 166 letter.setGuidedPos(QPointF(0, -250 - (i * 5))) 167 168 169class EffectScan(TickerEffect): 170 def __init__(self, letters): 171 super(EffectScan, self).__init__(letters) 172 173 for letter in self.letters: 174 letter.setGuidedPos(QPointF(100, -300)) 175 176 177class EffectRaindrops(TickerEffect): 178 def __init__(self, letters): 179 super(EffectRaindrops, self).__init__(letters) 180 181 for letter in self.letters: 182 letter.setGuidedPos(QPointF(random.randint(-100, 100), 183 random.randint(-200, 1100))) 184 185 186class EffectLine(TickerEffect): 187 def __init__(self, letters): 188 super(EffectLine, self).__init__(letters) 189 190 for i, letter in enumerate(self.letters): 191 letter.setGuidedPos(QPointF(100, 500 + i * 20)) 192 193 194class ItemCircleAnimation(DemoItem): 195 def __init__(self, parent=None): 196 super(ItemCircleAnimation, self).__init__(parent) 197 198 self.letterList = [] 199 self.letterCount = Colors.tickerLetterCount 200 self.scale = 1.0 201 self.showCount = -1 202 self.tickOnPaint = False 203 self.paused = False 204 self.doIntroTransitions = True 205 self.setAcceptHoverEvents(True) 206 self.setCursor(Qt.OpenHandCursor) 207 self.setupGuides() 208 self.setupLetters() 209 self.useGuideQt() 210 self.effect = None 211 212 self.mouseMoveLastPosition = QPointF() 213 self.tickTimer = QTime() 214 215 def createLetter(self, c): 216 letter = LetterItem(c, self) 217 self.letterList.append(letter) 218 219 def setupLetters(self): 220 self.letterList = [] 221 222 s = Colors.tickerText 223 tlen = len(s) 224 room = self.letterCount 225 while room >= tlen: 226 for c in s: 227 self.createLetter(c) 228 229 room -= tlen 230 231 # Fill in with blanks. 232 while room > 0: 233 self.createLetter(' ') 234 room -= 1 235 236 def setupGuides(self): 237 x = 0 238 y = 20 239 240 self.qtGuide1 = GuideCircle(QRectF(x, y, 260, 260), -36, 342) 241 GuideLine(QPointF(x + 240, y + 268), self.qtGuide1) 242 GuideLine(QPointF(x + 265, y + 246), self.qtGuide1) 243 GuideLine(QPointF(x + 158, y + 134), self.qtGuide1) 244 GuideLine(QPointF(x + 184, y + 109), self.qtGuide1) 245 GuideLine(QPointF(x + 160, y + 82), self.qtGuide1) 246 GuideLine(QPointF(x + 77, y + 163), self.qtGuide1) 247 GuideLine(QPointF(x + 100, y + 190), self.qtGuide1) 248 GuideLine(QPointF(x + 132, y + 159), self.qtGuide1) 249 GuideLine(QPointF(x + 188, y + 211), self.qtGuide1) 250 GuideCircle(QRectF(x + 30, y + 30, 200, 200), -30, 336, GuideCircle.CW, self.qtGuide1) 251 GuideLine(QPointF(x + 238, y + 201), self.qtGuide1) 252 253 y = 30 254 self.qtGuide2 = GuideCircle(QRectF(x + 30, y + 30, 200, 200), 135, 270, GuideCircle.CCW) 255 GuideLine(QPointF(x + 222, y + 38), self.qtGuide2) 256 GuideCircle(QRectF(x, y, 260, 260), 135, 270, GuideCircle.CW, self.qtGuide2) 257 GuideLine(QPointF(x + 59, y + 59), self.qtGuide2) 258 259 x = 115 260 y = 10 261 self.qtGuide3 = GuideLine(QLineF(x, y, x + 30, y)) 262 GuideLine(QPointF(x + 30, y + 170), self.qtGuide3) 263 GuideLine(QPointF(x, y + 170), self.qtGuide3) 264 GuideLine(QPointF(x, y), self.qtGuide3) 265 266 self.qtGuide1.setFence(QRectF(0, 0, 800, 600)) 267 self.qtGuide2.setFence(QRectF(0, 0, 800, 600)) 268 self.qtGuide3.setFence(QRectF(0, 0, 800, 600)) 269 270 def useGuide(self, guide, firstLetter, lastLetter): 271 padding = guide.lengthAll() / float(lastLetter - firstLetter) 272 273 for i, letter in enumerate(self.letterList[firstLetter:lastLetter]): 274 letter.useGuide(guide, i * padding) 275 276 def useGuideQt(self): 277 if self.currGuide is not self.qtGuide1: 278 self.useGuide(self.qtGuide1, 0, self.letterCount) 279 self.currGuide = self.qtGuide1 280 281 def useGuideTt(self): 282 if self.currGuide is not self.qtGuide2: 283 split = int(self.letterCount * 5.0 / 7.0) 284 self.useGuide(self.qtGuide2, 0, split) 285 self.useGuide(self.qtGuide3, split, self.letterCount) 286 self.currGuide = self.qtGuide2 287 288 def boundingRect(self): 289 return QRectF(0, 0, 300, 320) 290 291 def prepare(self): 292 pass 293 294 def switchToNextEffect(self): 295 self.showCount += 1 296 297 if self.showCount == 1: 298 self.effect = EffectSnake(self.letterList) 299 elif self.showCount == 2: 300 self.effect = EffectLine(self.letterList) 301 self.effect.setPostEffect(PostRotateXYTwist(0.01, 0.0, 0.003, 0.0)) 302 elif self.showCount == 3: 303 self.effect = EffectRaindrops(self.letterList) 304 self.effect.setPostEffect(PostRotateXYTwist(0.01, 0.005, 0.003, 0.003)) 305 elif self.showCount == 4: 306 self.effect = EffectScan(self.letterList) 307 self.effect.normalMoveSpeed = 0 308 self.effect.setPostEffect(PostRotateXY(0.008, 0.0, 0.005, 0.0)) 309 else: 310 self.showCount = 0 311 self.effect = EffectWhirlWind(self.letterList) 312 313 def animationStarted(self, id): 314 if id == DemoItemAnimation.ANIM_IN: 315 if self.doIntroTransitions: 316 # Make all letters disappear. 317 for letter in self.letterList: 318 letter.setPos(1000, 0) 319 320 self.switchToNextEffect() 321 self.useGuideQt() 322 self.scale = 1.0 323 324 # The first time we run, we have a rather large delay to 325 # perform benchmark before the ticker shows. But now, since we 326 # are showing, use a more appropriate value. 327 self.currentAnimation.setStartDelay(1500) 328 elif self.effect is not None: 329 self.effect.useSheepDog = False 330 331 self.tickTimer = QTime.currentTime() 332 333 def swapModel(self): 334 if self.currGuide is self.qtGuide2: 335 self.useGuideQt() 336 else: 337 self.useGuideTt() 338 339 def hoverEnterEvent(self, event): 340 # Skip swap here to enhance ticker dragging. 341 pass 342 343 def hoverLeaveEvent(self, event): 344 self.swapModel() 345 346 def setTickerScale(self, s): 347 self.scale = s 348 self.qtGuide1.setScale(self.scale, self.scale) 349 self.qtGuide2.setScale(self.scale, self.scale) 350 self.qtGuide3.setScale(self.scale, self.scale) 351 352 def mousePressEvent(self, event): 353 self.mouseMoveLastPosition = event.scenePos(); 354 355 if event.button() == Qt.LeftButton: 356 self.setCursor(Qt.ClosedHandCursor) 357 else: 358 self.switchToNextEffect() 359 360 def mouseReleaseEvent(self, event): 361 if event.button() == Qt.LeftButton: 362 self.setCursor(Qt.OpenHandCursor) 363 364 def mouseMoveEvent(self, event): 365 newPosition = event.scenePos() 366 self.setPosUsingSheepDog(self.pos() + newPosition - self.mouseMoveLastPosition, QRectF(-260, -280, 1350, 1160)) 367 self.mouseMoveLastPosition = newPosition 368 369 def wheelEvent(self, event): 370 if event.angleDelta().y() > 0: 371 self.effect.moveSpeed -= 0.20 372 else: 373 self.effect.moveSpeed += 0.20 374 375 if self.effect.moveSpeed < 0: 376 self.effect.moveSpeed = 0.0 377 378 def pause(self, on): 379 self.paused = on 380 self.tickTimer = QTime.currentTime() 381 382 def tick(self): 383 if self.paused or not self.effect: 384 return 385 386 t = self.tickTimer.msecsTo(QTime.currentTime()) 387 self.tickTimer = QTime.currentTime() 388 self.effect.tick(t / 10.0) 389 390 def paint(self, painter, opt, widget): 391 if self.tickOnPaint: 392 self.tick() 393