Example #1
0
def showProgress(text, *tasks, **kwargs):
    """
    Show a progress dialog for the given task(s). Each task should be an iterable,
    yielding progress info as (current, max) or (current, max, statusString) tuples.
    Return the last value yielded by the task.

    :param text:
    :type text:
    :param iter:
    :type iter:
    :param cancel:
    :type cancel:
    :return:
    :rtype:
    """
    progress = None
    cancel = kwargs.pop('cancel', None)
    start = time.time()
    shown = False
    with LoaderTimer.stopCtx():

        dialog = MCEProgressDialog(QtGui.qApp.mainWindow)
        if not cancel:
            dialog.setCancelButtonText(None)
        dialog.setWindowTitle(text)
        dialog.setWindowModality(Qt.WindowModal)
        log.info("Starting progress: %d tasks." % len(tasks))
        totalMaximum = len(tasks) * 100
        for i, task in enumerate(tasks):
            log.info("Task #%d", i)
            task = rescaleProgress(task, i*100, i*100+100)
            for progress in task:
                if isinstance(progress, basestring):
                    current = 0
                    maximum = 0
                    status = progress
                elif isinstance(progress, tuple):
                    if len(progress) > 2:
                        current, maximum, status = progress[:3]
                    else:
                        current, maximum = progress
                        status = ""
                else:
                    current = 0
                    maximum = 0
                    status = ""

                dialog.setValue(current)
                if maximum == 0:
                    # Task progress is indeterminate
                    dialog.setMaximum(0)
                else:
                    dialog.setMaximum(totalMaximum)
                dialog.setLabelText(status)
                if time.time() > start + timeBeforeDialog:
                    if not shown:
                        dialog.show()
                        shown = True
                    QtGui.QApplication.processEvents()

                if dialog.wasCanceled():
                    return False

        dialog.reset()
        return progress
Example #2
0
    def writeAllChangesIter(self, requestedRevision=None):
        """
        Write all changes to the root world folder, preserving undo history. The world folder
        becomes the new head node. The previous head node is no longer valid after calling
        writeAllChanges. Specify a revision to only save changes up to and including that
        revision.

        :return:
        :rtype:
        """
        # XXXXX wait for async writes to complete here
        # Progress counts:
        # 0-20:   Orphaned chains
        # 20-100: History nodes

        maxprogress = 100

        if isinstance(requestedRevision, RevisionHistoryNode):
            requestedIndex = self.nodes.index(requestedRevision)
        elif requestedRevision is None:
            requestedIndex = len(self.nodes) - 1
        else:
            requestedIndex = requestedRevision

        orphanChainProgress = 20
        if self.orphanChainIndex is not None:
            # Root node is orphaned - collapse orphan chain into it in reverse order
            orphanNodes = []
            orphanChainNode = self.nodes[self.orphanChainIndex]
            while orphanChainNode is not self.rootNode:
                orphanNodes.append(orphanChainNode)
                orphanChainNode = orphanChainNode.parentNode

            for progress, orphanChainNode in enumProgress(reversed(orphanNodes), 0, 20):
                yield (progress, maxprogress, "Collapsing orphaned chain")

                copyTask = copyToFolderIter(self.rootFolder, orphanChainNode)
                copyTask = rescaleProgress(copyTask, progress, 20/len(orphanNodes))
                for current, _, status in copyTask:
                    yield current, maxprogress, status

            self.nodes[self.orphanChainIndex] = self.rootNode
            self.orphanChainIndex = None

        if requestedIndex == self.rootNodeIndex:
            return  # nothing to do
        elif requestedIndex < self.rootNodeIndex:
            direction = -1
            indexes = xrange(self.rootNodeIndex-1, requestedIndex-1, -1)
        else:
            direction = 1
            indexes = xrange(self.rootNodeIndex+1, requestedIndex+1)
        log.info("writeAllChanges: moving %s", "forwards" if direction == 1 else "backwards")

        for progress, currentIndex in enumProgress(indexes, 20, 80):
            # Write all changes from each node into the initial folder. Save the previous
            # chunk and file data from the initial folder into a reverse revision.

            currentNode = self.nodes[currentIndex]

            reverseFolder = self._createRevisionFolder()
            reverseNode = RevisionHistoryNode(self, reverseFolder, self.nodes[currentIndex - direction])

            reverseNode.differences = self.rootNode.differences
            self.rootNode.differences = currentNode.getChanges()

            copyTask = copyToFolderIter(self.rootFolder, currentNode, reverseNode)
            copyTask = rescaleProgress(copyTask, progress, 80 / len(indexes))
            for current, _, status in copyTask:
                yield current, maxprogress, status

            # xxx look ahead one or more nodes to skip some copies

            reverseNode.setRevisionInfo(self.rootNode.getRevisionInfo())
            reverseNode.readonly = True
            self.rootNode.setRevisionInfo(currentNode.getRevisionInfo())

            # Replace the previousNode with the reverse node, and the currentNode with the rootNode
            self.nodes[currentIndex - direction] = reverseNode
            self.nodes[currentIndex] = self.rootNode
            self.rootNodeIndex = currentIndex

            log.info("Root node now at index %d", currentIndex)

            assert currentNode is not self.rootNode, "Root node appears twice in nodes!"
            currentNode.worldFolder.close()
            currentNode.invalid = True
            shutil.rmtree(currentNode.worldFolder.filename, ignore_errors=True)
