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 sys 44from xml.dom.minidom import parseString 45 46from PyQt5.QtCore import (QByteArray, QDir, QEasingCurve, QFile, QFileInfo, 47 QLibraryInfo, QObject, QPointF, QProcess, QProcessEnvironment, 48 QStandardPaths, Qt, QT_VERSION, QT_VERSION_STR, QTextStream, QUrl) 49from PyQt5.QtWidgets import QApplication, QMessageBox 50 51from colors import Colors 52from demoitemanimation import DemoItemAnimation 53from examplecontent import ExampleContent 54from itemcircleanimation import ItemCircleAnimation 55from menucontent import MenuContentItem 56from score import Score 57from textbutton import TextButton 58 59 60class MenuManager(QObject): 61 ROOT, MENU1, MENU2, LAUNCH, DOCUMENTATION, QUIT, FULLSCREEN, UP, DOWN, \ 62 BACK, LAUNCH_QML = range(11) 63 64 pInstance = None 65 66 def __init__(self): 67 super(MenuManager, self).__init__() 68 69 self.contentsDoc = None 70 self.assistantProcess = QProcess() 71 self.helpRootUrl = '' 72 self.docDir = QDir() 73 self.imgDir = QDir() 74 75 self.info = {} 76 self.window = None 77 78 self.ticker = None 79 self.tickerInAnim = None 80 self.upButton = None 81 self.downButton = None 82 self.score = Score() 83 self.currentMenu = "[no menu visible]" 84 self.currentCategory = "[no category visible]" 85 self.currentMenuButtons = "[no menu buttons visible]" 86 self.currentInfo = "[no info visible]" 87 self.currentMenuCode = -1 88 self.readXmlDocument() 89 90 @classmethod 91 def instance(cls): 92 if cls.pInstance is None: 93 cls.pInstance = cls() 94 95 return cls.pInstance 96 97 def getResource(self, name): 98 return QByteArray() 99 100 def readXmlDocument(self): 101 root = QFileInfo(__file__).absolutePath() 102 xml_file = QFile(root + '/examples.xml') 103 xml_file.open(QFile.ReadOnly | QFile.Text) 104 contents = xml_file.readAll().data() 105 xml_file.close() 106 107 self.contentsDoc = parseString(contents) 108 109 def itemSelected(self, userCode, menuName): 110 if userCode == MenuManager.LAUNCH: 111 self.launchExample(self.currentInfo) 112 elif userCode == MenuManager.LAUNCH_QML: 113 self.launchQml(self.currentInfo) 114 elif userCode == MenuManager.DOCUMENTATION: 115 self.showDocInAssistant(self.currentInfo) 116 elif userCode == MenuManager.QUIT: 117 QApplication.quit() 118 elif userCode == MenuManager.FULLSCREEN: 119 self.window.toggleFullscreen() 120 elif userCode == MenuManager.ROOT: 121 # Out. 122 self.score.queueMovie(self.currentMenu + ' -out', Score.FROM_START, 123 Score.LOCK_ITEMS) 124 self.score.queueMovie(self.currentMenuButtons + ' -out', 125 Score.FROM_START, Score.LOCK_ITEMS) 126 self.score.queueMovie(self.currentInfo + ' -out') 127 self.score.queueMovie(self.currentInfo + ' -buttons -out', 128 Score.NEW_ANIMATION_ONLY) 129 self.score.queueMovie('back -out', Score.ONLY_IF_VISIBLE) 130 131 # Book-keeping. 132 self.currentMenuCode = MenuManager.ROOT 133 self.currentMenu = menuName + ' -menu1' 134 self.currentMenuButtons = menuName + ' -buttons' 135 self.currentInfo = menuName + ' -info' 136 137 # In. 138 self.score.queueMovie('upndown -shake') 139 self.score.queueMovie(self.currentMenu, Score.FROM_START, 140 Score.UNLOCK_ITEMS) 141 self.score.queueMovie(self.currentMenuButtons, Score.FROM_START, 142 Score.UNLOCK_ITEMS) 143 self.score.queueMovie(self.currentInfo) 144 145 if not Colors.noTicker: 146 self.ticker.doIntroTransitions = True 147 self.tickerInAnim.setStartDelay(2000) 148 self.ticker.useGuideQt() 149 self.score.queueMovie('ticker', Score.NEW_ANIMATION_ONLY) 150 elif userCode == MenuManager.MENU1: 151 # Out. 152 self.score.queueMovie(self.currentMenu + ' -out', Score.FROM_START, 153 Score.LOCK_ITEMS) 154 self.score.queueMovie(self.currentMenuButtons + ' -out', 155 Score.FROM_START, Score.LOCK_ITEMS) 156 self.score.queueMovie(self.currentInfo + ' -out') 157 158 # Book-keeping. 159 self.currentMenuCode = MenuManager.MENU1 160 self.currentCategory = menuName 161 self.currentMenu = menuName + ' -menu1' 162 self.currentInfo = menuName + ' -info' 163 164 # In. 165 self.score.queueMovie('upndown -shake') 166 self.score.queueMovie('back -in') 167 self.score.queueMovie(self.currentMenu, Score.FROM_START, 168 Score.UNLOCK_ITEMS) 169 self.score.queueMovie(self.currentInfo) 170 171 if not Colors.noTicker: 172 self.ticker.useGuideTt() 173 elif userCode == MenuManager.MENU2: 174 # Out. 175 self.score.queueMovie(self.currentInfo + ' -out', 176 Score.NEW_ANIMATION_ONLY) 177 self.score.queueMovie(self.currentInfo + ' -buttons -out', 178 Score.NEW_ANIMATION_ONLY) 179 180 # Book-keeping. 181 self.currentMenuCode = MenuManager.MENU2 182 self.currentInfo = menuName 183 184 # In/shake. 185 self.score.queueMovie('upndown -shake') 186 self.score.queueMovie('back -shake') 187 self.score.queueMovie(self.currentMenu + ' -shake') 188 self.score.queueMovie(self.currentInfo, Score.NEW_ANIMATION_ONLY) 189 self.score.queueMovie(self.currentInfo + ' -buttons', 190 Score.NEW_ANIMATION_ONLY) 191 192 if not Colors.noTicker: 193 self.score.queueMovie('ticker -out', Score.NEW_ANIMATION_ONLY) 194 elif userCode == MenuManager.UP: 195 backMenu = self.info[self.currentMenu]['back'] 196 if backMenu: 197 self.score.queueMovie(self.currentMenu + ' -top_out', 198 Score.FROM_START, Score.LOCK_ITEMS) 199 self.score.queueMovie(backMenu + ' -bottom_in', 200 Score.FROM_START, Score.UNLOCK_ITEMS) 201 self.currentMenu = backMenu 202 elif userCode == MenuManager.DOWN: 203 moreMenu = self.info[self.currentMenu]['more'] 204 if moreMenu: 205 self.score.queueMovie(self.currentMenu + ' -bottom_out', 206 Score.FROM_START, Score.LOCK_ITEMS) 207 self.score.queueMovie(moreMenu + ' -top_in', Score.FROM_START, 208 Score.UNLOCK_ITEMS) 209 self.currentMenu = moreMenu 210 elif userCode == MenuManager.BACK: 211 if self.currentMenuCode == MenuManager.MENU2: 212 # Out. 213 self.score.queueMovie(self.currentInfo + ' -out', 214 Score.NEW_ANIMATION_ONLY) 215 self.score.queueMovie(self.currentInfo + ' -buttons -out', 216 Score.NEW_ANIMATION_ONLY) 217 218 # Book-keeping. 219 self.currentMenuCode = MenuManager.MENU1 220 self.currentMenuButtons = self.currentCategory + ' -buttons' 221 self.currentInfo = self.currentCategory + ' -info' 222 223 # In/shake. 224 self.score.queueMovie('upndown -shake') 225 self.score.queueMovie(self.currentMenu + ' -shake') 226 self.score.queueMovie(self.currentInfo, 227 Score.NEW_ANIMATION_ONLY) 228 self.score.queueMovie(self.currentInfo + ' -buttons', 229 Score.NEW_ANIMATION_ONLY) 230 231 if not Colors.noTicker: 232 self.ticker.doIntroTransitions = False 233 self.tickerInAnim.setStartDelay(500) 234 self.score.queueMovie('ticker', Score.NEW_ANIMATION_ONLY) 235 elif self.currentMenuCode != MenuManager.ROOT: 236 self.itemSelected(MenuManager.ROOT, Colors.rootMenuName) 237 238 # Update back and more buttons. 239 if self.info.setdefault(self.currentMenu, {}).get('back'): 240 back_state = TextButton.OFF 241 else: 242 back_state = TextButton.DISABLED 243 244 if self.info[self.currentMenu].get('more'): 245 more_state = TextButton.OFF 246 else: 247 more_state = TextButton.DISABLED 248 249 self.upButton.setState(back_state) 250 self.downButton.setState(more_state) 251 252 if self.score.hasQueuedMovies(): 253 self.score.playQue() 254 # Playing new movies might include loading etc., so ignore the FPS 255 # at this point. 256 self.window.fpsHistory = [] 257 258 def showDocInAssistant(self, name): 259 url = self.resolveDocUrl(name) 260 Colors.debug("Sending URL to Assistant:", url) 261 262 # Start assistant if it's not already running. 263 if self.assistantProcess.state() != QProcess.Running: 264 app = QLibraryInfo.location(QLibraryInfo.BinariesPath) + QDir.separator() 265 266 if sys.platform == 'darwin': 267 app += 'Assistant.app/Contents/MacOS/Assistant' 268 else: 269 app += 'assistant' 270 271 args = ['-enableRemoteControl'] 272 self.assistantProcess.start(app, args) 273 if not self.assistantProcess.waitForStarted(): 274 QMessageBox.critical(None, "PyQt Demo", 275 "Could not start %s." % app) 276 return 277 278 # Send command through remote control even if the process was just 279 # started to activate assistant and bring it to the front. 280 cmd_str = QTextStream(self.assistantProcess) 281 cmd_str << 'SetSource ' << url << '\n' 282 283 def launchExample(self, name): 284 executable = self.resolveExeFile(name) 285 286 process = QProcess(self) 287 process.error.connect(self.launchError) 288 289 if sys.platform == 'win32': 290 # Make sure it finds the DLLs on Windows. 291 env = QProcessEnvironment.systemEnvironment() 292 env.insert('PATH', 293 QLibraryInfo.location(QLibraryInfo.BinariesPath) + ';' + 294 env.value('PATH')) 295 process.setProcessEnvironment(env) 296 297 if self.info[name]['changedirectory'] != 'false': 298 workingDirectory = self.resolveDataDir(name) 299 process.setWorkingDirectory(workingDirectory) 300 Colors.debug("Setting working directory:", workingDirectory) 301 302 Colors.debug("Launching:", executable) 303 process.start(sys.executable, [executable]) 304 305 def launchQml(self, name): 306 import_path = self.resolveDataDir(name) 307 qml = self.resolveQmlFile(name) 308 309 process = QProcess(self) 310 process.error.connect(self.launchError) 311 312 env = QProcessEnvironment.systemEnvironment() 313 env.insert('QML2_IMPORT_PATH', import_path) 314 process.setProcessEnvironment(env) 315 316 executable = QLibraryInfo.location(QLibraryInfo.BinariesPath) + '/qmlscene' 317 Colors.debug("Launching:", executable) 318 process.start(executable, [qml]) 319 320 def launchError(self, error): 321 if error != QProcess.Crashed: 322 QMessageBox.critical(None, "Failed to launch the example", 323 "Could not launch the example. Ensure that it has been " 324 "built.", 325 QMessageBox.Cancel) 326 327 def init(self, window): 328 self.window = window 329 330 # Create div. 331 self.createTicker() 332 self.createUpnDownButtons() 333 self.createBackButton() 334 335 # Create first level menu. 336 rootElement = self.contentsDoc.documentElement 337 self.createRootMenu(rootElement) 338 339 # Create second level menus. 340 level2Menu = self._first_element(rootElement) 341 while level2Menu is not None: 342 self.createSubMenu(level2Menu) 343 344 # Create leaf menu and example info. 345 example = self._first_element(level2Menu) 346 while example is not None: 347 self.readInfoAboutExample(example) 348 self.createLeafMenu(example) 349 example = self._next_element(example) 350 351 level2Menu = self._next_element(level2Menu) 352 353 @classmethod 354 def _first_element(cls, node): 355 return cls._skip_nonelements(node.firstChild) 356 357 @classmethod 358 def _next_element(cls, node): 359 return cls._skip_nonelements(node.nextSibling) 360 361 @staticmethod 362 def _skip_nonelements(node): 363 while node is not None and node.nodeType != node.ELEMENT_NODE: 364 node = node.nextSibling 365 366 return node 367 368 def readInfoAboutExample(self, example): 369 name = example.getAttribute('name') 370 if name in self.info: 371 Colors.debug("__WARNING: MenuManager.readInfoAboutExample: " 372 "Demo/example with name", name, "appears twice in " 373 "the xml-file!__") 374 375 self.info.setdefault(name, {})['filename'] = example.getAttribute('filename') 376 self.info[name]['dirname'] = example.parentNode.getAttribute('dirname') 377 self.info[name]['changedirectory'] = example.getAttribute('changedirectory') 378 self.info[name]['image'] = example.getAttribute('image') 379 self.info[name]['qml'] = example.getAttribute('qml') 380 381 def resolveDir(self, name): 382 dirName = self.info[name]['dirname'] 383 fileName = self.info[name]['filename'].split('/') 384 385 dir = QFileInfo(__file__).dir() 386 # To the 'examples' directory. 387 dir.cdUp() 388 389 dir.cd(dirName) 390 391 if len(fileName) > 1: 392 dir.cd('/'.join(fileName[:-1])) 393 394 # This may legitimately fail if the example is just a simple .py file. 395 dir.cd(fileName[-1]) 396 397 return dir 398 399 def resolveDataDir(self, name): 400 return self.resolveDir(name).absolutePath() 401 402 def resolveExeFile(self, name): 403 dir = self.resolveDir(name) 404 405 fileName = self.info[name]['filename'].split('/')[-1] 406 407 pyFile = QFile(dir.path() + '/' + fileName + '.py') 408 if pyFile.exists(): 409 return pyFile.fileName() 410 411 pywFile = QFile(dir.path() + '/' + fileName + '.pyw') 412 if pywFile.exists(): 413 return pywFile.fileName() 414 415 Colors.debug("- WARNING: Could not resolve executable:", dir.path(), 416 fileName) 417 return '__executable not found__' 418 419 def resolveQmlFile(self, name): 420 dir = self.resolveDir(name) 421 422 fileName = self.info[name]['filename'].split('/')[-1] 423 424 qmlFile = QFile(dir.path() + '/' + fileName + '.qml') 425 if qmlFile.exists(): 426 return qmlFile.fileName() 427 428 Colors.debug("- WARNING: Could not resolve QML file:", dir.path(), 429 fileName) 430 return '__QML not found__' 431 432 def resolveDocUrl(self, name): 433 dirName = self.info[name]['dirname'] 434 fileName = self.info[name]['filename'] 435 436 return self.helpRootUrl + dirName.replace('/', '-') + '-' + fileName + '.html' 437 438 def resolveImageUrl(self, name): 439 return self.helpRootUrl + 'images/' + name 440 441 def getHtml(self, name): 442 return self.getResource(self.resolveDocUrl(name)) 443 444 def getImage(self, name): 445 imageName = self.info[name]['image'] 446 fileName = self.info[name]['filename'] 447 448 if self.info[name]['qml'] == 'true': 449 fileName = 'qml-' + fileName.split('/')[-1] 450 451 if not imageName: 452 imageName = fileName + '-example.png' 453 454 if self.getResource(self.resolveImageUrl(imageName)).isEmpty(): 455 imageName = fileName + '.png' 456 457 if self.getResource(self.resolveImageUrl(imageName)).isEmpty(): 458 imageName = fileName + 'example.png' 459 460 return self.getResource(self.resolveImageUrl(imageName)) 461 462 def createRootMenu(self, el): 463 name = el.getAttribute('name') 464 self.createMenu(el, MenuManager.MENU1) 465 self.createInfo( 466 MenuContentItem(el, self.window.mainSceneRoot), 467 name + ' -info') 468 469 menuButtonsIn = self.score.insertMovie(name + ' -buttons') 470 menuButtonsOut = self.score.insertMovie(name + ' -buttons -out') 471 self.createLowLeftButton("Quit", MenuManager.QUIT, menuButtonsIn, 472 menuButtonsOut, None) 473 self.createLowRightButton("Toggle fullscreen", MenuManager.FULLSCREEN, 474 menuButtonsIn, menuButtonsOut, None) 475 476 def createSubMenu(self, el): 477 name = el.getAttribute('name') 478 self.createMenu(el, MenuManager.MENU2) 479 self.createInfo( 480 MenuContentItem(el, self.window.mainSceneRoot), 481 name + ' -info') 482 483 def createLeafMenu(self, el): 484 name = el.getAttribute('name') 485 self.createInfo(ExampleContent(name, self.window.mainSceneRoot), name) 486 487 infoButtonsIn = self.score.insertMovie(name + ' -buttons') 488 infoButtonsOut = self.score.insertMovie(name + ' -buttons -out') 489 self.createLowRightLeafButton("Documentation", 600, 490 MenuManager.DOCUMENTATION, infoButtonsIn, infoButtonsOut, None) 491 if el.getAttribute('executable') != 'false': 492 self.createLowRightLeafButton("Launch", 405, MenuManager.LAUNCH, 493 infoButtonsIn, infoButtonsOut, None) 494 elif el.getAttribute('qml') == 'true': 495 self.createLowRightLeafButton("Display", 405, 496 MenuManager.LAUNCH_QML, infoButtonsIn, infoButtonsOut, 497 None) 498 499 def createMenu(self, category, type): 500 sw = self.window.scene.sceneRect().width() 501 xOffset = 15 502 yOffset = 10 503 maxExamples = Colors.menuCount 504 menuIndex = 1 505 name = category.getAttribute('name') 506 currentNode = self._first_element(category) 507 currentMenu = '%s -menu%d' % (name, menuIndex) 508 509 while currentNode is not None: 510 movieIn = self.score.insertMovie(currentMenu) 511 movieOut = self.score.insertMovie(currentMenu + ' -out') 512 movieNextTopOut = self.score.insertMovie(currentMenu + ' -top_out') 513 movieNextBottomOut = self.score.insertMovie(currentMenu + ' -bottom_out') 514 movieNextTopIn = self.score.insertMovie(currentMenu + ' -top_in') 515 movieNextBottomIn = self.score.insertMovie(currentMenu + ' -bottom_in') 516 movieShake = self.score.insertMovie(currentMenu + ' -shake') 517 518 i = 0 519 while currentNode is not None and i < maxExamples: 520 # Create a normal menu button. 521 label = currentNode.getAttribute('name') 522 item = TextButton(label, TextButton.LEFT, type, 523 self.window.mainSceneRoot) 524 525 item.setRecursiveVisible(False) 526 item.setZValue(10) 527 ih = item.sceneBoundingRect().height() 528 iw = item.sceneBoundingRect().width() 529 ihp = ih + 3 530 531 # Create in-animation. 532 anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN) 533 anim.setDuration(1000 + (i * 20)) 534 anim.setStartValue(QPointF(xOffset, -ih)) 535 anim.setKeyValueAt(0.20, QPointF(xOffset, -ih)) 536 anim.setKeyValueAt(0.50, QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY + (10 * float(i / 4.0)))) 537 anim.setKeyValueAt(0.60, QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY)) 538 anim.setKeyValueAt(0.70, QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY + (5 * float(i / 4.0)))) 539 anim.setKeyValueAt(0.80, QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY)) 540 anim.setKeyValueAt(0.90, QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY + (2 * float(i / 4.0)))) 541 anim.setEndValue(QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY)) 542 movieIn.append(anim) 543 544 # Create out-animation. 545 anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT) 546 anim.setHideOnFinished(True) 547 anim.setDuration(700 + (30 * i)) 548 anim.setStartValue(QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY)) 549 anim.setKeyValueAt(0.60, QPointF(xOffset, 600 - ih - ih)) 550 anim.setKeyValueAt(0.65, QPointF(xOffset + 20, 600 - ih)) 551 anim.setEndValue(QPointF(sw + iw, 600 - ih)) 552 movieOut.append(anim) 553 554 # Create shake-animation. 555 anim = DemoItemAnimation(item) 556 anim.setDuration(700) 557 anim.setStartValue(QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY)) 558 anim.setKeyValueAt(0.55, QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY - i*2.0)) 559 anim.setKeyValueAt(0.70, QPointF(xOffset - 10, (i * ihp) + yOffset + Colors.contentStartY - i*1.5)) 560 anim.setKeyValueAt(0.80, QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY - i*1.0)) 561 anim.setKeyValueAt(0.90, QPointF(xOffset - 2, (i * ihp) + yOffset + Colors.contentStartY - i*0.5)) 562 anim.setEndValue(QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY)) 563 movieShake.append(anim) 564 565 # Create next-menu top-out-animation. 566 anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT) 567 anim.setHideOnFinished(True) 568 anim.setDuration(200 + (30 * i)) 569 anim.setStartValue(QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY)) 570 anim.setKeyValueAt(0.70, QPointF(xOffset, yOffset + Colors.contentStartY)) 571 anim.setEndValue(QPointF(-iw, yOffset + Colors.contentStartY)) 572 movieNextTopOut.append(anim) 573 574 # Create next-menu bottom-out-animation. 575 anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT) 576 anim.setHideOnFinished(True) 577 anim.setDuration(200 + (30 * i)) 578 anim.setStartValue(QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY)) 579 anim.setKeyValueAt(0.70, QPointF(xOffset, (maxExamples * ihp) + yOffset + Colors.contentStartY)) 580 anim.setEndValue(QPointF(-iw, (maxExamples * ihp) + yOffset + Colors.contentStartY)) 581 movieNextBottomOut.append(anim) 582 583 # Create next-menu top-in-animation. 584 anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN) 585 anim.setDuration(700 - (30 * i)) 586 anim.setStartValue(QPointF(-iw, yOffset + Colors.contentStartY)) 587 anim.setKeyValueAt(0.30, QPointF(xOffset, yOffset + Colors.contentStartY)) 588 anim.setEndValue(QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY)) 589 movieNextTopIn.append(anim) 590 591 # Create next-menu bottom-in-animation. 592 reverse = maxExamples - i 593 anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN) 594 anim.setDuration(1000 - (30 * reverse)) 595 anim.setStartValue(QPointF(-iw, (maxExamples * ihp) + yOffset + Colors.contentStartY)) 596 anim.setKeyValueAt(0.30, QPointF(xOffset, (maxExamples * ihp) + yOffset + Colors.contentStartY)) 597 anim.setEndValue(QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY)) 598 movieNextBottomIn.append(anim) 599 600 i += 1 601 currentNode = self._next_element(currentNode) 602 603 if currentNode is not None and i == maxExamples: 604 # We need another menu, so register for 'more' and 'back' 605 # buttons. 606 menuIndex += 1 607 self.info.setdefault(currentMenu, {})['more'] = '%s -menu%d' % (name, menuIndex) 608 currentMenu = '%s -menu%d' % (name, menuIndex) 609 self.info.setdefault(currentMenu, {})['back'] = '%s -menu%d' % (name, menuIndex - 1) 610 611 def createLowLeftButton(self, label, type, movieIn, movieOut, movieShake, menuString=""): 612 button = TextButton(label, TextButton.RIGHT, type, 613 self.window.mainSceneRoot, TextButton.PANEL) 614 if menuString: 615 button.setMenuString(menuString) 616 button.setRecursiveVisible(False) 617 button.setZValue(10) 618 619 iw = button.sceneBoundingRect().width() 620 xOffset = 15 621 622 # Create in-animation. 623 buttonIn = DemoItemAnimation(button, DemoItemAnimation.ANIM_IN) 624 buttonIn.setDuration(1800) 625 buttonIn.setStartValue(QPointF(-iw, Colors.contentStartY + Colors.contentHeight - 35)) 626 buttonIn.setKeyValueAt(0.5, QPointF(-iw, Colors.contentStartY + Colors.contentHeight - 35)) 627 buttonIn.setKeyValueAt(0.7, QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 35)) 628 buttonIn.setEndValue(QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 26)) 629 movieIn.append(buttonIn) 630 631 # Create out-animation. 632 buttonOut = DemoItemAnimation(button, DemoItemAnimation.ANIM_OUT) 633 buttonOut.setHideOnFinished(True) 634 buttonOut.setDuration(400) 635 buttonOut.setStartValue(QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 26)) 636 buttonOut.setEndValue(QPointF(-iw, Colors.contentStartY + Colors.contentHeight - 26)) 637 movieOut.append(buttonOut) 638 639 if movieShake is not None: 640 shakeAnim = DemoItemAnimation(button, DemoItemAnimation.ANIM_UNSPECIFIED) 641 shakeAnim.setDuration(650) 642 shakeAnim.setStartValue(buttonIn.endValue()) 643 shakeAnim.setKeyValueAt(0.60, buttonIn.endValue()) 644 shakeAnim.setKeyValueAt(0.70, buttonIn.endValue() + QPointF(-3, 0)) 645 shakeAnim.setKeyValueAt(0.80, buttonIn.endValue() + QPointF(2, 0)) 646 shakeAnim.setKeyValueAt(0.90, buttonIn.endValue() + QPointF(-1, 0)) 647 shakeAnim.setEndValue(buttonIn.endValue()) 648 movieShake.append(shakeAnim) 649 650 def createLowRightButton(self, label, type, movieIn, movieOut, movieShake): 651 item = TextButton(label, TextButton.RIGHT, type, 652 self.window.mainSceneRoot, TextButton.PANEL) 653 item.setRecursiveVisible(False) 654 item.setZValue(10) 655 656 sw = self.window.scene.sceneRect().width() 657 xOffset = 70 658 659 # Create in-animation. 660 anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN) 661 anim.setDuration(1800) 662 anim.setStartValue(QPointF(sw, Colors.contentStartY + Colors.contentHeight - 35)) 663 anim.setKeyValueAt(0.5, QPointF(sw, Colors.contentStartY + Colors.contentHeight - 35)) 664 anim.setKeyValueAt(0.7, QPointF(xOffset + 535, Colors.contentStartY + Colors.contentHeight - 35)) 665 anim.setEndValue(QPointF(xOffset + 535, Colors.contentStartY + Colors.contentHeight - 26)) 666 movieIn.append(anim) 667 668 # Create out-animation. 669 anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT) 670 anim.setHideOnFinished(True) 671 anim.setDuration(400) 672 anim.setStartValue(QPointF(xOffset + 535, Colors.contentStartY + Colors.contentHeight - 26)) 673 anim.setEndValue(QPointF(sw, Colors.contentStartY + Colors.contentHeight - 26)) 674 movieOut.append(anim) 675 676 def createLowRightLeafButton(self, label, xOffset, type, movieIn, movieOut, movieShake): 677 item = TextButton(label, TextButton.RIGHT, type, 678 self.window.mainSceneRoot, TextButton.PANEL) 679 item.setRecursiveVisible(False) 680 item.setZValue(10) 681 682 sw = self.window.scene.sceneRect().width() 683 sh = self.window.scene.sceneRect().height() 684 685 # Create in-animation. 686 anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN) 687 anim.setDuration(1050) 688 anim.setStartValue(QPointF(sw, Colors.contentStartY + Colors.contentHeight - 35)) 689 anim.setKeyValueAt(0.10, QPointF(sw, Colors.contentStartY + Colors.contentHeight - 35)) 690 anim.setKeyValueAt(0.30, QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 35)) 691 anim.setKeyValueAt(0.35, QPointF(xOffset + 30, Colors.contentStartY + Colors.contentHeight - 35)) 692 anim.setKeyValueAt(0.40, QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 35)) 693 anim.setKeyValueAt(0.45, QPointF(xOffset + 5, Colors.contentStartY + Colors.contentHeight - 35)) 694 anim.setKeyValueAt(0.50, QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 35)) 695 anim.setEndValue(QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 26)) 696 movieIn.append(anim) 697 698 # Create out-animation. 699 anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT) 700 anim.setHideOnFinished(True) 701 anim.setDuration(300) 702 anim.setStartValue(QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 26)) 703 anim.setEndValue(QPointF(xOffset, sh)) 704 movieOut.append(anim) 705 706 def createInfo(self, item, name): 707 movie_in = self.score.insertMovie(name) 708 movie_out = self.score.insertMovie(name + ' -out') 709 item.setZValue(8) 710 item.setRecursiveVisible(False) 711 712 xOffset = 230.0 713 infoIn = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN) 714 infoIn.setDuration(650) 715 infoIn.setStartValue(QPointF(self.window.scene.sceneRect().width(), Colors.contentStartY)) 716 infoIn.setKeyValueAt(0.60, QPointF(xOffset, Colors.contentStartY)) 717 infoIn.setKeyValueAt(0.70, QPointF(xOffset + 20, Colors.contentStartY)) 718 infoIn.setKeyValueAt(0.80, QPointF(xOffset, Colors.contentStartY)) 719 infoIn.setKeyValueAt(0.90, QPointF(xOffset + 7, Colors.contentStartY)) 720 infoIn.setEndValue(QPointF(xOffset, Colors.contentStartY)) 721 movie_in.append(infoIn) 722 723 infoOut = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT) 724 infoOut.setCurveShape(QEasingCurve.InQuad) 725 infoOut.setDuration(300) 726 infoOut.setHideOnFinished(True) 727 infoOut.setStartValue(QPointF(xOffset, Colors.contentStartY)) 728 infoOut.setEndValue(QPointF(-600, Colors.contentStartY)) 729 movie_out.append(infoOut) 730 731 def createTicker(self): 732 if Colors.noTicker: 733 return 734 735 movie_in = self.score.insertMovie('ticker') 736 movie_out = self.score.insertMovie('ticker -out') 737 movie_activate = self.score.insertMovie('ticker -activate') 738 movie_deactivate = self.score.insertMovie('ticker -deactivate') 739 740 self.ticker = ItemCircleAnimation() 741 self.ticker.setZValue(50) 742 self.ticker.hide() 743 744 # Move ticker in. 745 qtendpos = 485 746 qtPosY = 120 747 self.tickerInAnim = DemoItemAnimation(self.ticker, 748 DemoItemAnimation.ANIM_IN) 749 self.tickerInAnim.setDuration(500) 750 self.tickerInAnim.setStartValue(QPointF(self.window.scene.sceneRect().width(), Colors.contentStartY + qtPosY)) 751 self.tickerInAnim.setKeyValueAt(0.60, QPointF(qtendpos, Colors.contentStartY + qtPosY)) 752 self.tickerInAnim.setKeyValueAt(0.70, QPointF(qtendpos + 30, Colors.contentStartY + qtPosY)) 753 self.tickerInAnim.setKeyValueAt(0.80, QPointF(qtendpos, Colors.contentStartY + qtPosY)) 754 self.tickerInAnim.setKeyValueAt(0.90, QPointF(qtendpos + 5, Colors.contentStartY + qtPosY)) 755 self.tickerInAnim.setEndValue(QPointF(qtendpos, Colors.contentStartY + qtPosY)) 756 movie_in.append(self.tickerInAnim) 757 758 # Move ticker out. 759 qtOut = DemoItemAnimation(self.ticker, DemoItemAnimation.ANIM_OUT) 760 qtOut.setHideOnFinished(True) 761 qtOut.setDuration(500) 762 qtOut.setStartValue(QPointF(qtendpos, Colors.contentStartY + qtPosY)) 763 qtOut.setEndValue(QPointF(self.window.scene.sceneRect().width() + 700, Colors.contentStartY + qtPosY)) 764 movie_out.append(qtOut) 765 766 # Move ticker in on activate. 767 qtActivate = DemoItemAnimation(self.ticker) 768 qtActivate.setDuration(400) 769 qtActivate.setStartValue(QPointF(self.window.scene.sceneRect().width(), Colors.contentStartY + qtPosY)) 770 qtActivate.setKeyValueAt(0.60, QPointF(qtendpos, Colors.contentStartY + qtPosY)) 771 qtActivate.setKeyValueAt(0.70, QPointF(qtendpos + 30, Colors.contentStartY + qtPosY)) 772 qtActivate.setKeyValueAt(0.80, QPointF(qtendpos, Colors.contentStartY + qtPosY)) 773 qtActivate.setKeyValueAt(0.90, QPointF(qtendpos + 5, Colors.contentStartY + qtPosY)) 774 qtActivate.setEndValue(QPointF(qtendpos, Colors.contentStartY + qtPosY)) 775 movie_activate.append(qtActivate) 776 777 # Move ticker out on deactivate. 778 qtDeactivate = DemoItemAnimation(self.ticker) 779 qtDeactivate.setHideOnFinished(True) 780 qtDeactivate.setDuration(400) 781 qtDeactivate.setStartValue(QPointF(qtendpos, Colors.contentStartY + qtPosY)) 782 qtDeactivate.setEndValue(QPointF(qtendpos, 800)) 783 movie_deactivate.append(qtDeactivate) 784 785 def createUpnDownButtons(self): 786 xOffset = 15.0 787 yOffset = 450.0 788 789 self.upButton = TextButton("", TextButton.LEFT, MenuManager.UP, 790 self.window.mainSceneRoot, TextButton.UP) 791 self.upButton.prepare() 792 self.upButton.setPos(xOffset, yOffset) 793 self.upButton.setState(TextButton.DISABLED) 794 795 self.downButton = TextButton("", TextButton.LEFT, MenuManager.DOWN, 796 self.window.mainSceneRoot, TextButton.DOWN) 797 self.downButton.prepare() 798 self.downButton.setPos(xOffset + 10 + self.downButton.sceneBoundingRect().width(), yOffset) 799 800 movieShake = self.score.insertMovie('upndown -shake') 801 802 shakeAnim = DemoItemAnimation(self.upButton, 803 DemoItemAnimation.ANIM_UNSPECIFIED) 804 shakeAnim.setDuration(650) 805 shakeAnim.setStartValue(self.upButton.pos()) 806 shakeAnim.setKeyValueAt(0.60, self.upButton.pos()) 807 shakeAnim.setKeyValueAt(0.70, self.upButton.pos() + QPointF(-2, 0)) 808 shakeAnim.setKeyValueAt(0.80, self.upButton.pos() + QPointF(1, 0)) 809 shakeAnim.setKeyValueAt(0.90, self.upButton.pos() + QPointF(-1, 0)) 810 shakeAnim.setEndValue(self.upButton.pos()) 811 movieShake.append(shakeAnim) 812 813 shakeAnim = DemoItemAnimation(self.downButton, 814 DemoItemAnimation.ANIM_UNSPECIFIED) 815 shakeAnim.setDuration(650) 816 shakeAnim.setStartValue(self.downButton.pos()) 817 shakeAnim.setKeyValueAt(0.60, self.downButton.pos()) 818 shakeAnim.setKeyValueAt(0.70, self.downButton.pos() + QPointF(-5, 0)) 819 shakeAnim.setKeyValueAt(0.80, self.downButton.pos() + QPointF(-3, 0)) 820 shakeAnim.setKeyValueAt(0.90, self.downButton.pos() + QPointF(-1, 0)) 821 shakeAnim.setEndValue(self.downButton.pos()) 822 movieShake.append(shakeAnim) 823 824 def createBackButton(self): 825 backIn = self.score.insertMovie('back -in') 826 backOut = self.score.insertMovie('back -out') 827 backShake = self.score.insertMovie('back -shake') 828 self.createLowLeftButton("Back", MenuManager.ROOT, backIn, backOut, 829 backShake, Colors.rootMenuName) 830