Exemple #1
0
class MenuManager(QtCore.QObject):
    ROOT, MENU1, MENU2, LAUNCH, DOCUMENTATION, QUIT, FULLSCREEN, UP, DOWN, \
            BACK, LAUNCH_QML = range(11)

    pInstance = None

    def __init__(self):
        super(MenuManager, self).__init__()

        self.contentsDoc = None
        self.assistantProcess = QtCore.QProcess()
        self.helpRootUrl = ''
        self.docDir = QtCore.QDir()
        self.imgDir = QtCore.QDir()

        self.info = {}
        self.window = None
        self.qmlRoot = None
        self.declarativeEngine = None

        self.ticker = None
        self.tickerInAnim = None
        self.upButton = None
        self.downButton = None
        self.helpEngine = None
        self.score = Score()
        self.currentMenu = "[no menu visible]"
        self.currentCategory = "[no category visible]"
        self.currentMenuButtons = "[no menu buttons visible]"
        self.currentInfo = "[no info visible]"
        self.currentMenuCode = -1
        self.readXmlDocument()
        self.initHelpEngine()

    @classmethod
    def instance(cls):
        if cls.pInstance is None:
            cls.pInstance = cls()

        return cls.pInstance

    def getResource(self, name):
        ba = self.helpEngine.fileData(QtCore.QUrl(name))

        if ba.isEmpty():
            Colors.debug(" - WARNING: Could not get", name)

        return ba

    def readXmlDocument(self):
        self.contentsDoc = QtXml.QDomDocument()

        xml_file = QtCore.QFile(':/xml/examples.xml')
        statusOK, errorStr, errorLine, errorColumn = \
                self.contentsDoc.setContent(xml_file, True)

        if not statusOK:
            QtGui.QMessageBox.critical(None, "DOM Parser",
                    "Could not read or find the contents document. Error at "
                    "line %d, column %d:\n%s" % (errorLine, errorColumn, errorStr))
            sys.exit(-1)

    def initHelpEngine(self):
        self.helpRootUrl = 'qthelp://com.trolltech.qt.%d%d%d/qdoc/' % (QtCore.QT_VERSION >> 16, (QtCore.QT_VERSION >> 8) & 0xff, QtCore.QT_VERSION & 0xff)

        # Store help collection file in cache dir of assistant.
        cacheDir = QtGui.QDesktopServices.storageLocation(QtGui.QDesktopServices.DataLocation) + '/Trolltech/Assistant/'
        helpDataFile = 'qtdemo_%s.qhc' % QtCore.QT_VERSION_STR

        dir = QtCore.QDir()
        if not dir.exists(cacheDir):
            dir.mkpath(cacheDir)

        # Create help engine (and new helpDataFile if it does not exist).
        self.helpEngine = QtHelp.QHelpEngineCore(cacheDir + helpDataFile)
        self.helpEngine.setupData()

        qtDocRoot = QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.DocumentationPath) + '/qch'
        qtDocRoot = QtCore.QDir(qtDocRoot).absolutePath()

        qchFiles = ['/qt.qch', '/designer.qch', '/linguist.qch']

        oldDir = self.helpEngine.customValue('docDir', '')
        if oldDir != qtDocRoot:
            for qchFile in qchFiles:
                self.helpEngine.unregisterDocumentation(QtHelp.QHelpEngineCore.namespaceName(qtDocRoot + qchFile))

        # If the data that the engine will work on is not yet registered, do it
        # now.
        for qchFile in qchFiles:
            self.helpEngine.registerDocumentation(qtDocRoot + qchFile)

        self.helpEngine.setCustomValue('docDir', qtDocRoot)

    def itemSelected(self, userCode, menuName):
        if userCode == MenuManager.LAUNCH:
            self.launchExample(self.currentInfo)
        elif userCode == MenuManager.LAUNCH_QML:
            self.launchQmlExample(self.currentInfo)
        elif userCode == MenuManager.DOCUMENTATION:
            self.showDocInAssistant(self.currentInfo)
        elif userCode == MenuManager.QUIT:
            self.window.quit()
        elif userCode == MenuManager.FULLSCREEN:
            self.window.toggleFullscreen()
        elif userCode == MenuManager.ROOT:
            # Out.
            self.score.queueMovie(self.currentMenu + ' -out', Score.FROM_START,
                    Score.LOCK_ITEMS)
            self.score.queueMovie(self.currentMenuButtons + ' -out',
                    Score.FROM_START, Score.LOCK_ITEMS)
            self.score.queueMovie(self.currentInfo + ' -out')
            self.score.queueMovie(self.currentInfo + ' -buttons -out',
                    Score.NEW_ANIMATION_ONLY)
            self.score.queueMovie('back -out', Score.ONLY_IF_VISIBLE)

            if self.qmlRoot is not None:
                self.qmlRoot.setProperty('show', False)

            # Book-keeping.
            self.currentMenuCode = MenuManager.ROOT
            self.currentMenu = menuName + ' -menu1'
            self.currentMenuButtons = menuName + ' -buttons'
            self.currentInfo = menuName + ' -info'

            # In.
            self.score.queueMovie('upndown -shake')
            self.score.queueMovie(self.currentMenu, Score.FROM_START,
                    Score.UNLOCK_ITEMS)
            self.score.queueMovie(self.currentMenuButtons, Score.FROM_START,
                    Score.UNLOCK_ITEMS)
            self.score.queueMovie(self.currentInfo)

            if not Colors.noTicker:
                self.ticker.doIntroTransitions = True
                self.tickerInAnim.startDelay = 2000
                self.ticker.useGuideQt()
                self.score.queueMovie('ticker', Score.NEW_ANIMATION_ONLY)
        elif userCode == MenuManager.MENU1:
            # Out.
            self.score.queueMovie(self.currentMenu + ' -out', Score.FROM_START,
                    Score.LOCK_ITEMS)
            self.score.queueMovie(self.currentMenuButtons + ' -out',
                    Score.FROM_START, Score.LOCK_ITEMS)
            self.score.queueMovie(self.currentInfo + ' -out')

            if self.qmlRoot is not None:
                self.qmlRoot.setProperty('show', False)

            # Book-keeping.
            self.currentMenuCode = MenuManager.MENU1
            self.currentCategory = menuName
            self.currentMenu = menuName + ' -menu1'
            self.currentInfo = menuName + ' -info'

            # In.
            self.score.queueMovie('upndown -shake')
            self.score.queueMovie('back -in')
            self.score.queueMovie(self.currentMenu, Score.FROM_START,
                    Score.UNLOCK_ITEMS)
            self.score.queueMovie(self.currentInfo)

            if not Colors.noTicker:
                self.ticker.useGuideTt()
        elif userCode == MenuManager.MENU2:
            # Out.
            self.score.queueMovie(self.currentInfo + ' -out',
                    Score.NEW_ANIMATION_ONLY)
            self.score.queueMovie(self.currentInfo + ' -buttons -out',
                    Score.NEW_ANIMATION_ONLY)

            if self.qmlRoot is not None:
                self.qmlRoot.setProperty('show', False)

            # Book-keeping.
            self.currentMenuCode = MenuManager.MENU2
            self.currentInfo = menuName

            # In/shake.
            self.score.queueMovie('upndown -shake')
            self.score.queueMovie('back -shake')
            self.score.queueMovie(self.currentMenu + ' -shake')
            self.score.queueMovie(self.currentInfo, Score.NEW_ANIMATION_ONLY)
            self.score.queueMovie(self.currentInfo + ' -buttons',
                    Score.NEW_ANIMATION_ONLY)

            if not Colors.noTicker:
                self.score.queueMovie('ticker -out', Score.NEW_ANIMATION_ONLY)
        elif userCode == MenuManager.UP:
            backMenu = self.info[self.currentMenu]['back']
            if backMenu:
                self.score.queueMovie(self.currentMenu + ' -top_out',
                        Score.FROM_START, Score.LOCK_ITEMS)
                self.score.queueMovie(backMenu + ' -bottom_in',
                        Score.FROM_START, Score.UNLOCK_ITEMS)
                self.currentMenu = backMenu
        elif userCode == MenuManager.DOWN:
            moreMenu = self.info[self.currentMenu]['more']
            if moreMenu:
                self.score.queueMovie(self.currentMenu + ' -bottom_out',
                        Score.FROM_START, Score.LOCK_ITEMS)
                self.score.queueMovie(moreMenu + ' -top_in', Score.FROM_START,
                        Score.UNLOCK_ITEMS)
                self.currentMenu = moreMenu
        elif userCode == MenuManager.BACK:
            if self.currentMenuCode == MenuManager.MENU2:
                # Out.
                self.score.queueMovie(self.currentInfo + ' -out',
                        Score.NEW_ANIMATION_ONLY)
                self.score.queueMovie(self.currentInfo + ' -buttons -out',
                        Score.NEW_ANIMATION_ONLY)

                if self.qmlRoot is not None:
                    self.qmlRoot.setProperty('show', False)

                # Book-keeping.
                self.currentMenuCode = MenuManager.MENU1
                self.currentMenuButtons = self.currentCategory + ' -buttons'
                self.currentInfo = self.currentCategory + ' -info'

                # In/shake.
                self.score.queueMovie('upndown -shake')
                self.score.queueMovie(self.currentMenu + ' -shake')
                self.score.queueMovie(self.currentInfo,
                        Score.NEW_ANIMATION_ONLY)
                self.score.queueMovie(self.currentInfo + ' -buttons',
                        Score.NEW_ANIMATION_ONLY)

                if not Colors.noTicker:
                    self.ticker.doIntroTransitions = False
                    self.tickerInAnim.startDelay = 500
                    self.score.queueMovie('ticker', Score.NEW_ANIMATION_ONLY)
            elif self.currentMenuCode != MenuManager.ROOT:
                self.itemSelected(MenuManager.ROOT, Colors.rootMenuName)

        # Update back and more buttons.
        if self.info.setdefault(self.currentMenu, {}).get('back'):
            back_state = TextButton.OFF
        else:
            back_state = TextButton.DISABLED

        if self.info[self.currentMenu].get('more'):
            more_state = TextButton.OFF
        else:
            more_state = TextButton.DISABLED

        self.upButton.setState(back_state)
        self.downButton.setState(more_state)

        if self.score.hasQueuedMovies():
            self.score.playQue()
            # Playing new movies might include loading etc., so ignore the FPS
            # at this point.
            self.window.fpsHistory = []

    def showDocInAssistant(self, name):
        url = self.resolveDocUrl(name)
        Colors.debug("Sending URL to Assistant:", url)

        # Start assistant if it's not already running.
        if self.assistantProcess.state() != QtCore.QProcess.Running:
            app = QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.BinariesPath) + QtCore.QDir.separator()

            if sys.platform == 'darwin':
                app += 'Assistant.app/Contents/MacOS/Assistant'
            else:
                app += 'assistant'

            args = ['-enableRemoteControl']
            self.assistantProcess.start(app, args)
            if not self.assistantProcess.waitForStarted():
                QtGui.QMessageBox.critical(None, "PyQt Demo",
                        "Could not start %s." % app)
                return

        # Send command through remote control even if the process was just
        # started to activate assistant and bring it to the front.
        cmd_str = QtCore.QTextStream(self.assistantProcess)
        cmd_str << 'SetSource ' << url << '\n'

    def launchExample(self, name):
        executable = self.resolveExeFile(name)

        process = QtCore.QProcess(self)
        process.finished.connect(self.exampleFinished)
        process.error.connect(self.exampleError)

        if sys.platform == 'win32':
            # Make sure it finds the DLLs on Windows.
            curpath = os.getenv('PATH')
            newpath = 'PATH=%s;%s' % (QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.BinariesPath), curpath)
            process.setEnvironment([newpath])

        if self.info[name]['changedirectory'] != 'false':
            workingDirectory = self.resolveDataDir(name)
            process.setWorkingDirectory(workingDirectory)
            Colors.debug("Setting working directory:", workingDirectory)

        Colors.debug("Launching:", executable)
        process.start(sys.executable, [executable])

    def launchQmlExample(self, name):
        if self.declarativeEngine is None:
            QtGui.QMessageBox.critical(None, "QtDeclarative unsupported",
                    "The QtDeclarative module is not supported by this "
                    "installation of PyQt.",
                    QtGui.QMessageBox.Cancel)
            return

        if self.qmlRoot is None:
            self.exampleError(QtCore.QProcess.UnknownError)
            return

        qml_dir = self.resolveDataDir(name)
        qml_file = QtCore.QFile(qml_dir + '/main.qml')

        if not qml_file.exists():
            qml_file.setFileName(qml_dir + '/' + qml_dir.split('/')[-1] + '.qml')
            if not qml_file.exists():
                self.exampleError(QtCore.QProcess.UnknownError)
                return

        # Unload any existing component.
        self.qmlRoot.setProperty('qmlFile', '')
        self.qmlRoot.setProperty('show', True)
        self.qmlRoot.setProperty('qmlFile', QtCore.QUrl.fromLocalFile(qml_file.fileName()))

    def exampleFinished(self):
        pass

    def exampleError(self, error):
        if error != QtCore.QProcess.Crashed:
            QtGui.QMessageBox.critical(None, "Failed to launch the example",
                    "Could not launch the example. Ensure that it has been "
                    "built.",
                    QtGui.QMessageBox.Cancel)

    def init(self, window):
        self.window = window

        # Create div.
        self.createTicker()
        self.createUpnDownButtons()
        self.createBackButton()

        # Create first level menu.
        rootElement = self.contentsDoc.documentElement()
        self.createRootMenu(rootElement)

        # Create second level menus.
        level2MenuNode = rootElement.firstChild()
        while not level2MenuNode.isNull():
            level2MenuElement = level2MenuNode.toElement()
            self.createSubMenu(level2MenuElement)

            # Create leaf menu and example info.
            exampleNode = level2MenuElement.firstChild()
            while not exampleNode.isNull():
                exampleElement = exampleNode.toElement()
                self.readInfoAboutExample(exampleElement)
                self.createLeafMenu(exampleElement)
                exampleNode = exampleNode.nextSibling()

            level2MenuNode = level2MenuNode.nextSibling()

        # Support QML if possible.
        try:
            from PyQt4 import QtDeclarative
        except ImportError:
            return

        self.declarativeEngine = QtDeclarative.QDeclarativeEngine(self)

        component = QtDeclarative.QDeclarativeComponent(self.declarativeEngine,
                QtCore.QUrl('qrc:/qml/qmlShell.qml'), self)
        if component.isReady():
            self.qmlRoot = component.create()

        if self.qmlRoot is not None:
            self.qmlRoot.setHeight(window.scene.sceneRect().height())
            self.qmlRoot.setWidth(window.scene.sceneRect().width())
            self.qmlRoot.setZValue(101)
            self.qmlRoot.setCursor(QtCore.Qt.ArrowCursor)
            window.scene.addItem(self.qmlRoot)

            window.viewport().setFocusPolicy(QtCore.Qt.NoFocus)
            window.setFocusPolicy(QtCore.Qt.StrongFocus)
            window.scene.setStickyFocus(True)
            window.setFocus()
        else:
            Colors.debug("Error initializing QML subsystem, Declarative examples will not work")

    def readInfoAboutExample(self, example):
        name = str(example.attribute('name'))
        if name in self.info:
            Colors.debug("__WARNING: MenuManager.readInfoAboutExample: "
                         "Demo/example with name", name, "appears twice in "
                         "the xml-file!__")

        self.info.setdefault(name, {})['filename'] = str(example.attribute('filename'))
        self.info[name]['category'] = str(example.parentNode().toElement().tagName())
        self.info[name]['dirname'] = str(example.parentNode().toElement().attribute('dirname'))
        self.info[name]['changedirectory'] = str(example.attribute('changedirectory'))
        self.info[name]['image'] = str(example.attribute('image'))
        self.info[name]['qml'] = str(example.attribute('qml'))

    def resolveDir(self, name):
        dirName = self.info[name]['dirname']
        category = self.info[name]['category']
        fileName = self.info[name]['filename'].split('/')

        dir = QtCore.QFileInfo(__file__).dir()
        # To the 'demos' directory.
        dir.cdUp()
        # To the 'examples' directory.
        dir.cdUp()

        dir.cd(dirName)

        if len(fileName) > 1:
            dir.cd('/'.join(fileName[:-1]))

        # This may legitimately fail if the example is just a simple .py file.
        dir.cd(fileName[-1])

        return dir

    def resolveDataDir(self, name):
        return self.resolveDir(name).absolutePath()

    def resolveExeFile(self, name):
        dir = self.resolveDir(name)

        fileName = self.info[name]['filename'].split('/')[-1]

        pyFile = QtCore.QFile(dir.path() + '/' + fileName + '.py')
        if pyFile.exists():
            return pyFile.fileName()

        pywFile = QtCore.QFile(dir.path() + '/' + fileName + '.pyw')
        if pywFile.exists():
            return pywFile.fileName()

        Colors.debug("- WARNING: Could not resolve executable:", dir.path(),
                fileName)
        return '__executable not found__'

    def resolveDocUrl(self, name):
        dirName = self.info[name]['dirname']
        category = self.info[name]['category']
        fileName = self.info[name]['filename']

        if category == 'demos':
            return self.helpRootUrl + 'demos-' + fileName + '.html'
        else:
            return self.helpRootUrl + dirName.replace('/', '-') + '-' + fileName + '.html'

    def resolveImageUrl(self, name):
        return self.helpRootUrl + 'images/' + name

    def getHtml(self, name):
        return self.getResource(self.resolveDocUrl(name))

    def getImage(self, name):
        imageName = self.info[name]['image']
        category = self.info[name]['category']
        fileName = self.info[name]['filename']

        if self.info[name]['qml'] == 'true':
            fileName = 'qml-' + fileName.split('/')[-1]

        if not imageName:
            if category == 'demos':
                imageName = fileName + '-demo.png'
            else:
                imageName = fileName + '-example.png'

            if self.getResource(self.resolveImageUrl(imageName)).isEmpty():
                imageName = fileName + '.png'

            if self.getResource(self.resolveImageUrl(imageName)).isEmpty():
                imageName = fileName + 'example.png'

        return self.getResource(self.resolveImageUrl(imageName))

    def createRootMenu(self, el):
        name = str(el.attribute('name'))
        self.createMenu(el, MenuManager.MENU1)
        self.createInfo(
                MenuContentItem(el, self.window.scene,
                        self.window.mainSceneRoot),
                name + ' -info')

        menuButtonsIn = self.score.insertMovie(name + ' -buttons')
        menuButtonsOut = self.score.insertMovie(name + ' -buttons -out')
        self.createLowLeftButton("Quit", MenuManager.QUIT, menuButtonsIn,
                menuButtonsOut, None)
        self.createLowRightButton("Toggle fullscreen", MenuManager.FULLSCREEN,
                menuButtonsIn, menuButtonsOut, None)

    def createSubMenu(self, el):
        name = str(el.attribute('name'))
        self.createMenu(el, MenuManager.MENU2)
        self.createInfo(
                MenuContentItem(el, self.window.scene,
                        self.window.mainSceneRoot),
                name + ' -info')

    def createLeafMenu(self, el):
        name = str(el.attribute('name'))
        self.createInfo(
                ExampleContent(name, self.window.scene,
                        self.window.mainSceneRoot),
                name)

        infoButtonsIn = self.score.insertMovie(name + ' -buttons')
        infoButtonsOut = self.score.insertMovie(name + ' -buttons -out')
        self.createLowRightLeafButton("Documentation", 600,
                MenuManager.DOCUMENTATION, infoButtonsIn, infoButtonsOut, None)
        if str(el.attribute('executable')) != 'false':
            self.createLowRightLeafButton("Launch", 405, MenuManager.LAUNCH,
                    infoButtonsIn, infoButtonsOut, None)
        elif str(el.attribute('qml')) == 'true':
            self.createLowRightLeafButton("Display", 405,
                    MenuManager.LAUNCH_QML, infoButtonsIn, infoButtonsOut,
                    None)

    def createMenu(self, category, type):
        sw = self.window.scene.sceneRect().width()
        xOffset = 15
        yOffset = 10
        maxExamples = Colors.menuCount
        menuIndex = 1
        name = str(category.attribute('name'))
        currentNode = category.firstChild()
        currentMenu = '%s -menu%d' % (name, menuIndex)

        while not currentNode.isNull():
            movieIn = self.score.insertMovie(currentMenu)
            movieOut = self.score.insertMovie(currentMenu + ' -out')
            movieNextTopOut = self.score.insertMovie(currentMenu + ' -top_out')
            movieNextBottomOut = self.score.insertMovie(currentMenu + ' -bottom_out')
            movieNextTopIn = self.score.insertMovie(currentMenu + ' -top_in')
            movieNextBottomIn = self.score.insertMovie(currentMenu + ' -bottom_in')
            movieShake = self.score.insertMovie(currentMenu + ' -shake')

            i = 0
            while not currentNode.isNull() and i < maxExamples:
                # Create a normal menu button.
                label = str(currentNode.toElement().attribute('name'))
                item = TextButton(label, TextButton.LEFT, type,
                        self.window.scene, self.window.mainSceneRoot)
                currentNode = currentNode.nextSibling()

                # Skip the OpenGL examples if they can't run.
                if str(currentNode.toElement().attribute('dirname')) == 'opengl':
                    if not Colors.openGlAvailable:
                        currentNode = currentNode.nextSibling()

                item.setRecursiveVisible(False)
                item.setZValue(10)
                ih = item.sceneBoundingRect().height()
                iw = item.sceneBoundingRect().width()
                ihp = ih + 3

                # Create in-animation.
                anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN)
                anim.setDuration(float(1000 + (i * 20)) * Colors.animSpeedButtons)
                anim.setStartPos(QtCore.QPointF(xOffset, -ih))
                anim.setPosAt(0.20, QtCore.QPointF(xOffset, -ih))
                anim.setPosAt(0.50, QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY + (10 * float(i / 4.0))))
                anim.setPosAt(0.60, QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                anim.setPosAt(0.70, QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY + (5 * float(i / 4.0))))
                anim.setPosAt(0.80, QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                anim.setPosAt(0.90, QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY + (2 * float(i / 4.0))))
                anim.setPosAt(1.00, QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                movieIn.append(anim)

                # Create out-animation.
                anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT)
                anim.hideOnFinished = True
                anim.setDuration((700 + (30 * i)) * Colors.animSpeedButtons)
                anim.setStartPos(QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                anim.setPosAt(0.60, QtCore.QPointF(xOffset, 600 - ih - ih))
                anim.setPosAt(0.65, QtCore.QPointF(xOffset + 20, 600 - ih))
                anim.setPosAt(1.00, QtCore.QPointF(sw + iw, 600 - ih))
                movieOut.append(anim)

                # Create shake-animation.
                anim = DemoItemAnimation(item)
                anim.setDuration(700 * Colors.animSpeedButtons)
                anim.setStartPos(QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                anim.setPosAt(0.55, QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY - i*2.0))
                anim.setPosAt(0.70, QtCore.QPointF(xOffset - 10, (i * ihp) + yOffset + Colors.contentStartY - i*1.5))
                anim.setPosAt(0.80, QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY - i*1.0))
                anim.setPosAt(0.90, QtCore.QPointF(xOffset - 2, (i * ihp) + yOffset + Colors.contentStartY - i*0.5))
                anim.setPosAt(1.00, QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                movieShake.append(anim)

                # Create next-menu top-out-animation.
                anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT)
                anim.hideOnFinished = True
                anim.setDuration((200 + (30 * i)) * Colors.animSpeedButtons)
                anim.setStartPos(QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                anim.setPosAt(0.70, QtCore.QPointF(xOffset, yOffset + Colors.contentStartY))
                anim.setPosAt(1.00, QtCore.QPointF(-iw, yOffset + Colors.contentStartY))
                movieNextTopOut.append(anim)

                # Create next-menu bottom-out-animation.
                anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT)
                anim.hideOnFinished = True
                anim.setDuration((200 + (30 * i)) * Colors.animSpeedButtons)
                anim.setStartPos(QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                anim.setPosAt(0.70, QtCore.QPointF(xOffset, (maxExamples * ihp) + yOffset + Colors.contentStartY))
                anim.setPosAt(1.00, QtCore.QPointF(-iw, (maxExamples * ihp) + yOffset + Colors.contentStartY))
                movieNextBottomOut.append(anim)

                # Create next-menu top-in-animation.
                anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN)
                anim.setDuration((700 - (30 * i)) * Colors.animSpeedButtons)
                anim.setStartPos(QtCore.QPointF(-iw, yOffset + Colors.contentStartY))
                anim.setPosAt(0.30, QtCore.QPointF(xOffset, yOffset + Colors.contentStartY))
                anim.setPosAt(1.00, QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                movieNextTopIn.append(anim)

                # Create next-menu bottom-in-animation.
                reverse = maxExamples - i
                anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN)
                anim.setDuration((1000 - (30 * reverse)) * Colors.animSpeedButtons)
                anim.setStartPos(QtCore.QPointF(-iw, (maxExamples * ihp) + yOffset + Colors.contentStartY))
                anim.setPosAt(0.30, QtCore.QPointF(xOffset, (maxExamples * ihp) + yOffset + Colors.contentStartY))
                anim.setPosAt(1.00, QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                movieNextBottomIn.append(anim)

                i += 1

            if not currentNode.isNull() and i == maxExamples:
                # We need another menu, so register for 'more' and 'back'
                # buttons.
                menuIndex += 1
                self.info.setdefault(currentMenu, {})['more'] = '%s -menu%d' % (name, menuIndex)
                currentMenu = '%s -menu%d' % (name, menuIndex)
                self.info.setdefault(currentMenu, {})['back'] = '%s -menu%d' % (name, menuIndex - 1)

    def createLowLeftButton(self, label, type, movieIn, movieOut, movieShake, menuString=""):
        button = TextButton(label, TextButton.RIGHT, type, self.window.scene,
                self.window.mainSceneRoot, TextButton.PANEL)
        if menuString:
            button.setMenuString(menuString)
        button.setRecursiveVisible(False)
        button.setZValue(10)

        iw = button.sceneBoundingRect().width()
        xOffset = 15

        # Create in-animation.
        buttonIn = DemoItemAnimation(button, DemoItemAnimation.ANIM_IN)
        buttonIn.setDuration(1800 * Colors.animSpeedButtons)
        buttonIn.setStartPos(QtCore.QPointF(-iw, Colors.contentStartY + Colors.contentHeight - 35))
        buttonIn.setPosAt(0.5, QtCore.QPointF(-iw, Colors.contentStartY + Colors.contentHeight - 35))
        buttonIn.setPosAt(0.7, QtCore.QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 35))
        buttonIn.setPosAt(1.0, QtCore.QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 26))
        movieIn.append(buttonIn)

        # Create out-animation.
        buttonOut = DemoItemAnimation(button, DemoItemAnimation.ANIM_OUT)
        buttonOut.hideOnFinished = True
        buttonOut.setDuration(400 * Colors.animSpeedButtons)
        buttonOut.setStartPos(QtCore.QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 26))
        buttonOut.setPosAt(1.0, QtCore.QPointF(-iw, Colors.contentStartY + Colors.contentHeight - 26))
        movieOut.append(buttonOut)

        if movieShake is not None:
            shakeAnim = DemoItemAnimation(button, DemoItemAnimation.ANIM_UNSPECIFIED)
            shakeAnim.timeline.setCurveShape(QtCore.QTimeLine.LinearCurve)
            shakeAnim.setDuration(650)
            shakeAnim.setStartPos(buttonIn.posAt(1.0))
            shakeAnim.setPosAt(0.60, buttonIn.posAt(1.0))
            shakeAnim.setPosAt(0.70, buttonIn.posAt(1.0) + QtCore.QPointF(-3, 0))
            shakeAnim.setPosAt(0.80, buttonIn.posAt(1.0) + QtCore.QPointF(2, 0))
            shakeAnim.setPosAt(0.90, buttonIn.posAt(1.0) + QtCore.QPointF(-1, 0))
            shakeAnim.setPosAt(1.00, buttonIn.posAt(1.0))
            movieShake.append(shakeAnim)

    def createLowRightButton(self, label, type, movieIn, movieOut, movieShake):
        item = TextButton(label, TextButton.RIGHT, type, self.window.scene,
                self.window.mainSceneRoot, TextButton.PANEL)
        item.setRecursiveVisible(False)
        item.setZValue(10)

        sw = self.window.scene.sceneRect().width()
        xOffset = 70

        # Create in-animation.
        anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN)
        anim.setDuration(1800 * Colors.animSpeedButtons)
        anim.setStartPos(QtCore.QPointF(sw, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setPosAt(0.5, QtCore.QPointF(sw, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setPosAt(0.7, QtCore.QPointF(xOffset + 535, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setPosAt(1.0, QtCore.QPointF(xOffset + 535, Colors.contentStartY + Colors.contentHeight - 26))
        movieIn.append(anim)

        # Create out-animation.
        anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT)
        anim.hideOnFinished = True
        anim.setDuration(400 * Colors.animSpeedButtons)
        anim.setStartPos(QtCore.QPointF(xOffset + 535, Colors.contentStartY + Colors.contentHeight - 26))
        anim.setPosAt(1.0, QtCore.QPointF(sw, Colors.contentStartY + Colors.contentHeight - 26))
        movieOut.append(anim)

    def createLowRightLeafButton(self, label, xOffset, type, movieIn, movieOut, movieShake):
        item = TextButton(label, TextButton.RIGHT, type, self.window.scene,
                self.window.mainSceneRoot, TextButton.PANEL)
        item.setRecursiveVisible(False)
        item.setZValue(10)

        sw = self.window.scene.sceneRect().width()
        sh = self.window.scene.sceneRect().height()

        # Create in-animation.
        anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN)
        anim.setDuration(1050 * Colors.animSpeedButtons)
        anim.setStartPos(QtCore.QPointF(sw, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setPosAt(0.10, QtCore.QPointF(sw, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setPosAt(0.30, QtCore.QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setPosAt(0.35, QtCore.QPointF(xOffset + 30, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setPosAt(0.40, QtCore.QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setPosAt(0.45, QtCore.QPointF(xOffset + 5, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setPosAt(0.50, QtCore.QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setPosAt(1.00, QtCore.QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 26))
        movieIn.append(anim)

        # Create out-animation.
        anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT)
        anim.hideOnFinished = True
        anim.setDuration(300 * Colors.animSpeedButtons)
        anim.setStartPos(QtCore.QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 26))
        anim.setPosAt(1.0, QtCore.QPointF(xOffset, sh))
        movieOut.append(anim)

    def createInfo(self, item, name):
        movie_in = self.score.insertMovie(name)
        movie_out = self.score.insertMovie(name + ' -out')
        item.setZValue(8)
        item.setRecursiveVisible(False)

        xOffset = 230.0
        infoIn = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN)
        infoIn.timeline.setCurveShape(QtCore.QTimeLine.LinearCurve)
        infoIn.setDuration(650)
        infoIn.setStartPos(QtCore.QPointF(self.window.scene.sceneRect().width(), Colors.contentStartY))
        infoIn.setPosAt(0.60, QtCore.QPointF(xOffset, Colors.contentStartY))
        infoIn.setPosAt(0.70, QtCore.QPointF(xOffset + 20, Colors.contentStartY))
        infoIn.setPosAt(0.80, QtCore.QPointF(xOffset, Colors.contentStartY))
        infoIn.setPosAt(0.90, QtCore.QPointF(xOffset + 7, Colors.contentStartY))
        infoIn.setPosAt(1.00, QtCore.QPointF(xOffset, Colors.contentStartY))
        movie_in.append(infoIn)

        infoOut = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT)
        infoOut.timeline.setCurveShape(QtCore.QTimeLine.EaseInCurve)
        infoOut.setDuration(300)
        infoOut.hideOnFinished = True
        infoOut.setStartPos(QtCore.QPointF(xOffset, Colors.contentStartY))
        infoOut.setPosAt(1.0, QtCore.QPointF(-600, Colors.contentStartY))
        movie_out.append(infoOut)

    def createTicker(self):
        if Colors.noTicker:
            return

        movie_in = self.score.insertMovie('ticker')
        movie_out = self.score.insertMovie('ticker -out')
        movie_activate = self.score.insertMovie('ticker -activate')
        movie_deactivate = self.score.insertMovie('ticker -deactivate')

        self.ticker = ItemCircleAnimation(self.window.scene)
        self.ticker.setZValue(50)
        self.ticker.hide()

        # Move ticker in.
        qtendpos = 485
        qtPosY = 120
        self.tickerInAnim = DemoItemAnimation(self.ticker,
                DemoItemAnimation.ANIM_IN)
        self.tickerInAnim.setDuration(500)
        self.tickerInAnim.setStartPos(QtCore.QPointF(self.window.scene.sceneRect().width(), Colors.contentStartY + qtPosY))
        self.tickerInAnim.setPosAt(0.60, QtCore.QPointF(qtendpos, Colors.contentStartY + qtPosY))
        self.tickerInAnim.setPosAt(0.70, QtCore.QPointF(qtendpos + 30, Colors.contentStartY + qtPosY))
        self.tickerInAnim.setPosAt(0.80, QtCore.QPointF(qtendpos, Colors.contentStartY + qtPosY))
        self.tickerInAnim.setPosAt(0.90, QtCore.QPointF(qtendpos + 5, Colors.contentStartY + qtPosY))
        self.tickerInAnim.setPosAt(1.00, QtCore.QPointF(qtendpos, Colors.contentStartY + qtPosY))
        movie_in.append(self.tickerInAnim)

        # Move ticker out.
        qtOut = DemoItemAnimation(self.ticker, DemoItemAnimation.ANIM_OUT)
        qtOut.hideOnFinished = True
        qtOut.setDuration(500)
        qtOut.setStartPos(QtCore.QPointF(qtendpos, Colors.contentStartY + qtPosY))
        qtOut.setPosAt(1.00, QtCore.QPointF(self.window.scene.sceneRect().width() + 700, Colors.contentStartY + qtPosY))
        movie_out.append(qtOut)

        # Move ticker in on activate.
        qtActivate = DemoItemAnimation(self.ticker)
        qtActivate.setDuration(400)
        qtActivate.setStartPos(QtCore.QPointF(self.window.scene.sceneRect().width(), Colors.contentStartY + qtPosY))
        qtActivate.setPosAt(0.60, QtCore.QPointF(qtendpos, Colors.contentStartY + qtPosY))
        qtActivate.setPosAt(0.70, QtCore.QPointF(qtendpos + 30, Colors.contentStartY + qtPosY))
        qtActivate.setPosAt(0.80, QtCore.QPointF(qtendpos, Colors.contentStartY + qtPosY))
        qtActivate.setPosAt(0.90, QtCore.QPointF(qtendpos + 5, Colors.contentStartY + qtPosY))
        qtActivate.setPosAt(1.00, QtCore.QPointF(qtendpos, Colors.contentStartY + qtPosY))
        movie_activate.append(qtActivate)

        # Move ticker out on deactivate.
        qtDeactivate = DemoItemAnimation(self.ticker)
        qtDeactivate.hideOnFinished = True
        qtDeactivate.setDuration(400)
        qtDeactivate.setStartPos(QtCore.QPointF(qtendpos, Colors.contentStartY + qtPosY))
        qtDeactivate.setPosAt(1.00, QtCore.QPointF(qtendpos, 800))
        movie_deactivate.append(qtDeactivate)

    def createUpnDownButtons(self):
        xOffset = 15.0
        yOffset = 450.0

        self.upButton = TextButton("", TextButton.LEFT, MenuManager.UP,
                self.window.scene, self.window.mainSceneRoot, TextButton.UP)
        self.upButton.prepare()
        self.upButton.setPos(xOffset, yOffset)
        self.upButton.setState(TextButton.DISABLED)

        self.downButton = TextButton("", TextButton.LEFT, MenuManager.DOWN,
                self.window.scene, self.window.mainSceneRoot, TextButton.DOWN)
        self.downButton.prepare()
        self.downButton.setPos(xOffset + 10 + self.downButton.sceneBoundingRect().width(), yOffset)

        movieShake = self.score.insertMovie('upndown -shake')

        shakeAnim = DemoItemAnimation(self.upButton,
                DemoItemAnimation.ANIM_UNSPECIFIED)
        shakeAnim.timeline.setCurveShape(QtCore.QTimeLine.LinearCurve)
        shakeAnim.setDuration(650)
        shakeAnim.setStartPos(self.upButton.pos())
        shakeAnim.setPosAt(0.60, self.upButton.pos())
        shakeAnim.setPosAt(0.70, self.upButton.pos() + QtCore.QPointF(-2, 0))
        shakeAnim.setPosAt(0.80, self.upButton.pos() + QtCore.QPointF(1, 0))
        shakeAnim.setPosAt(0.90, self.upButton.pos() + QtCore.QPointF(-1, 0))
        shakeAnim.setPosAt(1.00, self.upButton.pos())
        movieShake.append(shakeAnim)

        shakeAnim = DemoItemAnimation(self.downButton,
                DemoItemAnimation.ANIM_UNSPECIFIED)
        shakeAnim.timeline.setCurveShape(QtCore.QTimeLine.LinearCurve)
        shakeAnim.setDuration(650)
        shakeAnim.setStartPos(self.downButton.pos())
        shakeAnim.setPosAt(0.60, self.downButton.pos())
        shakeAnim.setPosAt(0.70, self.downButton.pos() + QtCore.QPointF(-5, 0))
        shakeAnim.setPosAt(0.80, self.downButton.pos() + QtCore.QPointF(-3, 0))
        shakeAnim.setPosAt(0.90, self.downButton.pos() + QtCore.QPointF(-1, 0))
        shakeAnim.setPosAt(1.00, self.downButton.pos())
        movieShake.append(shakeAnim)

    def createBackButton(self):
        backIn = self.score.insertMovie('back -in')
        backOut = self.score.insertMovie('back -out')
        backShake = self.score.insertMovie('back -shake')
        self.createLowLeftButton("Back", MenuManager.ROOT, backIn, backOut,
                backShake, Colors.rootMenuName)
class MenuManager(QtCore.QObject):
    ROOT, MENU1, MENU2, LAUNCH, DOCUMENTATION, QUIT, FULLSCREEN, UP, DOWN, \
            BACK = range(10)

    pInstance = None

    def __init__(self):
        super(MenuManager, self).__init__()

        self.contentsDoc = None
        self.assistantProcess = QtCore.QProcess()
        self.helpRootUrl = ''
        self.docDir = QtCore.QDir()
        self.imgDir = QtCore.QDir()

        self.info = {}
        self.window = None

        self.ticker = None
        self.tickerInAnim = None
        self.upButton = None
        self.downButton = None
        self.helpEngine = None
        self.score = Score()
        self.currentMenu = "[no menu visible]"
        self.currentCategory = "[no category visible]"
        self.currentMenuButtons = "[no menu buttons visible]"
        self.currentInfo = "[no info visible]"
        self.currentMenuCode = -1
        self.readXmlDocument()
        self.initHelpEngine()

    @classmethod
    def instance(cls):
        if cls.pInstance is None:
            cls.pInstance = cls()

        return cls.pInstance

    def getResource(self, name):
        ba = self.helpEngine.fileData(QtCore.QUrl(name))

        if ba.isEmpty():
            Colors.debug(" - WARNING: Could not get", name)

        return ba

    def readXmlDocument(self):
        self.contentsDoc = QtXml.QDomDocument()

        xml_file = QtCore.QFile(':/xml/examples.xml')
        statusOK, errorStr, errorLine, errorColumn = \
                self.contentsDoc.setContent(xml_file, True)

        if not statusOK:
            QtGui.QMessageBox.critical(None, "DOM Parser",
                    "Could not read or find the contents document. Error at "
                    "line %d, column %d:\n%s" % (errorLine, errorColumn, errorStr))
            sys.exit(-1)

    def initHelpEngine(self):
        self.helpRootUrl = 'qthelp://com.trolltech.qt.%d%d%d/qdoc/' % QtCore.__version_info__

        # Store help collection file in cache dir of assistant.
        cacheDir = QtGui.QDesktopServices.storageLocation(QtGui.QDesktopServices.DataLocation) + '/Trolltech/Assistant/'
        helpDataFile = 'qtdemo_%s.qhc' % QtCore.__version__

        dir = QtCore.QDir()
        if not dir.exists(cacheDir):
            dir.mkpath(cacheDir)

        # Create help engine (and new helpDataFile if it does not exist).
        self.helpEngine = QtHelp.QHelpEngineCore(cacheDir + helpDataFile)
        self.helpEngine.setupData()

        qtDocRoot = QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.DocumentationPath) + '/qch'
        qtDocRoot = QtCore.QDir(qtDocRoot).absolutePath()

        qchFiles = ['/qt.qch', '/designer.qch', '/linguist.qch']

        oldDir = self.helpEngine.customValue('docDir', '')
        if oldDir != qtDocRoot:
            for qchFile in qchFiles:
                self.helpEngine.unregisterDocumentation(QtHelp.QHelpEngineCore.namespaceName(qtDocRoot + qchFile))

        # If the data that the engine will work on is not yet registered, do it
        # now.
        for qchFile in qchFiles:
            self.helpEngine.registerDocumentation(qtDocRoot + qchFile)

        self.helpEngine.setCustomValue('docDir', qtDocRoot)

    def itemSelected(self, userCode, menuName):
        if userCode == MenuManager.LAUNCH:
            self.launchExample(self.currentInfo)
        elif userCode == MenuManager.DOCUMENTATION:
            self.showDocInAssistant(self.currentInfo)
        elif userCode == MenuManager.QUIT:
            self.window.loop = False
            QtCore.QCoreApplication.quit()
        elif userCode == MenuManager.FULLSCREEN:
            self.window.toggleFullscreen()
        elif userCode == MenuManager.ROOT:
            # Out.
            self.score.queueMovie(self.currentMenu + ' -out', Score.FROM_START,
                    Score.LOCK_ITEMS)
            self.score.queueMovie(self.currentMenuButtons + ' -out',
                    Score.FROM_START, Score.LOCK_ITEMS)
            self.score.queueMovie(self.currentInfo + ' -out')
            self.score.queueMovie(self.currentInfo + ' -buttons -out',
                    Score.NEW_ANIMATION_ONLY)
            self.score.queueMovie('back -out', Score.ONLY_IF_VISIBLE)

            # Book-keeping.
            self.currentMenuCode = MenuManager.ROOT
            self.currentMenu = menuName + ' -menu1'
            self.currentMenuButtons = menuName + ' -buttons'
            self.currentInfo = menuName + ' -info'

            # In.
            self.score.queueMovie('upndown -shake')
            self.score.queueMovie(self.currentMenu, Score.FROM_START,
                    Score.UNLOCK_ITEMS)
            self.score.queueMovie(self.currentMenuButtons, Score.FROM_START,
                    Score.UNLOCK_ITEMS)
            self.score.queueMovie(self.currentInfo)

            if not Colors.noTicker:
                self.ticker.doIntroTransitions = True
                self.tickerInAnim.startDelay = 2000
                self.ticker.useGuideQt()
                self.score.queueMovie('ticker', Score.NEW_ANIMATION_ONLY)
        elif userCode == MenuManager.MENU1:
            # Out.
            self.score.queueMovie(self.currentMenu + ' -out', Score.FROM_START,
                    Score.LOCK_ITEMS)
            self.score.queueMovie(self.currentMenuButtons + ' -out',
                    Score.FROM_START, Score.LOCK_ITEMS)
            self.score.queueMovie(self.currentInfo + ' -out')

            # Book-keeping.
            self.currentMenuCode = MenuManager.MENU1
            self.currentCategory = menuName
            self.currentMenu = menuName + ' -menu1'
            self.currentInfo = menuName + ' -info'

            # In.
            self.score.queueMovie('upndown -shake')
            self.score.queueMovie('back -in')
            self.score.queueMovie(self.currentMenu, Score.FROM_START,
                    Score.UNLOCK_ITEMS)
            self.score.queueMovie(self.currentInfo)

            if not Colors.noTicker:
                self.ticker.useGuideTt()
        elif userCode == MenuManager.MENU2:
            # Out.
            self.score.queueMovie(self.currentInfo + ' -out',
                    Score.NEW_ANIMATION_ONLY)
            self.score.queueMovie(self.currentInfo + ' -buttons -out',
                    Score.NEW_ANIMATION_ONLY)

            # Book-keeping.
            self.currentMenuCode = MenuManager.MENU2
            self.currentInfo = menuName

            # In/shake.
            self.score.queueMovie('upndown -shake')
            self.score.queueMovie('back -shake')
            self.score.queueMovie(self.currentMenu + ' -shake')
            self.score.queueMovie(self.currentInfo, Score.NEW_ANIMATION_ONLY)
            self.score.queueMovie(self.currentInfo + ' -buttons',
                    Score.NEW_ANIMATION_ONLY)

            if not Colors.noTicker:
                self.score.queueMovie('ticker -out', Score.NEW_ANIMATION_ONLY)
        elif userCode == MenuManager.UP:
            backMenu = self.info[self.currentMenu]['back']
            if backMenu:
                self.score.queueMovie(self.currentMenu + ' -top_out',
                        Score.FROM_START, Score.LOCK_ITEMS)
                self.score.queueMovie(backMenu + ' -bottom_in',
                        Score.FROM_START, Score.UNLOCK_ITEMS)
                self.currentMenu = backMenu
        elif userCode == MenuManager.DOWN:
            moreMenu = self.info[self.currentMenu]['more']
            if moreMenu:
                self.score.queueMovie(self.currentMenu + ' -bottom_out',
                        Score.FROM_START, Score.LOCK_ITEMS)
                self.score.queueMovie(moreMenu + ' -top_in', Score.FROM_START,
                        Score.UNLOCK_ITEMS)
                self.currentMenu = moreMenu
        elif userCode == MenuManager.BACK:
            if self.currentMenuCode == MenuManager.MENU2:
                # Out.
                self.score.queueMovie(self.currentInfo + ' -out',
                        Score.NEW_ANIMATION_ONLY)
                self.score.queueMovie(self.currentInfo + ' -buttons -out',
                        Score.NEW_ANIMATION_ONLY)

                # Book-keeping.
                self.currentMenuCode = MenuManager.MENU1
                self.currentMenuButtons = self.currentCategory + ' -buttons'
                self.currentInfo = self.currentCategory + ' -info'

                # In/shake.
                self.score.queueMovie('upndown -shake')
                self.score.queueMovie(self.currentMenu + ' -shake')
                self.score.queueMovie(self.currentInfo,
                        Score.NEW_ANIMATION_ONLY)
                self.score.queueMovie(self.currentInfo + ' -buttons',
                        Score.NEW_ANIMATION_ONLY)

                if not Colors.noTicker:
                    self.ticker.doIntroTransitions = False
                    self.tickerInAnim.startDelay = 500
                    self.score.queueMovie('ticker', Score.NEW_ANIMATION_ONLY)
            elif self.currentMenuCode != MenuManager.ROOT:
                self.itemSelected(MenuManager.ROOT, Colors.rootMenuName)

        # Update back and more buttons.
        if self.info.setdefault(self.currentMenu, {}).get('back'):
            back_state = TextButton.OFF
        else:
            back_state = TextButton.DISABLED

        if self.info[self.currentMenu].get('more'):
            more_state = TextButton.OFF
        else:
            more_state = TextButton.DISABLED

        self.upButton.setState(back_state)
        self.downButton.setState(more_state)

        if self.score.hasQueuedMovies():
            self.score.playQue()
            # Playing new movies might include loading etc., so ignore the FPS
            # at this point.
            self.window.fpsHistory = []

    def showDocInAssistant(self, name):
        url = self.resolveDocUrl(name)
        Colors.debug("Sending URL to Assistant:", url)

        # Start assistant if it's not already running.
        if self.assistantProcess.state() != QtCore.QProcess.Running:
            app = QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.BinariesPath) + QtCore.QDir.separator()

            if sys.platform == 'darwin':
                app += 'Assistant.app/Contents/MacOS/Assistant'
            else:
                app += 'assistant'

            args = ['-enableRemoteControl']
            self.assistantProcess.start(app, args)
            if not self.assistantProcess.waitForStarted():
                QtGui.QMessageBox.critical(None, "PyQt Demo",
                        "Could not start %s." % app)
                return

        # Send command through remote control even if the process was just
        # started to activate assistant and bring it to the front.
        cmd_str = QtCore.QTextStream(self.assistantProcess)
        cmd_str << 'SetSource ' << url << '\n'

    def launchExample(self, name):
        executable = self.resolveExeFile(name)

        process = QtCore.QProcess(self)
        process.finished.connect(self.exampleFinished)
        process.error.connect(self.exampleError)

        if sys.platform == 'win32':
            # Make sure it finds the DLLs on Windows.
            curpath = os.getenv('PATH')
            newpath = 'PATH=%s;%s' % (QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.BinariesPath), curpath)
            process.setEnvironment([newpath])

        if self.info[name]['changedirectory'] != 'false':
            workingDirectory = self.resolveDataDir(name)
            process.setWorkingDirectory(workingDirectory)
            Colors.debug("Setting working directory:", workingDirectory)

        Colors.debug("Launching:", executable)
        process.start(sys.executable, [executable])

    def exampleFinished(self):
        pass

    def exampleError(self, error):
        if error != QtCore.QProcess.Crashed:
            QtGui.QMessageBox.critical(None, "Failed to launch the example",
                    "Could not launch the example. Ensure that it has been "
                    "built.",
                    QtGui.QMessageBox.Cancel)

    def init(self, window):
        self.window = window

        # Create div.
        self.createTicker()
        self.createUpnDownButtons()
        self.createBackButton()

        # Create first level menu.
        rootElement = self.contentsDoc.documentElement()
        self.createRootMenu(rootElement)

        # Create second level menus.
        level2MenuNode = rootElement.firstChild()
        while not level2MenuNode.isNull():
            level2MenuElement = level2MenuNode.toElement()
            self.createSubMenu(level2MenuElement)

            # Create leaf menu and example info.
            exampleNode = level2MenuElement.firstChild()
            while not exampleNode.isNull():
                exampleElement = exampleNode.toElement()
                self.readInfoAboutExample(exampleElement)
                self.createLeafMenu(exampleElement)
                exampleNode = exampleNode.nextSibling()

            level2MenuNode = level2MenuNode.nextSibling()

    def readInfoAboutExample(self, example):
        name = str(example.attribute('name'))
        if name in self.info:
            Colors.debug("__WARNING: MenuManager.readInfoAboutExample: "
                         "Demo/example with name", name, "appears twice in "
                         "the xml-file!__")

        self.info.setdefault(name, {})['filename'] = str(example.attribute('filename'))
        self.info[name]['category'] = str(example.parentNode().toElement().tagName())
        self.info[name]['dirname'] = str(example.parentNode().toElement().attribute('dirname'))
        self.info[name]['changedirectory'] = str(example.attribute('changedirectory'))
        self.info[name]['image'] = str(example.attribute('image'))

    def resolveDir(self, name):
        dirName = self.info[name]['dirname']
        category = self.info[name]['category']
        fileName = self.info[name]['filename']

        dir = QtCore.QFileInfo(__file__).dir()
        dir.cdUp()

        if category != 'demos':
            dir.cdUp()

        dir.cd(dirName)

        # This may legitimately fail if the example is just a simple .py file.
        dir.cd(fileName)

        return dir

    def resolveDataDir(self, name):
        return self.resolveDir(name).absolutePath()

    def resolveExeFile(self, name):
        dir = self.resolveDir(name)

        fileName = self.info[name]['filename']

        pyFile = QtCore.QFile(dir.path() + '/' + fileName + '.py')
        if pyFile.exists():
            return pyFile.fileName()

        pywFile = QtCore.QFile(dir.path() + '/' + fileName + '.pyw')
        if pywFile.exists():
            return pywFile.fileName()

        Colors.debug("- WARNING: Could not resolve executable:", dir.path(),
                fileName)
        return '__executable not found__'

    def resolveDocUrl(self, name):
        dirName = self.info[name]['dirname']
        category = self.info[name]['category']
        fileName = self.info[name]['filename']

        if category == 'demos':
            return self.helpRootUrl + 'demos-' + fileName + '.html'
        else:
            return self.helpRootUrl + dirName.replace('/', '-') + '-' + fileName + '.html'

    def resolveImageUrl(self, name):
        return self.helpRootUrl + 'images/' + name

    def getHtml(self, name):
        return self.getResource(self.resolveDocUrl(name))

    def getImage(self, name):
        imageName = self.info[name]['image']
        category = self.info[name]['category']
        fileName = self.info[name]['filename']

        if not imageName:
            if category == 'demos':
                imageName = fileName + '-demo.png'
            else:
                imageName = fileName + '-example.png'

            if self.getResource(self.resolveImageUrl(imageName)).isEmpty():
                imageName = fileName + '.png'

            if self.getResource(self.resolveImageUrl(imageName)).isEmpty():
                imageName = fileName + 'example.png'

        return self.getResource(self.resolveImageUrl(imageName))

    def createRootMenu(self, el):
        name = str(el.attribute('name'))
        self.createMenu(el, MenuManager.MENU1)
        self.createInfo(MenuContentItem(el, self.window.scene), name + ' -info')

        menuButtonsIn = self.score.insertMovie(name + ' -buttons')
        menuButtonsOut = self.score.insertMovie(name + ' -buttons -out')
        self.createLowLeftButton("Quit", MenuManager.QUIT, menuButtonsIn,
                menuButtonsOut, None)
        self.createLowRightButton("Toggle fullscreen", MenuManager.FULLSCREEN,
                menuButtonsIn, menuButtonsOut, None)

    def createSubMenu(self, el):
        name = str(el.attribute('name'))
        self.createMenu(el, MenuManager.MENU2)
        self.createInfo(MenuContentItem(el, self.window.scene), name + ' -info')

    def createLeafMenu(self, el):
        name = str(el.attribute('name'))
        self.createInfo(ExampleContent(name, self.window.scene), name)

        infoButtonsIn = self.score.insertMovie(name + ' -buttons')
        infoButtonsOut = self.score.insertMovie(name + ' -buttons -out')
        self.createLowRightLeafButton("Documentation", 600,
                MenuManager.DOCUMENTATION, infoButtonsIn, infoButtonsOut, None)
        if str(el.attribute('executable')) != 'false':
            self.createLowRightLeafButton("Launch", 405, MenuManager.LAUNCH,
                    infoButtonsIn, infoButtonsOut, None)

    def createMenu(self, category, type):
        sw = self.window.scene.sceneRect().width()
        xOffset = 15
        yOffset = 10
        maxExamples = Colors.menuCount
        menuIndex = 1
        name = str(category.attribute('name'))
        currentNode = category.firstChild()
        currentMenu = '%s -menu%d' % (name, menuIndex)

        while not currentNode.isNull():
            movieIn = self.score.insertMovie(currentMenu)
            movieOut = self.score.insertMovie(currentMenu + ' -out')
            movieNextTopOut = self.score.insertMovie(currentMenu + ' -top_out')
            movieNextBottomOut = self.score.insertMovie(currentMenu + ' -bottom_out')
            movieNextTopIn = self.score.insertMovie(currentMenu + ' -top_in')
            movieNextBottomIn = self.score.insertMovie(currentMenu + ' -bottom_in')
            movieShake = self.score.insertMovie(currentMenu + ' -shake')

            i = 0
            while not currentNode.isNull() and i < maxExamples:
                # Create a normal menu button.
                label = str(currentNode.toElement().attribute('name'))
                item = TextButton(label, TextButton.LEFT, type,
                        self.window.scene)
                currentNode = currentNode.nextSibling()

                # Skip the OpenGL examples if they can't run.
                if str(currentNode.toElement().attribute('dirname')) == 'opengl':
                    if not Colors.openGlAvailable:
                        currentNode = currentNode.nextSibling()

                item.setRecursiveVisible(False)
                item.setZValue(10)
                ih = item.sceneBoundingRect().height()
                iw = item.sceneBoundingRect().width()
                ihp = ih + 3

                # Create in-animation.
                anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN)
                anim.setDuration(float(1000 + (i * 20)) * Colors.animSpeedButtons)
                anim.setStartPos(QtCore.QPointF(xOffset, -ih))
                anim.setPosAt(0.20, QtCore.QPointF(xOffset, -ih))
                anim.setPosAt(0.50, QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY + (10 * float(i / 4.0))))
                anim.setPosAt(0.60, QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                anim.setPosAt(0.70, QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY + (5 * float(i / 4.0))))
                anim.setPosAt(0.80, QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                anim.setPosAt(0.90, QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY + (2 * float(i / 4.0))))
                anim.setPosAt(1.00, QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                movieIn.append(anim)

                # Create out-animation.
                anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT)
                anim.hideOnFinished = True
                anim.setDuration((700 + (30 * i)) * Colors.animSpeedButtons)
                anim.setStartPos(QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                anim.setPosAt(0.60, QtCore.QPointF(xOffset, 600 - ih - ih))
                anim.setPosAt(0.65, QtCore.QPointF(xOffset + 20, 600 - ih))
                anim.setPosAt(1.00, QtCore.QPointF(sw + iw, 600 - ih))
                movieOut.append(anim)

                # Create shake-animation.
                anim = DemoItemAnimation(item)
                anim.setDuration(700 * Colors.animSpeedButtons)
                anim.setStartPos(QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                anim.setPosAt(0.55, QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY - i*2.0))
                anim.setPosAt(0.70, QtCore.QPointF(xOffset - 10, (i * ihp) + yOffset + Colors.contentStartY - i*1.5))
                anim.setPosAt(0.80, QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY - i*1.0))
                anim.setPosAt(0.90, QtCore.QPointF(xOffset - 2, (i * ihp) + yOffset + Colors.contentStartY - i*0.5))
                anim.setPosAt(1.00, QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                movieShake.append(anim)

                # Create next-menu top-out-animation.
                anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT)
                anim.hideOnFinished = True
                anim.setDuration((200 + (30 * i)) * Colors.animSpeedButtons)
                anim.setStartPos(QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                anim.setPosAt(0.70, QtCore.QPointF(xOffset, yOffset + Colors.contentStartY))
                anim.setPosAt(1.00, QtCore.QPointF(-iw, yOffset + Colors.contentStartY))
                movieNextTopOut.append(anim)

                # Create next-menu bottom-out-animation.
                anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT)
                anim.hideOnFinished = True
                anim.setDuration((200 + (30 * i)) * Colors.animSpeedButtons)
                anim.setStartPos(QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                anim.setPosAt(0.70, QtCore.QPointF(xOffset, (maxExamples * ihp) + yOffset + Colors.contentStartY))
                anim.setPosAt(1.00, QtCore.QPointF(-iw, (maxExamples * ihp) + yOffset + Colors.contentStartY))
                movieNextBottomOut.append(anim)

                # Create next-menu top-in-animation.
                anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN)
                anim.setDuration((700 - (30 * i)) * Colors.animSpeedButtons)
                anim.setStartPos(QtCore.QPointF(-iw, yOffset + Colors.contentStartY))
                anim.setPosAt(0.30, QtCore.QPointF(xOffset, yOffset + Colors.contentStartY))
                anim.setPosAt(1.00, QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                movieNextTopIn.append(anim)

                # Create next-menu bottom-in-animation.
                reverse = maxExamples - i
                anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN)
                anim.setDuration((1000 - (30 * reverse)) * Colors.animSpeedButtons)
                anim.setStartPos(QtCore.QPointF(-iw, (maxExamples * ihp) + yOffset + Colors.contentStartY))
                anim.setPosAt(0.30, QtCore.QPointF(xOffset, (maxExamples * ihp) + yOffset + Colors.contentStartY))
                anim.setPosAt(1.00, QtCore.QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                movieNextBottomIn.append(anim)

                i += 1

            if not currentNode.isNull() and i == maxExamples:
                # We need another menu, so register for 'more' and 'back'
                # buttons.
                menuIndex += 1
                self.info.setdefault(currentMenu, {})['more'] = '%s -menu%d' % (name, menuIndex)
                currentMenu = '%s -menu%d' % (name, menuIndex)
                self.info.setdefault(currentMenu, {})['back'] = '%s -menu%d' % (name, menuIndex - 1)

    def createLowLeftButton(self, label, type, movieIn, movieOut, movieShake, menuString=""):
        button = TextButton(label, TextButton.RIGHT, type, self.window.scene,
                None, TextButton.PANEL)
        if menuString:
            button.setMenuString(menuString)
        button.setRecursiveVisible(False)
        button.setZValue(10)

        iw = button.sceneBoundingRect().width()
        xOffset = 15

        # Create in-animation.
        buttonIn = DemoItemAnimation(button, DemoItemAnimation.ANIM_IN)
        buttonIn.setDuration(1800 * Colors.animSpeedButtons)
        buttonIn.setStartPos(QtCore.QPointF(-iw, Colors.contentStartY + Colors.contentHeight - 35))
        buttonIn.setPosAt(0.5, QtCore.QPointF(-iw, Colors.contentStartY + Colors.contentHeight - 35))
        buttonIn.setPosAt(0.7, QtCore.QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 35))
        buttonIn.setPosAt(1.0, QtCore.QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 26))
        movieIn.append(buttonIn)

        # Create out-animation.
        buttonOut = DemoItemAnimation(button, DemoItemAnimation.ANIM_OUT)
        buttonOut.hideOnFinished = True
        buttonOut.setDuration(400 * Colors.animSpeedButtons)
        buttonOut.setStartPos(QtCore.QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 26))
        buttonOut.setPosAt(1.0, QtCore.QPointF(-iw, Colors.contentStartY + Colors.contentHeight - 26))
        movieOut.append(buttonOut)

        if movieShake is not None:
            shakeAnim = DemoItemAnimation(button, DemoItemAnimation.ANIM_UNSPECIFIED)
            shakeAnim.timeline.setCurveShape(QtCore.QTimeLine.LinearCurve)
            shakeAnim.setDuration(650)
            shakeAnim.setStartPos(buttonIn.posAt(1.0))
            shakeAnim.setPosAt(0.60, buttonIn.posAt(1.0))
            shakeAnim.setPosAt(0.70, buttonIn.posAt(1.0) + QtCore.QPointF(-3, 0))
            shakeAnim.setPosAt(0.80, buttonIn.posAt(1.0) + QtCore.QPointF(2, 0))
            shakeAnim.setPosAt(0.90, buttonIn.posAt(1.0) + QtCore.QPointF(-1, 0))
            shakeAnim.setPosAt(1.00, buttonIn.posAt(1.0))
            movieShake.append(shakeAnim)

    def createLowRightButton(self, label, type, movieIn, movieOut, movieShake):
        item = TextButton(label, TextButton.RIGHT, type, self.window.scene,
                None, TextButton.PANEL)
        item.setRecursiveVisible(False)
        item.setZValue(10)

        sw = self.window.scene.sceneRect().width()
        xOffset = 70

        # Create in-animation.
        anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN)
        anim.setDuration(1800 * Colors.animSpeedButtons)
        anim.setStartPos(QtCore.QPointF(sw, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setPosAt(0.5, QtCore.QPointF(sw, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setPosAt(0.7, QtCore.QPointF(xOffset + 535, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setPosAt(1.0, QtCore.QPointF(xOffset + 535, Colors.contentStartY + Colors.contentHeight - 26))
        movieIn.append(anim)

        # Create out-animation.
        anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT)
        anim.hideOnFinished = True
        anim.setDuration(400 * Colors.animSpeedButtons)
        anim.setStartPos(QtCore.QPointF(xOffset + 535, Colors.contentStartY + Colors.contentHeight - 26))
        anim.setPosAt(1.0, QtCore.QPointF(sw, Colors.contentStartY + Colors.contentHeight - 26))
        movieOut.append(anim)

    def createLowRightLeafButton(self, label, xOffset, type, movieIn, movieOut, movieShake):
        item = TextButton(label, TextButton.RIGHT, type, self.window.scene,
                None, TextButton.PANEL)
        item.setRecursiveVisible(False)
        item.setZValue(10)

        sw = self.window.scene.sceneRect().width()
        sh = self.window.scene.sceneRect().height()

        # Create in-animation.
        anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN)
        anim.setDuration(1050 * Colors.animSpeedButtons)
        anim.setStartPos(QtCore.QPointF(sw, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setPosAt(0.10, QtCore.QPointF(sw, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setPosAt(0.30, QtCore.QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setPosAt(0.35, QtCore.QPointF(xOffset + 30, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setPosAt(0.40, QtCore.QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setPosAt(0.45, QtCore.QPointF(xOffset + 5, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setPosAt(0.50, QtCore.QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setPosAt(1.00, QtCore.QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 26))
        movieIn.append(anim)

        # Create out-animation.
        anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT)
        anim.hideOnFinished = True
        anim.setDuration(300 * Colors.animSpeedButtons)
        anim.setStartPos(QtCore.QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 26))
        anim.setPosAt(1.0, QtCore.QPointF(xOffset, sh))
        movieOut.append(anim)

    def createInfo(self, item, name):
        movie_in = self.score.insertMovie(name)
        movie_out = self.score.insertMovie(name + ' -out')
        item.setZValue(8)
        item.setRecursiveVisible(False)

        xOffset = 230.0
        infoIn = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN)
        infoIn.timeline.setCurveShape(QtCore.QTimeLine.LinearCurve)
        infoIn.setDuration(650)
        infoIn.setStartPos(QtCore.QPointF(self.window.scene.sceneRect().width(), Colors.contentStartY))
        infoIn.setPosAt(0.60, QtCore.QPointF(xOffset, Colors.contentStartY))
        infoIn.setPosAt(0.70, QtCore.QPointF(xOffset + 20, Colors.contentStartY))
        infoIn.setPosAt(0.80, QtCore.QPointF(xOffset, Colors.contentStartY))
        infoIn.setPosAt(0.90, QtCore.QPointF(xOffset + 7, Colors.contentStartY))
        infoIn.setPosAt(1.00, QtCore.QPointF(xOffset, Colors.contentStartY))
        movie_in.append(infoIn)

        infoOut = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT)
        infoOut.timeline.setCurveShape(QtCore.QTimeLine.EaseInCurve)
        infoOut.setDuration(300)
        infoOut.hideOnFinished = True
        infoOut.setStartPos(QtCore.QPointF(xOffset, Colors.contentStartY))
        infoOut.setPosAt(1.0, QtCore.QPointF(-600, Colors.contentStartY))
        movie_out.append(infoOut)

    def createTicker(self):
        if Colors.noTicker:
            return

        movie_in = self.score.insertMovie('ticker')
        movie_out = self.score.insertMovie('ticker -out')
        movie_activate = self.score.insertMovie('ticker -activate')
        movie_deactivate = self.score.insertMovie('ticker -deactivate')

        self.ticker = ItemCircleAnimation(self.window.scene)
        self.ticker.setZValue(50)
        self.ticker.hide()

        # Move ticker in.
        qtendpos = 485
        qtPosY = 120
        self.tickerInAnim = DemoItemAnimation(self.ticker,
                DemoItemAnimation.ANIM_IN)
        self.tickerInAnim.setDuration(500)
        self.tickerInAnim.setStartPos(QtCore.QPointF(self.window.scene.sceneRect().width(), Colors.contentStartY + qtPosY))
        self.tickerInAnim.setPosAt(0.60, QtCore.QPointF(qtendpos, Colors.contentStartY + qtPosY))
        self.tickerInAnim.setPosAt(0.70, QtCore.QPointF(qtendpos + 30, Colors.contentStartY + qtPosY))
        self.tickerInAnim.setPosAt(0.80, QtCore.QPointF(qtendpos, Colors.contentStartY + qtPosY))
        self.tickerInAnim.setPosAt(0.90, QtCore.QPointF(qtendpos + 5, Colors.contentStartY + qtPosY))
        self.tickerInAnim.setPosAt(1.00, QtCore.QPointF(qtendpos, Colors.contentStartY + qtPosY))
        movie_in.append(self.tickerInAnim)

        # Move ticker out.
        qtOut = DemoItemAnimation(self.ticker, DemoItemAnimation.ANIM_OUT)
        qtOut.hideOnFinished = True
        qtOut.setDuration(500)
        qtOut.setStartPos(QtCore.QPointF(qtendpos, Colors.contentStartY + qtPosY))
        qtOut.setPosAt(1.00, QtCore.QPointF(self.window.scene.sceneRect().width() + 700, Colors.contentStartY + qtPosY))
        movie_out.append(qtOut)

        # Move ticker in on activate.
        qtActivate = DemoItemAnimation(self.ticker)
        qtActivate.setDuration(400)
        qtActivate.setStartPos(QtCore.QPointF(self.window.scene.sceneRect().width(), Colors.contentStartY + qtPosY))
        qtActivate.setPosAt(0.60, QtCore.QPointF(qtendpos, Colors.contentStartY + qtPosY))
        qtActivate.setPosAt(0.70, QtCore.QPointF(qtendpos + 30, Colors.contentStartY + qtPosY))
        qtActivate.setPosAt(0.80, QtCore.QPointF(qtendpos, Colors.contentStartY + qtPosY))
        qtActivate.setPosAt(0.90, QtCore.QPointF(qtendpos + 5, Colors.contentStartY + qtPosY))
        qtActivate.setPosAt(1.00, QtCore.QPointF(qtendpos, Colors.contentStartY + qtPosY))
        movie_activate.append(qtActivate)

        # Move ticker out on deactivate.
        qtDeactivate = DemoItemAnimation(self.ticker)
        qtDeactivate.hideOnFinished = True
        qtDeactivate.setDuration(400)
        qtDeactivate.setStartPos(QtCore.QPointF(qtendpos, Colors.contentStartY + qtPosY))
        qtDeactivate.setPosAt(1.00, QtCore.QPointF(qtendpos, 800))
        movie_deactivate.append(qtDeactivate)

    def createUpnDownButtons(self):
        xOffset = 15.0
        yOffset = 450.0

        self.upButton = TextButton("", TextButton.LEFT, MenuManager.UP,
                self.window.scene, None, TextButton.UP)
        self.upButton.prepare()
        self.upButton.setPos(xOffset, yOffset)
        self.upButton.setState(TextButton.DISABLED)

        self.downButton = TextButton("", TextButton.LEFT, MenuManager.DOWN,
                self.window.scene, None, TextButton.DOWN)
        self.downButton.prepare()
        self.downButton.setPos(xOffset + 10 + self.downButton.sceneBoundingRect().width(), yOffset)

        movieShake = self.score.insertMovie('upndown -shake')

        shakeAnim = DemoItemAnimation(self.upButton,
                DemoItemAnimation.ANIM_UNSPECIFIED)
        shakeAnim.timeline.setCurveShape(QtCore.QTimeLine.LinearCurve)
        shakeAnim.setDuration(650)
        shakeAnim.setStartPos(self.upButton.pos())
        shakeAnim.setPosAt(0.60, self.upButton.pos())
        shakeAnim.setPosAt(0.70, self.upButton.pos() + QtCore.QPointF(-2, 0))
        shakeAnim.setPosAt(0.80, self.upButton.pos() + QtCore.QPointF(1, 0))
        shakeAnim.setPosAt(0.90, self.upButton.pos() + QtCore.QPointF(-1, 0))
        shakeAnim.setPosAt(1.00, self.upButton.pos())
        movieShake.append(shakeAnim)

        shakeAnim = DemoItemAnimation(self.downButton,
                DemoItemAnimation.ANIM_UNSPECIFIED)
        shakeAnim.timeline.setCurveShape(QtCore.QTimeLine.LinearCurve)
        shakeAnim.setDuration(650)
        shakeAnim.setStartPos(self.downButton.pos())
        shakeAnim.setPosAt(0.60, self.downButton.pos())
        shakeAnim.setPosAt(0.70, self.downButton.pos() + QtCore.QPointF(-5, 0))
        shakeAnim.setPosAt(0.80, self.downButton.pos() + QtCore.QPointF(-3, 0))
        shakeAnim.setPosAt(0.90, self.downButton.pos() + QtCore.QPointF(-1, 0))
        shakeAnim.setPosAt(1.00, self.downButton.pos())
        movieShake.append(shakeAnim)

    def createBackButton(self):
        backIn = self.score.insertMovie('back -in')
        backOut = self.score.insertMovie('back -out')
        backShake = self.score.insertMovie('back -shake')
        self.createLowLeftButton("Back", MenuManager.ROOT, backIn, backOut,
                backShake, Colors.rootMenuName)
Exemple #3
0
class MenuManager(QObject):
    ROOT, MENU1, MENU2, LAUNCH, DOCUMENTATION, QUIT, FULLSCREEN, UP, DOWN, \
            BACK, LAUNCH_QML = range(11)

    pInstance = None

    def __init__(self):
        super(MenuManager, self).__init__()

        self.contentsDoc = None
        self.assistantProcess = QProcess()
        self.helpRootUrl = ''
        self.docDir = QDir()
        self.imgDir = QDir()

        self.info = {}
        self.window = None

        self.ticker = None
        self.tickerInAnim = None
        self.upButton = None
        self.downButton = None
        self.score = Score()
        self.currentMenu = "[no menu visible]"
        self.currentCategory = "[no category visible]"
        self.currentMenuButtons = "[no menu buttons visible]"
        self.currentInfo = "[no info visible]"
        self.currentMenuCode = -1
        self.readXmlDocument()

    @classmethod
    def instance(cls):
        if cls.pInstance is None:
            cls.pInstance = cls()

        return cls.pInstance

    def getResource(self, name):
        return QByteArray()

    def readXmlDocument(self):
        root = QFileInfo(__file__).absolutePath()
        xml_file = QFile(root + '/examples.xml')
        xml_file.open(QFile.ReadOnly | QFile.Text)
        contents = xml_file.readAll().data()
        xml_file.close()

        self.contentsDoc = parseString(contents)

    def itemSelected(self, userCode, menuName):
        if userCode == MenuManager.LAUNCH:
            self.launchExample(self.currentInfo)
        elif userCode == MenuManager.LAUNCH_QML:
            self.launchQml(self.currentInfo)
        elif userCode == MenuManager.DOCUMENTATION:
            self.showDocInAssistant(self.currentInfo)
        elif userCode == MenuManager.QUIT:
            QApplication.quit()
        elif userCode == MenuManager.FULLSCREEN:
            self.window.toggleFullscreen()
        elif userCode == MenuManager.ROOT:
            # Out.
            self.score.queueMovie(self.currentMenu + ' -out', Score.FROM_START,
                                  Score.LOCK_ITEMS)
            self.score.queueMovie(self.currentMenuButtons + ' -out',
                                  Score.FROM_START, Score.LOCK_ITEMS)
            self.score.queueMovie(self.currentInfo + ' -out')
            self.score.queueMovie(self.currentInfo + ' -buttons -out',
                                  Score.NEW_ANIMATION_ONLY)
            self.score.queueMovie('back -out', Score.ONLY_IF_VISIBLE)

            # Book-keeping.
            self.currentMenuCode = MenuManager.ROOT
            self.currentMenu = menuName + ' -menu1'
            self.currentMenuButtons = menuName + ' -buttons'
            self.currentInfo = menuName + ' -info'

            # In.
            self.score.queueMovie('upndown -shake')
            self.score.queueMovie(self.currentMenu, Score.FROM_START,
                                  Score.UNLOCK_ITEMS)
            self.score.queueMovie(self.currentMenuButtons, Score.FROM_START,
                                  Score.UNLOCK_ITEMS)
            self.score.queueMovie(self.currentInfo)

            if not Colors.noTicker:
                self.ticker.doIntroTransitions = True
                self.tickerInAnim.setStartDelay(2000)
                self.ticker.useGuideQt()
                self.score.queueMovie('ticker', Score.NEW_ANIMATION_ONLY)
        elif userCode == MenuManager.MENU1:
            # Out.
            self.score.queueMovie(self.currentMenu + ' -out', Score.FROM_START,
                                  Score.LOCK_ITEMS)
            self.score.queueMovie(self.currentMenuButtons + ' -out',
                                  Score.FROM_START, Score.LOCK_ITEMS)
            self.score.queueMovie(self.currentInfo + ' -out')

            # Book-keeping.
            self.currentMenuCode = MenuManager.MENU1
            self.currentCategory = menuName
            self.currentMenu = menuName + ' -menu1'
            self.currentInfo = menuName + ' -info'

            # In.
            self.score.queueMovie('upndown -shake')
            self.score.queueMovie('back -in')
            self.score.queueMovie(self.currentMenu, Score.FROM_START,
                                  Score.UNLOCK_ITEMS)
            self.score.queueMovie(self.currentInfo)

            if not Colors.noTicker:
                self.ticker.useGuideTt()
        elif userCode == MenuManager.MENU2:
            # Out.
            self.score.queueMovie(self.currentInfo + ' -out',
                                  Score.NEW_ANIMATION_ONLY)
            self.score.queueMovie(self.currentInfo + ' -buttons -out',
                                  Score.NEW_ANIMATION_ONLY)

            # Book-keeping.
            self.currentMenuCode = MenuManager.MENU2
            self.currentInfo = menuName

            # In/shake.
            self.score.queueMovie('upndown -shake')
            self.score.queueMovie('back -shake')
            self.score.queueMovie(self.currentMenu + ' -shake')
            self.score.queueMovie(self.currentInfo, Score.NEW_ANIMATION_ONLY)
            self.score.queueMovie(self.currentInfo + ' -buttons',
                                  Score.NEW_ANIMATION_ONLY)

            if not Colors.noTicker:
                self.score.queueMovie('ticker -out', Score.NEW_ANIMATION_ONLY)
        elif userCode == MenuManager.UP:
            backMenu = self.info[self.currentMenu]['back']
            if backMenu:
                self.score.queueMovie(self.currentMenu + ' -top_out',
                                      Score.FROM_START, Score.LOCK_ITEMS)
                self.score.queueMovie(backMenu + ' -bottom_in',
                                      Score.FROM_START, Score.UNLOCK_ITEMS)
                self.currentMenu = backMenu
        elif userCode == MenuManager.DOWN:
            moreMenu = self.info[self.currentMenu]['more']
            if moreMenu:
                self.score.queueMovie(self.currentMenu + ' -bottom_out',
                                      Score.FROM_START, Score.LOCK_ITEMS)
                self.score.queueMovie(moreMenu + ' -top_in', Score.FROM_START,
                                      Score.UNLOCK_ITEMS)
                self.currentMenu = moreMenu
        elif userCode == MenuManager.BACK:
            if self.currentMenuCode == MenuManager.MENU2:
                # Out.
                self.score.queueMovie(self.currentInfo + ' -out',
                                      Score.NEW_ANIMATION_ONLY)
                self.score.queueMovie(self.currentInfo + ' -buttons -out',
                                      Score.NEW_ANIMATION_ONLY)

                # Book-keeping.
                self.currentMenuCode = MenuManager.MENU1
                self.currentMenuButtons = self.currentCategory + ' -buttons'
                self.currentInfo = self.currentCategory + ' -info'

                # In/shake.
                self.score.queueMovie('upndown -shake')
                self.score.queueMovie(self.currentMenu + ' -shake')
                self.score.queueMovie(self.currentInfo,
                                      Score.NEW_ANIMATION_ONLY)
                self.score.queueMovie(self.currentInfo + ' -buttons',
                                      Score.NEW_ANIMATION_ONLY)

                if not Colors.noTicker:
                    self.ticker.doIntroTransitions = False
                    self.tickerInAnim.setStartDelay(500)
                    self.score.queueMovie('ticker', Score.NEW_ANIMATION_ONLY)
            elif self.currentMenuCode != MenuManager.ROOT:
                self.itemSelected(MenuManager.ROOT, Colors.rootMenuName)

        # Update back and more buttons.
        if self.info.setdefault(self.currentMenu, {}).get('back'):
            back_state = TextButton.OFF
        else:
            back_state = TextButton.DISABLED

        if self.info[self.currentMenu].get('more'):
            more_state = TextButton.OFF
        else:
            more_state = TextButton.DISABLED

        self.upButton.setState(back_state)
        self.downButton.setState(more_state)

        if self.score.hasQueuedMovies():
            self.score.playQue()
            # Playing new movies might include loading etc., so ignore the FPS
            # at this point.
            self.window.fpsHistory = []

    def showDocInAssistant(self, name):
        url = self.resolveDocUrl(name)
        Colors.debug("Sending URL to Assistant:", url)

        # Start assistant if it's not already running.
        if self.assistantProcess.state() != QProcess.Running:
            app = QLibraryInfo.location(
                QLibraryInfo.BinariesPath) + QDir.separator()

            if sys.platform == 'darwin':
                app += 'Assistant.app/Contents/MacOS/Assistant'
            else:
                app += 'assistant'

            args = ['-enableRemoteControl']
            self.assistantProcess.start(app, args)
            if not self.assistantProcess.waitForStarted():
                QMessageBox.critical(None, "PyQt Demo",
                                     "Could not start %s." % app)
                return

        # Send command through remote control even if the process was just
        # started to activate assistant and bring it to the front.
        cmd_str = QTextStream(self.assistantProcess)
        cmd_str << 'SetSource ' << url << '\n'

    def launchExample(self, name):
        executable = self.resolveExeFile(name)

        process = QProcess(self)
        process.error.connect(self.launchError)

        if sys.platform == 'win32':
            # Make sure it finds the DLLs on Windows.
            env = QProcessEnvironment.systemEnvironment()
            env.insert(
                'PATH',
                QLibraryInfo.location(QLibraryInfo.BinariesPath) + ';' +
                env.value('PATH'))
            process.setProcessEnvironment(env)

        if self.info[name]['changedirectory'] != 'false':
            workingDirectory = self.resolveDataDir(name)
            process.setWorkingDirectory(workingDirectory)
            Colors.debug("Setting working directory:", workingDirectory)

        Colors.debug("Launching:", executable)
        process.start(sys.executable, [executable])

    def launchQml(self, name):
        import_path = self.resolveDataDir(name)
        qml = self.resolveQmlFile(name)

        process = QProcess(self)
        process.error.connect(self.launchError)

        env = QProcessEnvironment.systemEnvironment()
        env.insert('QML2_IMPORT_PATH', import_path)
        process.setProcessEnvironment(env)

        executable = QLibraryInfo.location(
            QLibraryInfo.BinariesPath) + '/qmlscene'
        Colors.debug("Launching:", executable)
        process.start(executable, [qml])

    def launchError(self, error):
        if error != QProcess.Crashed:
            QMessageBox.critical(
                None, "Failed to launch the example",
                "Could not launch the example. Ensure that it has been "
                "built.", QMessageBox.Cancel)

    def init(self, window):
        self.window = window

        # Create div.
        self.createTicker()
        self.createUpnDownButtons()
        self.createBackButton()

        # Create first level menu.
        rootElement = self.contentsDoc.documentElement
        self.createRootMenu(rootElement)

        # Create second level menus.
        level2Menu = self._first_element(rootElement)
        while level2Menu is not None:
            self.createSubMenu(level2Menu)

            # Create leaf menu and example info.
            example = self._first_element(level2Menu)
            while example is not None:
                self.readInfoAboutExample(example)
                self.createLeafMenu(example)
                example = self._next_element(example)

            level2Menu = self._next_element(level2Menu)

    @classmethod
    def _first_element(cls, node):
        return cls._skip_nonelements(node.firstChild)

    @classmethod
    def _next_element(cls, node):
        return cls._skip_nonelements(node.nextSibling)

    @staticmethod
    def _skip_nonelements(node):
        while node is not None and node.nodeType != node.ELEMENT_NODE:
            node = node.nextSibling

        return node

    def readInfoAboutExample(self, example):
        name = example.getAttribute('name')
        if name in self.info:
            Colors.debug(
                "__WARNING: MenuManager.readInfoAboutExample: "
                "Demo/example with name", name, "appears twice in "
                "the xml-file!__")

        self.info.setdefault(name,
                             {})['filename'] = example.getAttribute('filename')
        self.info[name]['dirname'] = example.parentNode.getAttribute('dirname')
        self.info[name]['changedirectory'] = example.getAttribute(
            'changedirectory')
        self.info[name]['image'] = example.getAttribute('image')
        self.info[name]['qml'] = example.getAttribute('qml')

    def resolveDir(self, name):
        dirName = self.info[name]['dirname']
        fileName = self.info[name]['filename'].split('/')

        dir = QFileInfo(__file__).dir()
        # To the 'examples' directory.
        dir.cdUp()

        dir.cd(dirName)

        if len(fileName) > 1:
            dir.cd('/'.join(fileName[:-1]))

        # This may legitimately fail if the example is just a simple .py file.
        dir.cd(fileName[-1])

        return dir

    def resolveDataDir(self, name):
        return self.resolveDir(name).absolutePath()

    def resolveExeFile(self, name):
        dir = self.resolveDir(name)

        fileName = self.info[name]['filename'].split('/')[-1]

        pyFile = QFile(dir.path() + '/' + fileName + '.py')
        if pyFile.exists():
            return pyFile.fileName()

        pywFile = QFile(dir.path() + '/' + fileName + '.pyw')
        if pywFile.exists():
            return pywFile.fileName()

        Colors.debug("- WARNING: Could not resolve executable:", dir.path(),
                     fileName)
        return '__executable not found__'

    def resolveQmlFile(self, name):
        dir = self.resolveDir(name)

        fileName = self.info[name]['filename'].split('/')[-1]

        qmlFile = QFile(dir.path() + '/' + fileName + '.qml')
        if qmlFile.exists():
            return qmlFile.fileName()

        Colors.debug("- WARNING: Could not resolve QML file:", dir.path(),
                     fileName)
        return '__QML not found__'

    def resolveDocUrl(self, name):
        dirName = self.info[name]['dirname']
        fileName = self.info[name]['filename']

        return self.helpRootUrl + dirName.replace(
            '/', '-') + '-' + fileName + '.html'

    def resolveImageUrl(self, name):
        return self.helpRootUrl + 'images/' + name

    def getHtml(self, name):
        return self.getResource(self.resolveDocUrl(name))

    def getImage(self, name):
        imageName = self.info[name]['image']
        fileName = self.info[name]['filename']

        if self.info[name]['qml'] == 'true':
            fileName = 'qml-' + fileName.split('/')[-1]

        if not imageName:
            imageName = fileName + '-example.png'

            if self.getResource(self.resolveImageUrl(imageName)).isEmpty():
                imageName = fileName + '.png'

            if self.getResource(self.resolveImageUrl(imageName)).isEmpty():
                imageName = fileName + 'example.png'

        return self.getResource(self.resolveImageUrl(imageName))

    def createRootMenu(self, el):
        name = el.getAttribute('name')
        self.createMenu(el, MenuManager.MENU1)
        self.createInfo(MenuContentItem(el, self.window.mainSceneRoot),
                        name + ' -info')

        menuButtonsIn = self.score.insertMovie(name + ' -buttons')
        menuButtonsOut = self.score.insertMovie(name + ' -buttons -out')
        self.createLowLeftButton("Quit", MenuManager.QUIT, menuButtonsIn,
                                 menuButtonsOut, None)
        self.createLowRightButton("Toggle fullscreen", MenuManager.FULLSCREEN,
                                  menuButtonsIn, menuButtonsOut, None)

    def createSubMenu(self, el):
        name = el.getAttribute('name')
        self.createMenu(el, MenuManager.MENU2)
        self.createInfo(MenuContentItem(el, self.window.mainSceneRoot),
                        name + ' -info')

    def createLeafMenu(self, el):
        name = el.getAttribute('name')
        self.createInfo(ExampleContent(name, self.window.mainSceneRoot), name)

        infoButtonsIn = self.score.insertMovie(name + ' -buttons')
        infoButtonsOut = self.score.insertMovie(name + ' -buttons -out')
        self.createLowRightLeafButton("Documentation", 600,
                                      MenuManager.DOCUMENTATION, infoButtonsIn,
                                      infoButtonsOut, None)
        if el.getAttribute('executable') != 'false':
            self.createLowRightLeafButton("Launch", 405, MenuManager.LAUNCH,
                                          infoButtonsIn, infoButtonsOut, None)
        elif el.getAttribute('qml') == 'true':
            self.createLowRightLeafButton("Display", 405,
                                          MenuManager.LAUNCH_QML,
                                          infoButtonsIn, infoButtonsOut, None)

    def createMenu(self, category, type):
        sw = self.window.scene.sceneRect().width()
        xOffset = 15
        yOffset = 10
        maxExamples = Colors.menuCount
        menuIndex = 1
        name = category.getAttribute('name')
        currentNode = self._first_element(category)
        currentMenu = '%s -menu%d' % (name, menuIndex)

        while currentNode is not None:
            movieIn = self.score.insertMovie(currentMenu)
            movieOut = self.score.insertMovie(currentMenu + ' -out')
            movieNextTopOut = self.score.insertMovie(currentMenu + ' -top_out')
            movieNextBottomOut = self.score.insertMovie(currentMenu +
                                                        ' -bottom_out')
            movieNextTopIn = self.score.insertMovie(currentMenu + ' -top_in')
            movieNextBottomIn = self.score.insertMovie(currentMenu +
                                                       ' -bottom_in')
            movieShake = self.score.insertMovie(currentMenu + ' -shake')

            i = 0
            while currentNode is not None and i < maxExamples:
                # Create a normal menu button.
                label = currentNode.getAttribute('name')
                item = TextButton(label, TextButton.LEFT, type,
                                  self.window.mainSceneRoot)

                item.setRecursiveVisible(False)
                item.setZValue(10)
                ih = item.sceneBoundingRect().height()
                iw = item.sceneBoundingRect().width()
                ihp = ih + 3

                # Create in-animation.
                anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN)
                anim.setDuration(1000 + (i * 20))
                anim.setStartValue(QPointF(xOffset, -ih))
                anim.setKeyValueAt(0.20, QPointF(xOffset, -ih))
                anim.setKeyValueAt(
                    0.50,
                    QPointF(xOffset, (i * ihp) + yOffset +
                            Colors.contentStartY + (10 * float(i / 4.0))))
                anim.setKeyValueAt(
                    0.60,
                    QPointF(xOffset,
                            (i * ihp) + yOffset + Colors.contentStartY))
                anim.setKeyValueAt(
                    0.70,
                    QPointF(xOffset, (i * ihp) + yOffset +
                            Colors.contentStartY + (5 * float(i / 4.0))))
                anim.setKeyValueAt(
                    0.80,
                    QPointF(xOffset,
                            (i * ihp) + yOffset + Colors.contentStartY))
                anim.setKeyValueAt(
                    0.90,
                    QPointF(xOffset, (i * ihp) + yOffset +
                            Colors.contentStartY + (2 * float(i / 4.0))))
                anim.setEndValue(
                    QPointF(xOffset,
                            (i * ihp) + yOffset + Colors.contentStartY))
                movieIn.append(anim)

                # Create out-animation.
                anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT)
                anim.setHideOnFinished(True)
                anim.setDuration(700 + (30 * i))
                anim.setStartValue(
                    QPointF(xOffset,
                            (i * ihp) + yOffset + Colors.contentStartY))
                anim.setKeyValueAt(0.60, QPointF(xOffset, 600 - ih - ih))
                anim.setKeyValueAt(0.65, QPointF(xOffset + 20, 600 - ih))
                anim.setEndValue(QPointF(sw + iw, 600 - ih))
                movieOut.append(anim)

                # Create shake-animation.
                anim = DemoItemAnimation(item)
                anim.setDuration(700)
                anim.setStartValue(
                    QPointF(xOffset,
                            (i * ihp) + yOffset + Colors.contentStartY))
                anim.setKeyValueAt(
                    0.55,
                    QPointF(xOffset, (i * ihp) + yOffset +
                            Colors.contentStartY - i * 2.0))
                anim.setKeyValueAt(
                    0.70,
                    QPointF(xOffset - 10, (i * ihp) + yOffset +
                            Colors.contentStartY - i * 1.5))
                anim.setKeyValueAt(
                    0.80,
                    QPointF(xOffset, (i * ihp) + yOffset +
                            Colors.contentStartY - i * 1.0))
                anim.setKeyValueAt(
                    0.90,
                    QPointF(xOffset - 2, (i * ihp) + yOffset +
                            Colors.contentStartY - i * 0.5))
                anim.setEndValue(
                    QPointF(xOffset,
                            (i * ihp) + yOffset + Colors.contentStartY))
                movieShake.append(anim)

                # Create next-menu top-out-animation.
                anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT)
                anim.setHideOnFinished(True)
                anim.setDuration(200 + (30 * i))
                anim.setStartValue(
                    QPointF(xOffset,
                            (i * ihp) + yOffset + Colors.contentStartY))
                anim.setKeyValueAt(
                    0.70, QPointF(xOffset, yOffset + Colors.contentStartY))
                anim.setEndValue(QPointF(-iw, yOffset + Colors.contentStartY))
                movieNextTopOut.append(anim)

                # Create next-menu bottom-out-animation.
                anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT)
                anim.setHideOnFinished(True)
                anim.setDuration(200 + (30 * i))
                anim.setStartValue(
                    QPointF(xOffset,
                            (i * ihp) + yOffset + Colors.contentStartY))
                anim.setKeyValueAt(
                    0.70,
                    QPointF(xOffset, (maxExamples * ihp) + yOffset +
                            Colors.contentStartY))
                anim.setEndValue(
                    QPointF(-iw, (maxExamples * ihp) + yOffset +
                            Colors.contentStartY))
                movieNextBottomOut.append(anim)

                # Create next-menu top-in-animation.
                anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN)
                anim.setDuration(700 - (30 * i))
                anim.setStartValue(QPointF(-iw,
                                           yOffset + Colors.contentStartY))
                anim.setKeyValueAt(
                    0.30, QPointF(xOffset, yOffset + Colors.contentStartY))
                anim.setEndValue(
                    QPointF(xOffset,
                            (i * ihp) + yOffset + Colors.contentStartY))
                movieNextTopIn.append(anim)

                # Create next-menu bottom-in-animation.
                reverse = maxExamples - i
                anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN)
                anim.setDuration(1000 - (30 * reverse))
                anim.setStartValue(
                    QPointF(-iw, (maxExamples * ihp) + yOffset +
                            Colors.contentStartY))
                anim.setKeyValueAt(
                    0.30,
                    QPointF(xOffset, (maxExamples * ihp) + yOffset +
                            Colors.contentStartY))
                anim.setEndValue(
                    QPointF(xOffset,
                            (i * ihp) + yOffset + Colors.contentStartY))
                movieNextBottomIn.append(anim)

                i += 1
                currentNode = self._next_element(currentNode)

            if currentNode is not None and i == maxExamples:
                # We need another menu, so register for 'more' and 'back'
                # buttons.
                menuIndex += 1
                self.info.setdefault(
                    currentMenu, {})['more'] = '%s -menu%d' % (name, menuIndex)
                currentMenu = '%s -menu%d' % (name, menuIndex)
                self.info.setdefault(
                    currentMenu,
                    {})['back'] = '%s -menu%d' % (name, menuIndex - 1)

    def createLowLeftButton(self,
                            label,
                            type,
                            movieIn,
                            movieOut,
                            movieShake,
                            menuString=""):
        button = TextButton(label, TextButton.RIGHT, type,
                            self.window.mainSceneRoot, TextButton.PANEL)
        if menuString:
            button.setMenuString(menuString)
        button.setRecursiveVisible(False)
        button.setZValue(10)

        iw = button.sceneBoundingRect().width()
        xOffset = 15

        # Create in-animation.
        buttonIn = DemoItemAnimation(button, DemoItemAnimation.ANIM_IN)
        buttonIn.setDuration(1800)
        buttonIn.setStartValue(
            QPointF(-iw, Colors.contentStartY + Colors.contentHeight - 35))
        buttonIn.setKeyValueAt(
            0.5, QPointF(-iw,
                         Colors.contentStartY + Colors.contentHeight - 35))
        buttonIn.setKeyValueAt(
            0.7,
            QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 35))
        buttonIn.setEndValue(
            QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 26))
        movieIn.append(buttonIn)

        # Create out-animation.
        buttonOut = DemoItemAnimation(button, DemoItemAnimation.ANIM_OUT)
        buttonOut.setHideOnFinished(True)
        buttonOut.setDuration(400)
        buttonOut.setStartValue(
            QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 26))
        buttonOut.setEndValue(
            QPointF(-iw, Colors.contentStartY + Colors.contentHeight - 26))
        movieOut.append(buttonOut)

        if movieShake is not None:
            shakeAnim = DemoItemAnimation(button,
                                          DemoItemAnimation.ANIM_UNSPECIFIED)
            shakeAnim.setDuration(650)
            shakeAnim.setStartValue(buttonIn.endValue())
            shakeAnim.setKeyValueAt(0.60, buttonIn.endValue())
            shakeAnim.setKeyValueAt(0.70, buttonIn.endValue() + QPointF(-3, 0))
            shakeAnim.setKeyValueAt(0.80, buttonIn.endValue() + QPointF(2, 0))
            shakeAnim.setKeyValueAt(0.90, buttonIn.endValue() + QPointF(-1, 0))
            shakeAnim.setEndValue(buttonIn.endValue())
            movieShake.append(shakeAnim)

    def createLowRightButton(self, label, type, movieIn, movieOut, movieShake):
        item = TextButton(label, TextButton.RIGHT, type,
                          self.window.mainSceneRoot, TextButton.PANEL)
        item.setRecursiveVisible(False)
        item.setZValue(10)

        sw = self.window.scene.sceneRect().width()
        xOffset = 70

        # Create in-animation.
        anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN)
        anim.setDuration(1800)
        anim.setStartValue(
            QPointF(sw, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setKeyValueAt(
            0.5, QPointF(sw, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setKeyValueAt(
            0.7,
            QPointF(xOffset + 535,
                    Colors.contentStartY + Colors.contentHeight - 35))
        anim.setEndValue(
            QPointF(xOffset + 535,
                    Colors.contentStartY + Colors.contentHeight - 26))
        movieIn.append(anim)

        # Create out-animation.
        anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT)
        anim.setHideOnFinished(True)
        anim.setDuration(400)
        anim.setStartValue(
            QPointF(xOffset + 535,
                    Colors.contentStartY + Colors.contentHeight - 26))
        anim.setEndValue(
            QPointF(sw, Colors.contentStartY + Colors.contentHeight - 26))
        movieOut.append(anim)

    def createLowRightLeafButton(self, label, xOffset, type, movieIn, movieOut,
                                 movieShake):
        item = TextButton(label, TextButton.RIGHT, type,
                          self.window.mainSceneRoot, TextButton.PANEL)
        item.setRecursiveVisible(False)
        item.setZValue(10)

        sw = self.window.scene.sceneRect().width()
        sh = self.window.scene.sceneRect().height()

        # Create in-animation.
        anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN)
        anim.setDuration(1050)
        anim.setStartValue(
            QPointF(sw, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setKeyValueAt(
            0.10, QPointF(sw,
                          Colors.contentStartY + Colors.contentHeight - 35))
        anim.setKeyValueAt(
            0.30,
            QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setKeyValueAt(
            0.35,
            QPointF(xOffset + 30,
                    Colors.contentStartY + Colors.contentHeight - 35))
        anim.setKeyValueAt(
            0.40,
            QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setKeyValueAt(
            0.45,
            QPointF(xOffset + 5,
                    Colors.contentStartY + Colors.contentHeight - 35))
        anim.setKeyValueAt(
            0.50,
            QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setEndValue(
            QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 26))
        movieIn.append(anim)

        # Create out-animation.
        anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT)
        anim.setHideOnFinished(True)
        anim.setDuration(300)
        anim.setStartValue(
            QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 26))
        anim.setEndValue(QPointF(xOffset, sh))
        movieOut.append(anim)

    def createInfo(self, item, name):
        movie_in = self.score.insertMovie(name)
        movie_out = self.score.insertMovie(name + ' -out')
        item.setZValue(8)
        item.setRecursiveVisible(False)

        xOffset = 230.0
        infoIn = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN)
        infoIn.setDuration(650)
        infoIn.setStartValue(
            QPointF(self.window.scene.sceneRect().width(),
                    Colors.contentStartY))
        infoIn.setKeyValueAt(0.60, QPointF(xOffset, Colors.contentStartY))
        infoIn.setKeyValueAt(0.70, QPointF(xOffset + 20, Colors.contentStartY))
        infoIn.setKeyValueAt(0.80, QPointF(xOffset, Colors.contentStartY))
        infoIn.setKeyValueAt(0.90, QPointF(xOffset + 7, Colors.contentStartY))
        infoIn.setEndValue(QPointF(xOffset, Colors.contentStartY))
        movie_in.append(infoIn)

        infoOut = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT)
        infoOut.setCurveShape(QEasingCurve.InQuad)
        infoOut.setDuration(300)
        infoOut.setHideOnFinished(True)
        infoOut.setStartValue(QPointF(xOffset, Colors.contentStartY))
        infoOut.setEndValue(QPointF(-600, Colors.contentStartY))
        movie_out.append(infoOut)

    def createTicker(self):
        if Colors.noTicker:
            return

        movie_in = self.score.insertMovie('ticker')
        movie_out = self.score.insertMovie('ticker -out')
        movie_activate = self.score.insertMovie('ticker -activate')
        movie_deactivate = self.score.insertMovie('ticker -deactivate')

        self.ticker = ItemCircleAnimation()
        self.ticker.setZValue(50)
        self.ticker.hide()

        # Move ticker in.
        qtendpos = 485
        qtPosY = 120
        self.tickerInAnim = DemoItemAnimation(self.ticker,
                                              DemoItemAnimation.ANIM_IN)
        self.tickerInAnim.setDuration(500)
        self.tickerInAnim.setStartValue(
            QPointF(self.window.scene.sceneRect().width(),
                    Colors.contentStartY + qtPosY))
        self.tickerInAnim.setKeyValueAt(
            0.60, QPointF(qtendpos, Colors.contentStartY + qtPosY))
        self.tickerInAnim.setKeyValueAt(
            0.70, QPointF(qtendpos + 30, Colors.contentStartY + qtPosY))
        self.tickerInAnim.setKeyValueAt(
            0.80, QPointF(qtendpos, Colors.contentStartY + qtPosY))
        self.tickerInAnim.setKeyValueAt(
            0.90, QPointF(qtendpos + 5, Colors.contentStartY + qtPosY))
        self.tickerInAnim.setEndValue(
            QPointF(qtendpos, Colors.contentStartY + qtPosY))
        movie_in.append(self.tickerInAnim)

        # Move ticker out.
        qtOut = DemoItemAnimation(self.ticker, DemoItemAnimation.ANIM_OUT)
        qtOut.setHideOnFinished(True)
        qtOut.setDuration(500)
        qtOut.setStartValue(QPointF(qtendpos, Colors.contentStartY + qtPosY))
        qtOut.setEndValue(
            QPointF(self.window.scene.sceneRect().width() + 700,
                    Colors.contentStartY + qtPosY))
        movie_out.append(qtOut)

        # Move ticker in on activate.
        qtActivate = DemoItemAnimation(self.ticker)
        qtActivate.setDuration(400)
        qtActivate.setStartValue(
            QPointF(self.window.scene.sceneRect().width(),
                    Colors.contentStartY + qtPosY))
        qtActivate.setKeyValueAt(
            0.60, QPointF(qtendpos, Colors.contentStartY + qtPosY))
        qtActivate.setKeyValueAt(
            0.70, QPointF(qtendpos + 30, Colors.contentStartY + qtPosY))
        qtActivate.setKeyValueAt(
            0.80, QPointF(qtendpos, Colors.contentStartY + qtPosY))
        qtActivate.setKeyValueAt(
            0.90, QPointF(qtendpos + 5, Colors.contentStartY + qtPosY))
        qtActivate.setEndValue(QPointF(qtendpos,
                                       Colors.contentStartY + qtPosY))
        movie_activate.append(qtActivate)

        # Move ticker out on deactivate.
        qtDeactivate = DemoItemAnimation(self.ticker)
        qtDeactivate.setHideOnFinished(True)
        qtDeactivate.setDuration(400)
        qtDeactivate.setStartValue(
            QPointF(qtendpos, Colors.contentStartY + qtPosY))
        qtDeactivate.setEndValue(QPointF(qtendpos, 800))
        movie_deactivate.append(qtDeactivate)

    def createUpnDownButtons(self):
        xOffset = 15.0
        yOffset = 450.0

        self.upButton = TextButton("", TextButton.LEFT, MenuManager.UP,
                                   self.window.mainSceneRoot, TextButton.UP)
        self.upButton.prepare()
        self.upButton.setPos(xOffset, yOffset)
        self.upButton.setState(TextButton.DISABLED)

        self.downButton = TextButton("", TextButton.LEFT, MenuManager.DOWN,
                                     self.window.mainSceneRoot,
                                     TextButton.DOWN)
        self.downButton.prepare()
        self.downButton.setPos(
            xOffset + 10 + self.downButton.sceneBoundingRect().width(),
            yOffset)

        movieShake = self.score.insertMovie('upndown -shake')

        shakeAnim = DemoItemAnimation(self.upButton,
                                      DemoItemAnimation.ANIM_UNSPECIFIED)
        shakeAnim.setDuration(650)
        shakeAnim.setStartValue(self.upButton.pos())
        shakeAnim.setKeyValueAt(0.60, self.upButton.pos())
        shakeAnim.setKeyValueAt(0.70, self.upButton.pos() + QPointF(-2, 0))
        shakeAnim.setKeyValueAt(0.80, self.upButton.pos() + QPointF(1, 0))
        shakeAnim.setKeyValueAt(0.90, self.upButton.pos() + QPointF(-1, 0))
        shakeAnim.setEndValue(self.upButton.pos())
        movieShake.append(shakeAnim)

        shakeAnim = DemoItemAnimation(self.downButton,
                                      DemoItemAnimation.ANIM_UNSPECIFIED)
        shakeAnim.setDuration(650)
        shakeAnim.setStartValue(self.downButton.pos())
        shakeAnim.setKeyValueAt(0.60, self.downButton.pos())
        shakeAnim.setKeyValueAt(0.70, self.downButton.pos() + QPointF(-5, 0))
        shakeAnim.setKeyValueAt(0.80, self.downButton.pos() + QPointF(-3, 0))
        shakeAnim.setKeyValueAt(0.90, self.downButton.pos() + QPointF(-1, 0))
        shakeAnim.setEndValue(self.downButton.pos())
        movieShake.append(shakeAnim)

    def createBackButton(self):
        backIn = self.score.insertMovie('back -in')
        backOut = self.score.insertMovie('back -out')
        backShake = self.score.insertMovie('back -shake')
        self.createLowLeftButton("Back", MenuManager.ROOT, backIn, backOut,
                                 backShake, Colors.rootMenuName)
Exemple #4
0
class MenuManager(QObject):
    ROOT, MENU1, MENU2, LAUNCH, DOCUMENTATION, QUIT, FULLSCREEN, UP, DOWN, \
            BACK, LAUNCH_QML = range(11)

    pInstance = None

    def __init__(self):
        super(MenuManager, self).__init__()

        self.contentsDoc = None
        self.assistantProcess = QProcess()
        self.helpRootUrl = ''
        self.docDir = QDir()
        self.imgDir = QDir()

        self.info = {}
        self.window = None

        self.ticker = None
        self.tickerInAnim = None
        self.upButton = None
        self.downButton = None
        self.score = Score()
        self.currentMenu = "[no menu visible]"
        self.currentCategory = "[no category visible]"
        self.currentMenuButtons = "[no menu buttons visible]"
        self.currentInfo = "[no info visible]"
        self.currentMenuCode = -1
        self.readXmlDocument()

    @classmethod
    def instance(cls):
        if cls.pInstance is None:
            cls.pInstance = cls()

        return cls.pInstance

    def getResource(self, name):
        return QByteArray()

    def readXmlDocument(self):
        xml_file = QFile(':/xml/examples.xml')
        xml_file.open(QFile.ReadOnly | QFile.Text)
        contents = xml_file.readAll().data()
        xml_file.close()

        self.contentsDoc = parseString(contents)

    def itemSelected(self, userCode, menuName):
        if userCode == MenuManager.LAUNCH:
            self.launchExample(self.currentInfo)
        elif userCode == MenuManager.LAUNCH_QML:
            self.launchQml(self.currentInfo)
        elif userCode == MenuManager.DOCUMENTATION:
            self.showDocInAssistant(self.currentInfo)
        elif userCode == MenuManager.QUIT:
            QApplication.quit()
        elif userCode == MenuManager.FULLSCREEN:
            self.window.toggleFullscreen()
        elif userCode == MenuManager.ROOT:
            # Out.
            self.score.queueMovie(self.currentMenu + ' -out', Score.FROM_START,
                    Score.LOCK_ITEMS)
            self.score.queueMovie(self.currentMenuButtons + ' -out',
                    Score.FROM_START, Score.LOCK_ITEMS)
            self.score.queueMovie(self.currentInfo + ' -out')
            self.score.queueMovie(self.currentInfo + ' -buttons -out',
                    Score.NEW_ANIMATION_ONLY)
            self.score.queueMovie('back -out', Score.ONLY_IF_VISIBLE)

            # Book-keeping.
            self.currentMenuCode = MenuManager.ROOT
            self.currentMenu = menuName + ' -menu1'
            self.currentMenuButtons = menuName + ' -buttons'
            self.currentInfo = menuName + ' -info'

            # In.
            self.score.queueMovie('upndown -shake')
            self.score.queueMovie(self.currentMenu, Score.FROM_START,
                    Score.UNLOCK_ITEMS)
            self.score.queueMovie(self.currentMenuButtons, Score.FROM_START,
                    Score.UNLOCK_ITEMS)
            self.score.queueMovie(self.currentInfo)

            if not Colors.noTicker:
                self.ticker.doIntroTransitions = True
                self.tickerInAnim.setStartDelay(2000)
                self.ticker.useGuideQt()
                self.score.queueMovie('ticker', Score.NEW_ANIMATION_ONLY)
        elif userCode == MenuManager.MENU1:
            # Out.
            self.score.queueMovie(self.currentMenu + ' -out', Score.FROM_START,
                    Score.LOCK_ITEMS)
            self.score.queueMovie(self.currentMenuButtons + ' -out',
                    Score.FROM_START, Score.LOCK_ITEMS)
            self.score.queueMovie(self.currentInfo + ' -out')

            # Book-keeping.
            self.currentMenuCode = MenuManager.MENU1
            self.currentCategory = menuName
            self.currentMenu = menuName + ' -menu1'
            self.currentInfo = menuName + ' -info'

            # In.
            self.score.queueMovie('upndown -shake')
            self.score.queueMovie('back -in')
            self.score.queueMovie(self.currentMenu, Score.FROM_START,
                    Score.UNLOCK_ITEMS)
            self.score.queueMovie(self.currentInfo)

            if not Colors.noTicker:
                self.ticker.useGuideTt()
        elif userCode == MenuManager.MENU2:
            # Out.
            self.score.queueMovie(self.currentInfo + ' -out',
                    Score.NEW_ANIMATION_ONLY)
            self.score.queueMovie(self.currentInfo + ' -buttons -out',
                    Score.NEW_ANIMATION_ONLY)

            # Book-keeping.
            self.currentMenuCode = MenuManager.MENU2
            self.currentInfo = menuName

            # In/shake.
            self.score.queueMovie('upndown -shake')
            self.score.queueMovie('back -shake')
            self.score.queueMovie(self.currentMenu + ' -shake')
            self.score.queueMovie(self.currentInfo, Score.NEW_ANIMATION_ONLY)
            self.score.queueMovie(self.currentInfo + ' -buttons',
                    Score.NEW_ANIMATION_ONLY)

            if not Colors.noTicker:
                self.score.queueMovie('ticker -out', Score.NEW_ANIMATION_ONLY)
        elif userCode == MenuManager.UP:
            backMenu = self.info[self.currentMenu]['back']
            if backMenu:
                self.score.queueMovie(self.currentMenu + ' -top_out',
                        Score.FROM_START, Score.LOCK_ITEMS)
                self.score.queueMovie(backMenu + ' -bottom_in',
                        Score.FROM_START, Score.UNLOCK_ITEMS)
                self.currentMenu = backMenu
        elif userCode == MenuManager.DOWN:
            moreMenu = self.info[self.currentMenu]['more']
            if moreMenu:
                self.score.queueMovie(self.currentMenu + ' -bottom_out',
                        Score.FROM_START, Score.LOCK_ITEMS)
                self.score.queueMovie(moreMenu + ' -top_in', Score.FROM_START,
                        Score.UNLOCK_ITEMS)
                self.currentMenu = moreMenu
        elif userCode == MenuManager.BACK:
            if self.currentMenuCode == MenuManager.MENU2:
                # Out.
                self.score.queueMovie(self.currentInfo + ' -out',
                        Score.NEW_ANIMATION_ONLY)
                self.score.queueMovie(self.currentInfo + ' -buttons -out',
                        Score.NEW_ANIMATION_ONLY)

                # Book-keeping.
                self.currentMenuCode = MenuManager.MENU1
                self.currentMenuButtons = self.currentCategory + ' -buttons'
                self.currentInfo = self.currentCategory + ' -info'

                # In/shake.
                self.score.queueMovie('upndown -shake')
                self.score.queueMovie(self.currentMenu + ' -shake')
                self.score.queueMovie(self.currentInfo,
                        Score.NEW_ANIMATION_ONLY)
                self.score.queueMovie(self.currentInfo + ' -buttons',
                        Score.NEW_ANIMATION_ONLY)

                if not Colors.noTicker:
                    self.ticker.doIntroTransitions = False
                    self.tickerInAnim.setStartDelay(500)
                    self.score.queueMovie('ticker', Score.NEW_ANIMATION_ONLY)
            elif self.currentMenuCode != MenuManager.ROOT:
                self.itemSelected(MenuManager.ROOT, Colors.rootMenuName)

        # Update back and more buttons.
        if self.info.setdefault(self.currentMenu, {}).get('back'):
            back_state = TextButton.OFF
        else:
            back_state = TextButton.DISABLED

        if self.info[self.currentMenu].get('more'):
            more_state = TextButton.OFF
        else:
            more_state = TextButton.DISABLED

        self.upButton.setState(back_state)
        self.downButton.setState(more_state)

        if self.score.hasQueuedMovies():
            self.score.playQue()
            # Playing new movies might include loading etc., so ignore the FPS
            # at this point.
            self.window.fpsHistory = []

    def showDocInAssistant(self, name):
        url = self.resolveDocUrl(name)
        Colors.debug("Sending URL to Assistant:", url)

        # Start assistant if it's not already running.
        if self.assistantProcess.state() != QProcess.Running:
            app = QLibraryInfo.location(QLibraryInfo.BinariesPath) + QDir.separator()

            if sys.platform == 'darwin':
                app += 'Assistant.app/Contents/MacOS/Assistant'
            else:
                app += 'assistant'

            args = ['-enableRemoteControl']
            self.assistantProcess.start(app, args)
            if not self.assistantProcess.waitForStarted():
                QMessageBox.critical(None, "PyQt Demo",
                        "Could not start %s." % app)
                return

        # Send command through remote control even if the process was just
        # started to activate assistant and bring it to the front.
        cmd_str = QTextStream(self.assistantProcess)
        cmd_str << 'SetSource ' << url << '\n'

    def launchExample(self, name):
        executable = self.resolveExeFile(name)

        process = QProcess(self)
        process.error.connect(self.launchError)

        if sys.platform == 'win32':
            # Make sure it finds the DLLs on Windows.
            env = QProcessEnvironment.systemEnvironment()
            env.insert('PATH',
                    QLibraryInfo.location(QLibraryInfo.BinariesPath) + ';' +
                            env.value('PATH'))
            process.setProcessEnvironment(env)

        if self.info[name]['changedirectory'] != 'false':
            workingDirectory = self.resolveDataDir(name)
            process.setWorkingDirectory(workingDirectory)
            Colors.debug("Setting working directory:", workingDirectory)

        Colors.debug("Launching:", executable)
        process.start(sys.executable, [executable])

    def launchQml(self, name):
        import_path = self.resolveDataDir(name)
        qml = self.resolveQmlFile(name)

        process = QProcess(self)
        process.error.connect(self.launchError)

        env = QProcessEnvironment.systemEnvironment()
        env.insert('QML2_IMPORT_PATH', import_path)
        process.setProcessEnvironment(env)

        executable = QLibraryInfo.location(QLibraryInfo.BinariesPath) + '/qmlscene'
        Colors.debug("Launching:", executable)
        process.start(executable, [qml])

    def launchError(self, error):
        if error != QProcess.Crashed:
            QMessageBox.critical(None, "Failed to launch the example",
                    "Could not launch the example. Ensure that it has been "
                    "built.",
                    QMessageBox.Cancel)

    def init(self, window):
        self.window = window

        # Create div.
        self.createTicker()
        self.createUpnDownButtons()
        self.createBackButton()

        # Create first level menu.
        rootElement = self.contentsDoc.documentElement
        self.createRootMenu(rootElement)

        # Create second level menus.
        level2Menu = self._first_element(rootElement)
        while level2Menu is not None:
            self.createSubMenu(level2Menu)

            # Create leaf menu and example info.
            example = self._first_element(level2Menu)
            while example is not None:
                self.readInfoAboutExample(example)
                self.createLeafMenu(example)
                example = self._next_element(example)

            level2Menu = self._next_element(level2Menu)

    @classmethod
    def _first_element(cls, node):
        return cls._skip_nonelements(node.firstChild)

    @classmethod
    def _next_element(cls, node):
        return cls._skip_nonelements(node.nextSibling)

    @staticmethod
    def _skip_nonelements(node):
        while node is not None and node.nodeType != node.ELEMENT_NODE:
            node = node.nextSibling

        return node

    def readInfoAboutExample(self, example):
        name = example.getAttribute('name')
        if name in self.info:
            Colors.debug("__WARNING: MenuManager.readInfoAboutExample: "
                         "Demo/example with name", name, "appears twice in "
                         "the xml-file!__")

        self.info.setdefault(name, {})['filename'] = example.getAttribute('filename')
        self.info[name]['dirname'] = example.parentNode.getAttribute('dirname')
        self.info[name]['changedirectory'] = example.getAttribute('changedirectory')
        self.info[name]['image'] = example.getAttribute('image')
        self.info[name]['qml'] = example.getAttribute('qml')

    def resolveDir(self, name):
        dirName = self.info[name]['dirname']
        fileName = self.info[name]['filename'].split('/')

        dir = QFileInfo(__file__).dir()
        # To the 'examples' directory.
        dir.cdUp()

        dir.cd(dirName)

        if len(fileName) > 1:
            dir.cd('/'.join(fileName[:-1]))

        # This may legitimately fail if the example is just a simple .py file.
        dir.cd(fileName[-1])

        return dir

    def resolveDataDir(self, name):
        return self.resolveDir(name).absolutePath()

    def resolveExeFile(self, name):
        dir = self.resolveDir(name)

        fileName = self.info[name]['filename'].split('/')[-1]

        pyFile = QFile(dir.path() + '/' + fileName + '.py')
        if pyFile.exists():
            return pyFile.fileName()

        pywFile = QFile(dir.path() + '/' + fileName + '.pyw')
        if pywFile.exists():
            return pywFile.fileName()

        Colors.debug("- WARNING: Could not resolve executable:", dir.path(),
                fileName)
        return '__executable not found__'

    def resolveQmlFile(self, name):
        dir = self.resolveDir(name)

        fileName = self.info[name]['filename'].split('/')[-1]

        qmlFile = QFile(dir.path() + '/' + fileName + '.qml')
        if qmlFile.exists():
            return qmlFile.fileName()

        Colors.debug("- WARNING: Could not resolve QML file:", dir.path(),
                fileName)
        return '__QML not found__'

    def resolveDocUrl(self, name):
        dirName = self.info[name]['dirname']
        fileName = self.info[name]['filename']

        return self.helpRootUrl + dirName.replace('/', '-') + '-' + fileName + '.html'

    def resolveImageUrl(self, name):
        return self.helpRootUrl + 'images/' + name

    def getHtml(self, name):
        return self.getResource(self.resolveDocUrl(name))

    def getImage(self, name):
        imageName = self.info[name]['image']
        fileName = self.info[name]['filename']

        if self.info[name]['qml'] == 'true':
            fileName = 'qml-' + fileName.split('/')[-1]

        if not imageName:
            imageName = fileName + '-example.png'

            if self.getResource(self.resolveImageUrl(imageName)).isEmpty():
                imageName = fileName + '.png'

            if self.getResource(self.resolveImageUrl(imageName)).isEmpty():
                imageName = fileName + 'example.png'

        return self.getResource(self.resolveImageUrl(imageName))

    def createRootMenu(self, el):
        name = el.getAttribute('name')
        self.createMenu(el, MenuManager.MENU1)
        self.createInfo(
                MenuContentItem(el, self.window.mainSceneRoot),
                name + ' -info')

        menuButtonsIn = self.score.insertMovie(name + ' -buttons')
        menuButtonsOut = self.score.insertMovie(name + ' -buttons -out')
        self.createLowLeftButton("Quit", MenuManager.QUIT, menuButtonsIn,
                menuButtonsOut, None)
        self.createLowRightButton("Toggle fullscreen", MenuManager.FULLSCREEN,
                menuButtonsIn, menuButtonsOut, None)

    def createSubMenu(self, el):
        name = el.getAttribute('name')
        self.createMenu(el, MenuManager.MENU2)
        self.createInfo(
                MenuContentItem(el, self.window.mainSceneRoot),
                name + ' -info')

    def createLeafMenu(self, el):
        name = el.getAttribute('name')
        self.createInfo(ExampleContent(name, self.window.mainSceneRoot), name)

        infoButtonsIn = self.score.insertMovie(name + ' -buttons')
        infoButtonsOut = self.score.insertMovie(name + ' -buttons -out')
        self.createLowRightLeafButton("Documentation", 600,
                MenuManager.DOCUMENTATION, infoButtonsIn, infoButtonsOut, None)
        if el.getAttribute('executable') != 'false':
            self.createLowRightLeafButton("Launch", 405, MenuManager.LAUNCH,
                    infoButtonsIn, infoButtonsOut, None)
        elif el.getAttribute('qml') == 'true':
            self.createLowRightLeafButton("Display", 405,
                    MenuManager.LAUNCH_QML, infoButtonsIn, infoButtonsOut,
                    None)

    def createMenu(self, category, type):
        sw = self.window.scene.sceneRect().width()
        xOffset = 15
        yOffset = 10
        maxExamples = Colors.menuCount
        menuIndex = 1
        name = category.getAttribute('name')
        currentNode = self._first_element(category)
        currentMenu = '%s -menu%d' % (name, menuIndex)

        while currentNode is not None:
            movieIn = self.score.insertMovie(currentMenu)
            movieOut = self.score.insertMovie(currentMenu + ' -out')
            movieNextTopOut = self.score.insertMovie(currentMenu + ' -top_out')
            movieNextBottomOut = self.score.insertMovie(currentMenu + ' -bottom_out')
            movieNextTopIn = self.score.insertMovie(currentMenu + ' -top_in')
            movieNextBottomIn = self.score.insertMovie(currentMenu + ' -bottom_in')
            movieShake = self.score.insertMovie(currentMenu + ' -shake')

            i = 0
            while currentNode is not None and i < maxExamples:
                # Create a normal menu button.
                label = currentNode.getAttribute('name')
                item = TextButton(label, TextButton.LEFT, type,
                        self.window.mainSceneRoot)

                item.setRecursiveVisible(False)
                item.setZValue(10)
                ih = item.sceneBoundingRect().height()
                iw = item.sceneBoundingRect().width()
                ihp = ih + 3

                # Create in-animation.
                anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN)
                anim.setDuration(1000 + (i * 20))
                anim.setStartValue(QPointF(xOffset, -ih))
                anim.setKeyValueAt(0.20, QPointF(xOffset, -ih))
                anim.setKeyValueAt(0.50, QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY + (10 * float(i / 4.0))))
                anim.setKeyValueAt(0.60, QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                anim.setKeyValueAt(0.70, QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY + (5 * float(i / 4.0))))
                anim.setKeyValueAt(0.80, QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                anim.setKeyValueAt(0.90, QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY + (2 * float(i / 4.0))))
                anim.setEndValue(QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                movieIn.append(anim)

                # Create out-animation.
                anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT)
                anim.setHideOnFinished(True)
                anim.setDuration(700 + (30 * i))
                anim.setStartValue(QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                anim.setKeyValueAt(0.60, QPointF(xOffset, 600 - ih - ih))
                anim.setKeyValueAt(0.65, QPointF(xOffset + 20, 600 - ih))
                anim.setEndValue(QPointF(sw + iw, 600 - ih))
                movieOut.append(anim)

                # Create shake-animation.
                anim = DemoItemAnimation(item)
                anim.setDuration(700)
                anim.setStartValue(QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                anim.setKeyValueAt(0.55, QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY - i*2.0))
                anim.setKeyValueAt(0.70, QPointF(xOffset - 10, (i * ihp) + yOffset + Colors.contentStartY - i*1.5))
                anim.setKeyValueAt(0.80, QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY - i*1.0))
                anim.setKeyValueAt(0.90, QPointF(xOffset - 2, (i * ihp) + yOffset + Colors.contentStartY - i*0.5))
                anim.setEndValue(QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                movieShake.append(anim)

                # Create next-menu top-out-animation.
                anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT)
                anim.setHideOnFinished(True)
                anim.setDuration(200 + (30 * i))
                anim.setStartValue(QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                anim.setKeyValueAt(0.70, QPointF(xOffset, yOffset + Colors.contentStartY))
                anim.setEndValue(QPointF(-iw, yOffset + Colors.contentStartY))
                movieNextTopOut.append(anim)

                # Create next-menu bottom-out-animation.
                anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT)
                anim.setHideOnFinished(True)
                anim.setDuration(200 + (30 * i))
                anim.setStartValue(QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                anim.setKeyValueAt(0.70, QPointF(xOffset, (maxExamples * ihp) + yOffset + Colors.contentStartY))
                anim.setEndValue(QPointF(-iw, (maxExamples * ihp) + yOffset + Colors.contentStartY))
                movieNextBottomOut.append(anim)

                # Create next-menu top-in-animation.
                anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN)
                anim.setDuration(700 - (30 * i))
                anim.setStartValue(QPointF(-iw, yOffset + Colors.contentStartY))
                anim.setKeyValueAt(0.30, QPointF(xOffset, yOffset + Colors.contentStartY))
                anim.setEndValue(QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                movieNextTopIn.append(anim)

                # Create next-menu bottom-in-animation.
                reverse = maxExamples - i
                anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN)
                anim.setDuration(1000 - (30 * reverse))
                anim.setStartValue(QPointF(-iw, (maxExamples * ihp) + yOffset + Colors.contentStartY))
                anim.setKeyValueAt(0.30, QPointF(xOffset, (maxExamples * ihp) + yOffset + Colors.contentStartY))
                anim.setEndValue(QPointF(xOffset, (i * ihp) + yOffset + Colors.contentStartY))
                movieNextBottomIn.append(anim)

                i += 1
                currentNode = self._next_element(currentNode)

            if currentNode is not None and i == maxExamples:
                # We need another menu, so register for 'more' and 'back'
                # buttons.
                menuIndex += 1
                self.info.setdefault(currentMenu, {})['more'] = '%s -menu%d' % (name, menuIndex)
                currentMenu = '%s -menu%d' % (name, menuIndex)
                self.info.setdefault(currentMenu, {})['back'] = '%s -menu%d' % (name, menuIndex - 1)

    def createLowLeftButton(self, label, type, movieIn, movieOut, movieShake, menuString=""):
        button = TextButton(label, TextButton.RIGHT, type,
                self.window.mainSceneRoot, TextButton.PANEL)
        if menuString:
            button.setMenuString(menuString)
        button.setRecursiveVisible(False)
        button.setZValue(10)

        iw = button.sceneBoundingRect().width()
        xOffset = 15

        # Create in-animation.
        buttonIn = DemoItemAnimation(button, DemoItemAnimation.ANIM_IN)
        buttonIn.setDuration(1800)
        buttonIn.setStartValue(QPointF(-iw, Colors.contentStartY + Colors.contentHeight - 35))
        buttonIn.setKeyValueAt(0.5, QPointF(-iw, Colors.contentStartY + Colors.contentHeight - 35))
        buttonIn.setKeyValueAt(0.7, QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 35))
        buttonIn.setEndValue(QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 26))
        movieIn.append(buttonIn)

        # Create out-animation.
        buttonOut = DemoItemAnimation(button, DemoItemAnimation.ANIM_OUT)
        buttonOut.setHideOnFinished(True)
        buttonOut.setDuration(400)
        buttonOut.setStartValue(QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 26))
        buttonOut.setEndValue(QPointF(-iw, Colors.contentStartY + Colors.contentHeight - 26))
        movieOut.append(buttonOut)

        if movieShake is not None:
            shakeAnim = DemoItemAnimation(button, DemoItemAnimation.ANIM_UNSPECIFIED)
            shakeAnim.setDuration(650)
            shakeAnim.setStartValue(buttonIn.endValue())
            shakeAnim.setKeyValueAt(0.60, buttonIn.endValue())
            shakeAnim.setKeyValueAt(0.70, buttonIn.endValue() + QPointF(-3, 0))
            shakeAnim.setKeyValueAt(0.80, buttonIn.endValue() + QPointF(2, 0))
            shakeAnim.setKeyValueAt(0.90, buttonIn.endValue() + QPointF(-1, 0))
            shakeAnim.setEndValue(buttonIn.endValue())
            movieShake.append(shakeAnim)

    def createLowRightButton(self, label, type, movieIn, movieOut, movieShake):
        item = TextButton(label, TextButton.RIGHT, type,
                self.window.mainSceneRoot, TextButton.PANEL)
        item.setRecursiveVisible(False)
        item.setZValue(10)

        sw = self.window.scene.sceneRect().width()
        xOffset = 70

        # Create in-animation.
        anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN)
        anim.setDuration(1800)
        anim.setStartValue(QPointF(sw, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setKeyValueAt(0.5, QPointF(sw, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setKeyValueAt(0.7, QPointF(xOffset + 535, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setEndValue(QPointF(xOffset + 535, Colors.contentStartY + Colors.contentHeight - 26))
        movieIn.append(anim)

        # Create out-animation.
        anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT)
        anim.setHideOnFinished(True)
        anim.setDuration(400)
        anim.setStartValue(QPointF(xOffset + 535, Colors.contentStartY + Colors.contentHeight - 26))
        anim.setEndValue(QPointF(sw, Colors.contentStartY + Colors.contentHeight - 26))
        movieOut.append(anim)

    def createLowRightLeafButton(self, label, xOffset, type, movieIn, movieOut, movieShake):
        item = TextButton(label, TextButton.RIGHT, type,
                self.window.mainSceneRoot, TextButton.PANEL)
        item.setRecursiveVisible(False)
        item.setZValue(10)

        sw = self.window.scene.sceneRect().width()
        sh = self.window.scene.sceneRect().height()

        # Create in-animation.
        anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN)
        anim.setDuration(1050)
        anim.setStartValue(QPointF(sw, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setKeyValueAt(0.10, QPointF(sw, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setKeyValueAt(0.30, QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setKeyValueAt(0.35, QPointF(xOffset + 30, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setKeyValueAt(0.40, QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setKeyValueAt(0.45, QPointF(xOffset + 5, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setKeyValueAt(0.50, QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 35))
        anim.setEndValue(QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 26))
        movieIn.append(anim)

        # Create out-animation.
        anim = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT)
        anim.setHideOnFinished(True)
        anim.setDuration(300)
        anim.setStartValue(QPointF(xOffset, Colors.contentStartY + Colors.contentHeight - 26))
        anim.setEndValue(QPointF(xOffset, sh))
        movieOut.append(anim)

    def createInfo(self, item, name):
        movie_in = self.score.insertMovie(name)
        movie_out = self.score.insertMovie(name + ' -out')
        item.setZValue(8)
        item.setRecursiveVisible(False)

        xOffset = 230.0
        infoIn = DemoItemAnimation(item, DemoItemAnimation.ANIM_IN)
        infoIn.setDuration(650)
        infoIn.setStartValue(QPointF(self.window.scene.sceneRect().width(), Colors.contentStartY))
        infoIn.setKeyValueAt(0.60, QPointF(xOffset, Colors.contentStartY))
        infoIn.setKeyValueAt(0.70, QPointF(xOffset + 20, Colors.contentStartY))
        infoIn.setKeyValueAt(0.80, QPointF(xOffset, Colors.contentStartY))
        infoIn.setKeyValueAt(0.90, QPointF(xOffset + 7, Colors.contentStartY))
        infoIn.setEndValue(QPointF(xOffset, Colors.contentStartY))
        movie_in.append(infoIn)

        infoOut = DemoItemAnimation(item, DemoItemAnimation.ANIM_OUT)
        infoOut.setCurveShape(QEasingCurve.InQuad)
        infoOut.setDuration(300)
        infoOut.setHideOnFinished(True)
        infoOut.setStartValue(QPointF(xOffset, Colors.contentStartY))
        infoOut.setEndValue(QPointF(-600, Colors.contentStartY))
        movie_out.append(infoOut)

    def createTicker(self):
        if Colors.noTicker:
            return

        movie_in = self.score.insertMovie('ticker')
        movie_out = self.score.insertMovie('ticker -out')
        movie_activate = self.score.insertMovie('ticker -activate')
        movie_deactivate = self.score.insertMovie('ticker -deactivate')

        self.ticker = ItemCircleAnimation()
        self.ticker.setZValue(50)
        self.ticker.hide()

        # Move ticker in.
        qtendpos = 485
        qtPosY = 120
        self.tickerInAnim = DemoItemAnimation(self.ticker,
                DemoItemAnimation.ANIM_IN)
        self.tickerInAnim.setDuration(500)
        self.tickerInAnim.setStartValue(QPointF(self.window.scene.sceneRect().width(), Colors.contentStartY + qtPosY))
        self.tickerInAnim.setKeyValueAt(0.60, QPointF(qtendpos, Colors.contentStartY + qtPosY))
        self.tickerInAnim.setKeyValueAt(0.70, QPointF(qtendpos + 30, Colors.contentStartY + qtPosY))
        self.tickerInAnim.setKeyValueAt(0.80, QPointF(qtendpos, Colors.contentStartY + qtPosY))
        self.tickerInAnim.setKeyValueAt(0.90, QPointF(qtendpos + 5, Colors.contentStartY + qtPosY))
        self.tickerInAnim.setEndValue(QPointF(qtendpos, Colors.contentStartY + qtPosY))
        movie_in.append(self.tickerInAnim)

        # Move ticker out.
        qtOut = DemoItemAnimation(self.ticker, DemoItemAnimation.ANIM_OUT)
        qtOut.setHideOnFinished(True)
        qtOut.setDuration(500)
        qtOut.setStartValue(QPointF(qtendpos, Colors.contentStartY + qtPosY))
        qtOut.setEndValue(QPointF(self.window.scene.sceneRect().width() + 700, Colors.contentStartY + qtPosY))
        movie_out.append(qtOut)

        # Move ticker in on activate.
        qtActivate = DemoItemAnimation(self.ticker)
        qtActivate.setDuration(400)
        qtActivate.setStartValue(QPointF(self.window.scene.sceneRect().width(), Colors.contentStartY + qtPosY))
        qtActivate.setKeyValueAt(0.60, QPointF(qtendpos, Colors.contentStartY + qtPosY))
        qtActivate.setKeyValueAt(0.70, QPointF(qtendpos + 30, Colors.contentStartY + qtPosY))
        qtActivate.setKeyValueAt(0.80, QPointF(qtendpos, Colors.contentStartY + qtPosY))
        qtActivate.setKeyValueAt(0.90, QPointF(qtendpos + 5, Colors.contentStartY + qtPosY))
        qtActivate.setEndValue(QPointF(qtendpos, Colors.contentStartY + qtPosY))
        movie_activate.append(qtActivate)

        # Move ticker out on deactivate.
        qtDeactivate = DemoItemAnimation(self.ticker)
        qtDeactivate.setHideOnFinished(True)
        qtDeactivate.setDuration(400)
        qtDeactivate.setStartValue(QPointF(qtendpos, Colors.contentStartY + qtPosY))
        qtDeactivate.setEndValue(QPointF(qtendpos, 800))
        movie_deactivate.append(qtDeactivate)

    def createUpnDownButtons(self):
        xOffset = 15.0
        yOffset = 450.0

        self.upButton = TextButton("", TextButton.LEFT, MenuManager.UP,
                self.window.mainSceneRoot, TextButton.UP)
        self.upButton.prepare()
        self.upButton.setPos(xOffset, yOffset)
        self.upButton.setState(TextButton.DISABLED)

        self.downButton = TextButton("", TextButton.LEFT, MenuManager.DOWN,
                self.window.mainSceneRoot, TextButton.DOWN)
        self.downButton.prepare()
        self.downButton.setPos(xOffset + 10 + self.downButton.sceneBoundingRect().width(), yOffset)

        movieShake = self.score.insertMovie('upndown -shake')

        shakeAnim = DemoItemAnimation(self.upButton,
                DemoItemAnimation.ANIM_UNSPECIFIED)
        shakeAnim.setDuration(650)
        shakeAnim.setStartValue(self.upButton.pos())
        shakeAnim.setKeyValueAt(0.60, self.upButton.pos())
        shakeAnim.setKeyValueAt(0.70, self.upButton.pos() + QPointF(-2, 0))
        shakeAnim.setKeyValueAt(0.80, self.upButton.pos() + QPointF(1, 0))
        shakeAnim.setKeyValueAt(0.90, self.upButton.pos() + QPointF(-1, 0))
        shakeAnim.setEndValue(self.upButton.pos())
        movieShake.append(shakeAnim)

        shakeAnim = DemoItemAnimation(self.downButton,
                DemoItemAnimation.ANIM_UNSPECIFIED)
        shakeAnim.setDuration(650)
        shakeAnim.setStartValue(self.downButton.pos())
        shakeAnim.setKeyValueAt(0.60, self.downButton.pos())
        shakeAnim.setKeyValueAt(0.70, self.downButton.pos() + QPointF(-5, 0))
        shakeAnim.setKeyValueAt(0.80, self.downButton.pos() + QPointF(-3, 0))
        shakeAnim.setKeyValueAt(0.90, self.downButton.pos() + QPointF(-1, 0))
        shakeAnim.setEndValue(self.downButton.pos())
        movieShake.append(shakeAnim)

    def createBackButton(self):
        backIn = self.score.insertMovie('back -in')
        backOut = self.score.insertMovie('back -out')
        backShake = self.score.insertMovie('back -shake')
        self.createLowLeftButton("Back", MenuManager.ROOT, backIn, backOut,
                backShake, Colors.rootMenuName)