Example #3
0
def showProgress(text, *tasks, **kwargs):
    """
    Show a progress dialog for the given task(s). Each task should be an iterable,
    yielding progress info as (current, max) or (current, max, statusString) tuples.
    Return the last value yielded by the task.

    :param text:
    :type text:
    :param iter:
    :type iter:
    :param cancel:
    :type cancel:
    :return:
    :rtype:
    """
    progress = None
    cancel = kwargs.pop('cancel', None)
    start = time.time()
    shown = False
    with LoaderTimer.stopCtx():

        dialog = MCEProgressDialog(QtGui.qApp.mainWindow)
        if not cancel:
            dialog.setCancelButtonText(None)
        dialog.setWindowTitle(text)
        dialog.setWindowModality(Qt.WindowModal)
        log.info("Starting progress: %d tasks." % len(tasks))
        totalMaximum = len(tasks) * 100
        for i, task in enumerate(tasks):
            log.info("Task #%d", i)
            task = rescaleProgress(task, i * 100, i * 100 + 100)
            for progress in task:
                if isinstance(progress, basestring):
                    current = 0
                    maximum = 0
                    status = progress
                elif isinstance(progress, tuple):
                    if len(progress) > 2:
                        current, maximum, status = progress[:3]
                    else:
                        current, maximum = progress
                        status = ""
                else:
                    current = 0
                    maximum = 0
                    status = ""

                dialog.setValue(current)
                if maximum == 0:
                    # Task progress is indeterminate
                    dialog.setMaximum(0)
                else:
                    dialog.setMaximum(totalMaximum)
                dialog.setLabelText(status)
                if time.time() > start + timeBeforeDialog:
                    if not shown:
                        dialog.show()
                        shown = True
                    QtGui.QApplication.processEvents()

                if dialog.wasCanceled():
                    return False

        dialog.reset()
        return progress
