Progress bar in Sublime Text with Python - python

I am using the powerful Sublime Text 3 editor on MacOSX to run python codes. I want to display a progress bar of a for loop, and the following command:
sys.stdout.write('\rProgress : %03.2f %%' % (100*float(i)/(N)))
sys.flush()
does not clear the previously printed line in the output window as expected (\r) but produces N lines:
Progress : 0.25 %
Progress : 0.50 %
Progress : 0.75 %
Progress : 1.00 %
Progress : 1.25 %
Progress : 1.50 %
Progress : 1.75 %
Progress : 2.00 %
Progress : 2.25 %
Progress : 2.50 %
...
Which is not really nice to read – I conclude that the output window might be read-only.
Does anyone have suggestions to improve the use of progress bars in Sublime Text?

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:
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!')

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:

You can create a visual progress bar using:
the mdpopups library
sublime.set_timeout or sublime.set_timeout_async ( see: Sublime Module )
 
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
 

Unfortunately, this is not possible in Sublime's output panel. The panel is not a true console or terminal, and among other differences does not interpret escape sequences such as \r and \b (\n is interpreted correctly, however). If you want to see how exactly it works, install PackageResourceViewer, then open Packages/Default/exec.py.
In order to get this to work, you'll need to create a new build system to run it in Terminal. Due to the vagaries of OS X, you'll need to create two files. The first is a shell script:
#!/bin/sh
osascript -e '
on run parameters
tell application "Terminal"
activate
do script with command "/path/to/python " & parameters
end tell
end run
' $#
Change /path/to with your actual path to python (or python3). Save it wherever you want as PythonTerminal.sh. Next, select Tools -> Build System -> New Build System and paste in the following:
{
"cmd": ["/bin/sh /path/to/Python3Terminal.sh \"$file\""],
"file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
"selector": "source.python",
"shell": true
}
Again, change /path/to to the actual path to PythonTerminal.sh. Save the file as Packages/User/PythonTerminal.sublime-build (it should automatically open the correct directory when saving).
Finally, select Tools -> Build System -> PythonTerminal, switch to your Python file, and build with ⌘B. A new Terminal window will open and your progress bar should run.

What you might be looking for is a way to keep the output from consuming multiple lines. You can print \b (the backspace character) as many times as there were previously printed characters. I wrote this as an example:
(Python 2.7.6)
from __future__ import print_function
import time, sys
for i in range(1, 6):
print(i, end='')
sys.stdout.flush()
time.sleep(0.5)
print('\b', end='')
Try running that and you can adapt it to your needs.

You can use progressbar library. located here : https://pypi.python.org/pypi/progressbar/2.3-dev
Also you can install it from easy_install just type : easy_install progressbar
Example to use :
if you want simple progressbar with out information about function :
from progressbar import *
from time import sleep
progress = ProgressBar()
for i in progress(range(80)):
sleep(0.01)
else if you want progressbar with information about function :
from progressbar import *
from time import sleep
widgets = ['Something: ', Percentage(), ' ', Bar(marker=RotatingMarker()),
' ', ETA(), ' ', FileTransferSpeed()]
pbar = ProgressBar(widgets=widgets, maxval=10000000).start()
for i in range(1000000):
# do something
pbar.update(10*i+1)
sleep(0.000001)
pbar.finish()

Related

Cannot write white spaces in a Alert object in ipyvuetify

