Progress bar in Sublime Text with Python

As another alternative solution you can use status bar. When you you set the status bar message the previous text is cleared. Package control also uses status bar while installing packages.

Example:

Sublime text progress bar using status bar

import sublime, sublime_plugin
import time

class ExampleCommand(sublime_plugin.WindowCommand):
    def run(self, args):
        sublime.set_timeout_async(self.test,1)

    def test(self):
        i=80
        while i <= 100:
            sublime.status_message('%03.2f %%' % i)
            time.sleep(0.15)
            i+=0.25
        sublime.status_message('100% Stackoverflow!')

You can create a visual progress bar using:

  • the mdpopups library
  • sublime.set_timeout or sublime.set_timeout_async ( see: Sublime Module )

 



Demo:

Demo

 



Code:

@ GitHub

( run the plugin by typing Progress Bar Demo @ the Command Palette )

   



Notes:

There is a css file that controls the style of mdpopups. For some reason, the color property isn't having any effect.

Also, mdpopups.show_popup's location parameter takes -1 for the popup to be set at the caret position. Otherwise, I'm not sure how location affects the popup, since it only takes a single integer value.  

 

I've inquired about these issues at the following thread:

[Proof Of Concept] Visual Progress Bar

 


Taking a look at sublime.py we see that the flush method actually does nothing:

class _LogWriter:
    def flush(self):
        pass

    def write(self, s):
        sublime_api.log_message(s)

sys.stdout = _LogWriter()
sys.stderr = _LogWriter()

However I would not recommend to use the console for user outputs anyway. Usually you use output panels/views or status messages.

Status messages are easier to use, but less powerful. sergioFC demonstrated this in his answer.

This demonstrates how to use an output panel. It is very flexible, but you must write your own text command to insert the text. This is necessary, because you need an edit object to change the content of the view.

import sublime
import sublime_plugin


class MyInsertProgressBarCommand(sublime_plugin.TextCommand):
    def run(self, edit, value):
        view = self.view
        width, _ = view.viewport_extent()
        em_width = view.em_width()
        # the number of columns are the width divided by the width of a char
        # subtract two to add a little border
        columns = int(width / em_width) - 2

        # subtract two, because we surround it with [ and ]
        bar_length = columns - 2
        # calculate the size of the filled and the remaining part
        filled_length = int(bar_length * value / 100)
        remaining_length = bar_length - filled_length
        # assemble the string for the progress bar
        text = "[{0}{1}]\n".format("=" * filled_length, "." * remaining_length)
        # add the text for the percentages
        if value >= 100:
            percentage_text = "finished!"
        else:
            percentage_text = "{:3.2f} %".format(value)
        text += " " * (columns - len(percentage_text)) + percentage_text

        # replace the content of the view
        view.replace(edit, sublime.Region(0, view.size()), text)
        # reset sels
        view.sel().clear()
        view.sel().add(sublime.Region(0, 0))


class ProgressBarCommand(sublime_plugin.WindowCommand):
    def run(self):
        self.window.create_output_panel("progess_bar")
        self.window.run_command("show_panel", {"panel": "output.progess_bar"})

        def test_progress_bar():
            import random
            test_progress_bar.value += 2 * random.random()
            if test_progress_bar.value >= 100:
                self.finish_progress()
                return
            self.show_progress(test_progress_bar.value)

            sublime.set_timeout(test_progress_bar, 100)
        test_progress_bar.value = 0

        sublime.set_timeout_async(test_progress_bar, 1)

    def show_progress(self, progess):
        view = self.window.find_output_panel("progess_bar")
        view.run_command("my_insert_progress_bar", {"value": progess})

    def finish_progress(self):
        self.show_progress(100)
        sublime.set_timeout(self._destroy, 5000)

    def _destroy(self):
        self.window.destroy_output_panel("progess_bar")

The output:

Progress bar