Example #4
0
    def writeAllChangesIter(self, requestedRevision=None):
        """
        Write all changes to the root world folder, preserving undo history.
        
        If a revision is requested, the state of the world at that revision will be
        written, otherwise, the last node in the history is used.
        
        All nodes between the root node and the requested node, inclusive, are replaced
        with new nodes. The old nodes are no longer valid. The root node will be placed at
        the position in the nodes list previously occupied by the requested node.

        Parameters
        ----------
        
        requestedRevision: RevisionHistoryNode | int | None
            If given, this specifies the revision to write to the world folder, otherwise
            the most recent revision is written.
        
        Returns
        -------
        
        progress: Iterator[(current, max, status)]
            Progress information for the write-changes task.
            
        """
        # XXXXX wait for async writes to complete here
        # Progress counts:
        # 0-20:   Orphaned chains
        # 20-100: History nodes

        maxprogress = 100

        if isinstance(requestedRevision, RevisionHistoryNode):
            requestedIndex = self.nodes.index(requestedRevision)
        elif requestedRevision is None:
            requestedIndex = len(self.nodes) - 1
        else:
            requestedIndex = requestedRevision

        if self.orphanChainIndex is not None:
            # Root node is orphaned - collapse orphan chain into it in reverse order
            orphanNodes = []
            orphanChainNode = self.nodes[self.orphanChainIndex]
            while orphanChainNode is not self.rootNode:
                orphanNodes.append(orphanChainNode)
                orphanChainNode = orphanChainNode.parentNode

            # Apply each orphaned node onto the root node.
            for progress, orphanChainNode in enumProgress(
                    orphanNodes[::-1], 0, 20):
                yield (progress, maxprogress, "Collapsing orphaned chain")

                copyTask = copyToFolderIter(self.rootFolder, orphanChainNode)
                copyTask = rescaleProgress(copyTask, progress,
                                           progress + 20. / len(orphanNodes))
                for current, _, status in copyTask:
                    yield current, maxprogress, status

            # Root node now replaces the orphan chain's tail in the history.
            # (the nodes ahead and behind of the root node should now point to this node)
            self.nodes[self.orphanChainIndex] = self.rootNode
            self.orphanChainIndex = None

        if requestedIndex == self.rootNodeIndex:
            return  # nothing to do
        elif requestedIndex < self.rootNodeIndex:
            # Nodes behind the root node in the history will be re-reverted and replaced
            # with plain nodes
            direction = -1
            indexes = xrange(self.rootNodeIndex - 1, requestedIndex - 1, -1)
        else:
            # Nodes ahead of the root node will be reverted and replaced with
            # "reversion" nodes.
            direction = 1
            indexes = xrange(self.rootNodeIndex + 1, requestedIndex + 1)

        log.info("writeAllChanges: moving %s",
                 "forwards" if direction == 1 else "backwards")

        for progress, currentIndex in enumProgress(indexes, 20, 100):
            # Write all changes from each node into the initial folder. Save the previous
            # chunk and file data from the initial folder into a reverse revision.

            currentNode = self.nodes[currentIndex]

            reverseFolder = self._createRevisionFolder()
            reverseNode = RevisionHistoryNode(
                self, reverseFolder, self.nodes[currentIndex - direction])

            reverseNode.differences = self.rootNode.differences
            self.rootNode.differences = currentNode.getChanges()

            copyTask = copyToFolderIter(self.rootFolder, currentNode,
                                        reverseNode)
            copyTask = rescaleProgress(copyTask, progress,
                                       progress + 80. / len(indexes))
            for current, _, status in copyTask:
                yield current, maxprogress, status

            # xxx look ahead one or more nodes to skip some copies

            reverseNode.setRevisionInfo(self.rootNode.getRevisionInfo())
            reverseNode.readonly = True
            self.rootNode.setRevisionInfo(currentNode.getRevisionInfo())

            # Replace the previousNode with the reverse node, and the currentNode with the rootNode
            self.nodes[currentIndex - direction] = reverseNode
            self.nodes[currentIndex] = self.rootNode
            self.rootNodeIndex = currentIndex

            log.info("Root node now at index %d", currentIndex)

            assert currentNode is not self.rootNode, "Root node appears twice in nodes!"
            currentNode.worldFolder.close()
            currentNode.invalid = True
            shutil.rmtree(currentNode.worldFolder.filename, ignore_errors=True)