I try to display n progress bar in an Alert (instead of using the vuetify progress element I know).
I created a little script that does a nice output as a str :
def update_progress(progress, msg='Progress', bar_length=30):
plain_char = '█'
empty_char = ' '
progress = float(progress)
block = int(round(bar_length * progress))
text = f'{msg}: |{plain_char * block + empty_char * (bar_length - block)}| {progress *100:.1f}%'
return text
when I print the output, I get the following :
and when I insert it as a child in an Alert component :
First problem, the spaces have been trimed, second problem it's changing the block ascii character to a wider one.
Is it normal and how can I avoid it?
As referenced in the github repository of the ipyvuetify lib (#103), it's a 'feature' of HTML: https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Whitespace
Using the pre tag will preserve whitespace:
v.Html(
tag='pre',
children=['a mess age'],
class_='info--text',
style_='font-family:roboto'
)
The class and style traitlets are here to keep the formating of the Alert (need to be adapted to your own type_)

is there an easy way to add a progress bar/counter where I can add a line to increment it every so often - Not Timed

I have a script that is basically complete. I'd like to add some sort of a progress bar to it, instead of printing out each step as it passes by
is there anything that will let me do this.
setup a progress widget/counter/loop
give it a command function to increment
do some script
add the code to advance/increment the progress bar
do some more script
add the code to advance/increment the progress bar
do some more script
add the code to advance/increment the progress bar
do some more script
add the code to advance/increment the progress bar
also, can you please give me an example of some sort
I've looked at 3 or 4 different "progress bar" type libraries, and none give an example of doing it this way
all of the examples I seem to find want to do it by time or by byte size for downloading files
There is a number of progress bars in PIP, I recommend ezprogress if you run python3.
from ezprogress.progressbar import ProgressBar
import time
# Number of steps in your total script
steps_needed = 100
current_step = 0
# setup progress bar
pb = ProgressBar(steps_needed, bar_length=100)
pb.start()
# Do what your script wants
...
# Increment counter
current_step += 1
pb.update(current_step)
# Do what your script wants
...
# When you are done you can force the progress bar to finish
PB.finished()
The progress bar did not support turning off time estimation, however it is now possible in the newest version, just upgrade from PIP.
To turn off time estimation the progress bar just needs to be started with the parameter no_time=True like in the code below:
pb = ProgressBar(steps_needed, bar_length=100, no_time=True)
create your progressbar.py module
import sys
import copy
currentProgressCnt = 0
progressCntMax = 0 #
progressBarWidth = 50 # in chars
scaleFctr = 0.0
tasksToDo = []
class ProgressIndicator:
def showProgress(self):
global progressCntMax
global currentProgressCnt
cr = "\r"
progressChar = '#'
fillChar = '.'
progressBarDone = currentProgressCnt*progressChar*scaleFctr
progressBarRemain = fillChar*(progressCntMax - currentProgressCnt)*scaleFctr
percent = str(int((float(currentProgressCnt)/float(progressCntMax))*100)) + " % completed "
taskId = '(' + tasksToDo[currentProgressCnt - 1] + ') '
quote = str(currentProgressCnt) + '/' + str(progressCntMax) + ' '
sys.stdout.write(cr + progressBarDone + progressBarRemain + ' ' + percent + taskId + quote)
sys.stdout.flush()
if currentProgressCnt == progressCntMax:
print
def incProgress(self):
global currentProgressCnt
currentProgressCnt += 1
def setLastStep(self, size):
global progressCntMax, scaleFctr
progressCntMax = size
scaleFctr = progressBarWidth / progressCntMax
def setTaskList(self, taskList):
global tasksToDo
tasksToDo = copy.copy(taskList)
self.setLastStep(len(tasksToDo))
in main, use the ProgressIndicator class like this:
from progressbar import ProgressIndicator
import time
import datetime
#########################################
### MAIN ###
### SIMULATION ###
#########################################
# your procedure list you have to run
toDoList = ['proc1', 'proc2', 'proc3', 'proc1', 'proc4', 'proc5',
'proc6', 'proc7', 'proc21', 'proc32', 'proc43', 'proc51',
'proc4', 'proc65', 'proc76', 'proc87']
progressLine = ProgressIndicator() # create your indicator
progressLine.setTaskList(toDoList) # set params
# your main work
i = 0; lastTask = len(toDoList)
# log the start
startTime = str(datetime.datetime.now())
print ( startTime + " main started")
while i < lastTask:
# run your task list here
time.sleep(1) # simulating your toDoList[i]() run
i += 1
progressLine.incProgress() # use when task done, incrase progress
progressLine.showProgress() # use for update display
# work is done, log the end
endTime = str(datetime.datetime.now())
print ( endTime + " main finished")

py2exe SytaxError: invalid syntax (asyncsupport.py, line22) [duplicate]

This command works fine on my personal computer but keeps giving me this error on my work PC. What could be going on? I can run the Char_Limits.py script directly in Powershell without a problem.
error: compiling 'C:\ProgramData\Anaconda2\lib\site-packages\jinja2\asyncsupport.py' failed
SyntaxError: invalid syntax (asyncsupport.py, line 22)
My setup.py file looks like:
from distutils.core import setup
import py2exe
setup (console=['Char_Limits.py'])
My file looks like:
import xlwings as xw
from win32com.client import constants as c
import win32api
"""
Important Notes: Header row has to be the first row. No columns without a header row. If you need/want a blank column, just place a random placeholder
header value in the first row.
Product_Article_Number column is used to determine the number of rows. It must be populated for every row.
"""
#functions, hooray!
def setRange(columnDict, columnHeader):
column = columnDict[columnHeader]
rngForFormatting = xw.Range((2,column), (bttm, column))
cellReference = xw.Range((2,column)).get_address(False, False)
return rngForFormatting, cellReference
def msg_box(message):
win32api.MessageBox(wb.app.hwnd, message)
#Character limits for fields in Hybris
CharLimits_Fields = {"alerts":500, "certifications":255, "productTitle":300,
"teaserText":450 , "includes":1000, "compliance":255, "disclaimers":9000,
"ecommDescription100":100, "ecommDescription240":240,
"internalKeyword":1000, "metaKeywords":1000, "metaDescription":1000,
"productFeatures":7500, "productLongDescription":1500,"requires":500,
"servicePlan":255, "skuDifferentiatorText":255, "storage":255,
"techDetailsAndRefs":12000, "warranty":1000}
# Fields for which a break tag is problematic.
BreakTagNotAllowed = ["ecommDescription100", "ecommDescription240", "productTitle",
"skuDifferentiatorText"]
app = xw.apps.active
wb = xw.Book(r'C:\Users\XXXX\Documents\Import File.xlsx')
#identifies the blanket range of interest
firstCell = xw.Range('A1')
lstcolumn = firstCell.end("right").column
headers_Row = xw.Range((1,1), (1, lstcolumn)).value
columnDict = {}
for column in range(1, len(headers_Row) + 1):
header = headers_Row[column - 1]
columnDict[header] = column
try:
articleColumn = columnDict["Product_Article_Number"]
except:
articleColumn = columnDict["Family_Article_Number"]
firstCell = xw.Range((1,articleColumn))
bttm = firstCell.end("down").row
wholeRange = xw.Range((1,1),(bttm, lstcolumn))
wholeRangeVal = wholeRange.value
#Sets the font and deletes previous conditional formatting
wholeRange.api.Font.Name = "Arial Unicode MS"
wholeRange.api.FormatConditions.Delete()
for columnHeader in columnDict.keys():
if columnHeader in CharLimits_Fields.keys():
rng, cellRef = setRange(columnDict, columnHeader)
rng.api.FormatConditions.Add(2,3, "=len(" + cellRef + ") >=" + str(CharLimits_Fields[columnHeader]))
rng.api.FormatConditions(1).Interior.ColorIndex = 3
if columnHeader in BreakTagNotAllowed:
rng, cellRef = setRange(columnDict, columnHeader)
rng.api.FormatConditions.Add(2,3, '=OR(ISNUMBER(SEARCH("<br>",' + cellRef + ')), ISNUMBER(SEARCH("<br/>",' + cellRef + ")))")
rng.api.FormatConditions(2).Interior.ColorIndex = 6
searchResults = wholeRange.api.Find("~\"")
if searchResults is not None:
msg_box("There's a double quote in this spreadsheet")
else:
msg_box("There are no double quotes in this spreadsheet")
# app.api.FindFormat.Clear
# app.api.FindFormat.Interior.ColorIndex = 3
# foundRed = wholeRange.api.Find("*", SearchFormat=True)
# if foundRed is None:
# msg_box("There are no values exceeding character limits")
# else:
# msg_box("There are values exceeding character limits")
# app.api.FindFormat.Clear
# app.api.FindFormat.Interior.ColorIndex = 6
# foundYellow = wholeRange.api.Find("*", SearchFormat=True)
# if foundYellow is None:
# msg_box("There are no break tags in this spreadsheet")
# else:
# msg_box("There are break tags in this spreadsheet")
Note:
If you are reading this, I would try Santiago's solution first.
The issue:
Looking at what is likely at line 22 on the github package:
async def concat_async(async_gen):
This is making use of the async keyword which was added in python 3.5, however py2exe only supports up to python 3.4. Now jinja looks to be extending the python language in some way (perhaps during runtime?) to support this async keyword in earlier versions of python. py2exe cannot account for this language extension.
The Fix:
async support was added in jinja2 version 2.9 according to the documentation. So I tried installing an earlier version of jinja (version 2.8) which I downloaded here.
I made a backup of my current jinja installation by moving the contents of %PYTHONHOME%\Lib\site-packages\jinja2 to some other place.
extract the previously downloaded tar.gz file and install the package via pip:
cd .\Downloads\dist\Jinja2-2.8 # or wherever you extracted jinja2.8
python setup.py install
As a side note, I also had to increase my recursion limit because py2exe was reaching the default limit.
from distutils.core import setup
import py2exe
import sys
sys.setrecursionlimit(5000)
setup (console=['test.py'])
Warning:
If whatever it is you are using relies on the latest version of jinja2, then this might fail or have unintended side effects when actually running your code. I was compiling a very simple script.
I had the same trouble coding in python3.7. I fixed that adding the excludes part to my py2exe file:
a = Analysis(['pyinst_test.py'],
#...
excludes=['jinja2.asyncsupport','jinja2.asyncfilters'],
#...)
I took that from: https://github.com/pyinstaller/pyinstaller/issues/2393

Remove tkinter text default binding

I'm making a simple tkinter Text editor, but i want all default bindings of the Text widget removed if possible.
For example when i press Ctrl + i it inserts a Tab character by default.
I made an event binding that prints how many lines are in the text box, I set the event binding to Ctrl + i as well.
When i run it, It prints the number of lines inside the text box, but also inserts a tab character.
I want to know how i can Overwrite the default bindings, or learna way how to remove all the default bindings.
Heres my code btw:
from tkinter import *
class comd: # Contains primary commands
# Capital Rule ----------------------------
# G = Get | I = Insert | D = Draw | S = Set
# -----------------------------------------
def Ggeo(self): # Get Geometry (Get window geometry)
x = root.winfo_width()
y = root.winfo_height()
print("Current Window Geometry")
print(str(x) + " x " +str(y))
def Idum(self): # Insters "Dummy Insert"
import tkinter as tkin
tbox.insert(INSERT, "Dummy Insert")
def Ilim(self): # Prints How many lines are in
info = int(tbox.index('end-1c').split('.')[0])
print(info)
root = Tk()
root.geometry("885x600-25-25")
tbox = Text(root, font=("Courier","14","bold"))
tbox.pack(expand = True , fill = BOTH)
# Problem here --------------------
tbox.bind("<Control-i>", comd.Ilim)
# ---------------------------------
mainloop()
You can overwrite a binding by having your function return the string "break". For example:
def Ilim(self): # Prints How many lines are in
info = int(tbox.index('end-1c').split('.')[0])
print(info)
return "break"
If you want to completely remove all bindings (including the bindings that allow you to insert characters), that can be easily accomplished. All of the bindings are associated with a "bind tag" (or "bindtag"). If you remove the bindtag, you remove the bindings.
For example, this removes all of the default bindings:
bindtags = list(tbox.bindtags())
bindtags.remove("Text")
tbox.bindtags(tuple(bindtags))
For more information on bind tags, see this answer: https://stackoverflow.com/a/11542200/7432

Get progress by Esky tasks

I'm using Esky with my frozen app. It has the following properties and methods
are available on the Esky class:
app.version: the current best available version.
app.active_version: the currently-executing version, or None
if the esky isn't for the current app.
app.find_update(): find the best available update, or None
if no updates are available.
app.fetch_version(v): fetch the specified version into local storage.
app.install_version(v): install and activate the specified version.
Now, that's nice and all, but I want to show the progress of the download task in my Gui.
How can I achieve that?
wxPython has wrapped Esky in their own SoftwareUpdate method:
https://github.com/wxWidgets/wxPython/blob/master/wx/lib/softwareupdate.py
In their implementation, the application checks for new versions, and asks the user if they'd like to update (using wx GUI for interaction). If the user chooses to update, the code simply calls esky's auto_update() method to handle the rest, but they provide it with an _updateProgress method which updates a progress bar and provides messages indicating Esky's progress:
self._esky.auto_update(self._updateProgress)
...
def _updateProgress(self, status):
# Show progress of the download and install. This function is passed to Esky
# functions to use as a callback.
if self._pd is None and status.get('status') != 'done':
self._pd = wx.ProgressDialog('Software Update', ' '*40,
style=wx.PD_CAN_ABORT|wx.PD_APP_MODAL,
parent=self._parentWindow)
self._pd.Update(0, '')
if self._parentWindow:
self._pd.CenterOnParent()
simpleMsgMap = { 'searching' : 'Searching...',
'retrying' : 'Retrying...',
'ready' : 'Download complete...',
'installing' : 'Installing...',
'cleaning up' : 'Cleaning up...',}
if status.get('status') in simpleMsgMap:
self._doUpdateProgress(True, simpleMsgMap[status.get('status')])
elif status.get('status') == 'found':
self._doUpdateProgress(True, 'Found version %s...' % status.get('new_version'))
elif status.get('status') == 'downloading':
received = status.get('received')
size = status.get('size')
currentPercentage = 1.0 * received / size * 100
if currentPercentage > 99.5:
self._doUpdateProgress(False, "Unzipping...", int(currentPercentage))
else:
self._doUpdateProgress(False, "Downloading...", int(currentPercentage))
elif status.get('status') == 'done':
if self._pd:
self._pd.Destroy()
self._pd = None
wx.Yield()
def _doUpdateProgress(self, pulse, message, value=0):
if pulse:
keepGoing, skip = self._pd.Pulse(message)
else:
keepGoing, skip = self._pd.Update(value, message)
if not keepGoing: # user pressed the cancel button
self._pd.Destroy()
self._pd = None
raise UpdateAbortedError()
The code above was take directly from https://github.com/wxWidgets/wxPython/blob/master/wx/lib/softwareupdate.py.
This feature is documented in the Esky source, file: init.py, line: 689.
The code itself shows what your callback can expect to see throughout an update. Here are some excerpts where your callback would be called:
callback({"status":"searching"})
callback({"status":"found", "new_version":version})
callback({"status":"installing", "new_version":version})
for status in self.sudo_proxy.fetch_version_iter(version):
if callback is not None:
callback(status)
callback({"status":"cleaning up"})
callback({"status":"cleaning up"})
callback({"status":"error","exception":e})
callback({"status":"done"})
Not properly documenated, there is the fetch_version_iter generator function:
fetch_version_iter: like fetch_version but yielding progress updates
during its execution
It yields the following values:
yield {"status": "downloading",
"size": infile_size,
"received": partfile.tell(),
}
yield {"status":"retrying","size":None}
yield {"status":"ready","path":name}
Also, you can get the filename like that:
app.version_finder.version_graph.get_best_path(app.version,v)

Categories