Example #5
0
    def writeAllChangesIter(self, requestedRevision=None):
        """
        Write all changes to the root world folder, preserving undo history.
        
        If a revision is requested, the state of the world at that revision will be
        written, otherwise, the last node in the history is used.
        
        All nodes between the root node and the requested node, inclusive, are replaced
        with new nodes. The old nodes are no longer valid. The root node will be placed at
        the position in the nodes list previously occupied by the requested node.

        Parameters
        ----------
        
        requestedRevision: RevisionHistoryNode | int | None
            If given, this specifies the revision to write to the world folder, otherwise
            the most recent revision is written.
        
        Returns
        -------
        
        progress: Iterator[(current, max, status)]
            Progress information for the write-changes task.
            
        """
        # XXXXX wait for async writes to complete here
        # Progress counts:
        # 0-20:   Orphaned chains
        # 20-100: History nodes

        maxprogress = 100

        if isinstance(requestedRevision, RevisionHistoryNode):
            requestedIndex = self.nodes.index(requestedRevision)
        elif requestedRevision is None:
            requestedIndex = len(self.nodes) - 1
        else:
            requestedIndex = requestedRevision

        if self.orphanChainIndex is not None:
            # Root node is orphaned - collapse orphan chain into it in reverse order
            orphanNodes = []
            orphanChainNode = self.nodes[self.orphanChainIndex]
            while orphanChainNode is not self.rootNode:
                orphanNodes.append(orphanChainNode)
                orphanChainNode = orphanChainNode.parentNode
            
            # Apply each orphaned node onto the root node.
            for progress, orphanChainNode in enumProgress(orphanNodes[::-1], 0, 20):
                yield (progress, maxprogress, "Collapsing orphaned chain")

                copyTask = copyToFolderIter(self.rootFolder, orphanChainNode)
                copyTask = rescaleProgress(copyTask, progress, progress + 20./len(orphanNodes))
                for current, _, status in copyTask:
                    yield current, maxprogress, status

            # Root node now replaces the orphan chain's tail in the history.
            # (the nodes ahead and behind of the root node should now point to this node)
            self.nodes[self.orphanChainIndex] = self.rootNode
            self.orphanChainIndex = None
            
        if requestedIndex == self.rootNodeIndex:
            return  # nothing to do
        elif requestedIndex < self.rootNodeIndex:
            # Nodes behind the root node in the history will be re-reverted and replaced
            # with plain nodes
            direction = -1
            indexes = xrange(self.rootNodeIndex-1, requestedIndex-1, -1)
        else:
            # Nodes ahead of the root node will be reverted and replaced with
            # "reversion" nodes.
            direction = 1
            indexes = xrange(self.rootNodeIndex+1, requestedIndex+1)
            
        log.info("writeAllChanges: moving %s", "forwards" if direction == 1 else "backwards")

        for progress, currentIndex in enumProgress(indexes, 20, 100):
            # Write all changes from each node into the initial folder. Save the previous
            # chunk and file data from the initial folder into a reverse revision.

            currentNode = self.nodes[currentIndex]

            reverseFolder = self._createRevisionFolder()
            reverseNode = RevisionHistoryNode(self, reverseFolder, self.nodes[currentIndex - direction])

            reverseNode.differences = self.rootNode.differences
            self.rootNode.differences = currentNode.getChanges()

            copyTask = copyToFolderIter(self.rootFolder, currentNode, reverseNode)
            copyTask = rescaleProgress(copyTask, progress, progress + 80. / len(indexes))
            for current, _, status in copyTask:
                yield current, maxprogress, status

            # xxx look ahead one or more nodes to skip some copies

            reverseNode.setRevisionInfo(self.rootNode.getRevisionInfo())
            reverseNode.readonly = True
            self.rootNode.setRevisionInfo(currentNode.getRevisionInfo())

            # Replace the previousNode with the reverse node, and the currentNode with the rootNode
            self.nodes[currentIndex - direction] = reverseNode
            self.nodes[currentIndex] = self.rootNode
            self.rootNodeIndex = currentIndex

            log.info("Root node now at index %d", currentIndex)

            assert currentNode is not self.rootNode, "Root node appears twice in nodes!"
            currentNode.worldFolder.close()
            currentNode.invalid = True
            shutil.rmtree(currentNode.worldFolder.filename, ignore_errors=True)
    def writeAllChangesIter(self, requestedRevision=None):
        """
        Write all changes to the root world folder, preserving undo history. The world folder
        becomes the new head node. The previous head node is no longer valid after calling
        writeAllChanges. Specify a revision to only save changes up to and including that
        revision.

        :return:
        :rtype:
        """
        # XXXXX wait for async writes to complete here
        # Progress counts:
        # 0-20:   Orphaned chains
        # 20-100: History nodes

        maxprogress = 100

        if isinstance(requestedRevision, RevisionHistoryNode):
            requestedIndex = self.nodes.index(requestedRevision)
        elif requestedRevision is None:
            requestedIndex = len(self.nodes) - 1
        else:
            requestedIndex = requestedRevision

        orphanChainProgress = 20
        if self.orphanChainIndex is not None:
            # Root node is orphaned - collapse orphan chain into it in reverse order
            orphanNodes = []
            orphanChainNode = self.nodes[self.orphanChainIndex]
            while orphanChainNode is not self.rootNode:
                orphanNodes.append(orphanChainNode)
                orphanChainNode = orphanChainNode.parentNode

            for progress, orphanChainNode in enumProgress(
                    reversed(orphanNodes), 0, 20):
                yield (progress, maxprogress, "Collapsing orphaned chain")

                copyTask = copyToFolderIter(self.rootFolder, orphanChainNode)
                copyTask = rescaleProgress(copyTask, progress,
                                           20 / len(orphanNodes))
                for current, _, status in copyTask:
                    yield current, maxprogress, status

            self.nodes[self.orphanChainIndex] = self.rootNode
            self.orphanChainIndex = None

        if requestedIndex == self.rootNodeIndex:
            return  # nothing to do
        elif requestedIndex < self.rootNodeIndex:
            direction = -1
            indexes = xrange(self.rootNodeIndex - 1, requestedIndex - 1, -1)
        else:
            direction = 1
            indexes = xrange(self.rootNodeIndex + 1, requestedIndex + 1)
        log.info("writeAllChanges: moving %s",
                 "forwards" if direction == 1 else "backwards")

        for progress, currentIndex in enumProgress(indexes, 20, 80):
            # Write all changes from each node into the initial folder. Save the previous
            # chunk and file data from the initial folder into a reverse revision.

            currentNode = self.nodes[currentIndex]

            reverseFolder = self._createRevisionFolder()
            reverseNode = RevisionHistoryNode(
                self, reverseFolder, self.nodes[currentIndex - direction])

            reverseNode.differences = self.rootNode.differences
            self.rootNode.differences = currentNode.getChanges()

            copyTask = copyToFolderIter(self.rootFolder, currentNode,
                                        reverseNode)
            copyTask = rescaleProgress(copyTask, progress, 80 / len(indexes))
            for current, _, status in copyTask:
                yield current, maxprogress, status

            # xxx look ahead one or more nodes to skip some copies

            reverseNode.setRevisionInfo(self.rootNode.getRevisionInfo())
            reverseNode.readonly = True
            self.rootNode.setRevisionInfo(currentNode.getRevisionInfo())

            # Replace the previousNode with the reverse node, and the currentNode with the rootNode
            self.nodes[currentIndex - direction] = reverseNode
            self.nodes[currentIndex] = self.rootNode
            self.rootNodeIndex = currentIndex

            log.info("Root node now at index %d", currentIndex)

            assert currentNode is not self.rootNode, "Root node appears twice in nodes!"
            currentNode.worldFolder.close()
            currentNode.invalid = True
            shutil.rmtree(currentNode.worldFolder.filename, ignore_errors=True)