Geotagging JPEGs with pyexiv2 - python

I am geotagging JPEGs using the pyexiv2 Python module using code I found in another SO answer (see: What is the best way to geotag jpeg-images using Python?) and I have a question about the GPSTag value.
The code given in the answer has the following lines:
exiv_image["Exif.Image.GPSTag"] = 654
exiv_image["Exif.GPSInfo.GPSMapDatum"] = "WGS-84"
exiv_image["Exif.GPSInfo.GPSVersionID"] = '2 0 0 0'
I have looked at the Exiv2 documentation and found descriptions of GPSTag, GPSMapDatum, and GPSVersionID but am still confused about the value for GPSTag.
From the documentation it says:
A pointer to the GPS Info IFD. The Interoperability structure of the GPS Info IFD, like that of Exif IFD, has no image data.
This description does not really explain how to determine what value to use and I have not been able to find a better description of GPSTag online.
So my questions are:
Given a new image, how do you determine the value of Exif.Image.GPSTag?
Why is the code sample using a value of 654 (this may be answered by question one)?
Thanks for your help.

Best way to geotag photos using pyexiv2 is definitely with my program, GottenGeography ;-)
But seriously though, if you're wanting to access GPS data from pyexiv2, that code looks like this:
GPS = 'Exif.GPSInfo.GPS'
try:
self.latitude = dms_to_decimal(
*self.exif[GPS + 'Latitude'].value +
[self.exif[GPS + 'LatitudeRef'].value]
)
self.longitude = dms_to_decimal(
*self.exif[GPS + 'Longitude'].value +
[self.exif[GPS + 'LongitudeRef'].value]
)
except KeyError:
pass
try:
self.altitude = float(self.exif[GPS + 'Altitude'].value)
if int(self.exif[GPS + 'AltitudeRef'].value) > 0:
self.altitude *= -1
except KeyError:
pass
And writing looks like this:
self.exif[GPS + 'AltitudeRef'] = '0' if self.altitude >= 0 else '1'
self.exif[GPS + 'Altitude'] = Fraction(self.altitude)
self.exif[GPS + 'Latitude'] = decimal_to_dms(self.latitude)
self.exif[GPS + 'LatitudeRef'] = 'N' if self.latitude >= 0 else 'S'
self.exif[GPS + 'Longitude'] = decimal_to_dms(self.longitude)
self.exif[GPS + 'LongitudeRef'] = 'E' if self.longitude >= 0 else 'W'
self.exif[GPS + 'MapDatum'] = 'WGS-84'
With these support functions:
class Fraction(fractions.Fraction):
"""Only create Fractions from floats.
>>> Fraction(0.3)
Fraction(3, 10)
>>> Fraction(1.1)
Fraction(11, 10)
"""
def __new__(cls, value, ignore=None):
"""Should be compatible with Python 2.6, though untested."""
return fractions.Fraction.from_float(value).limit_denominator(99999)
def dms_to_decimal(degrees, minutes, seconds, sign=' '):
"""Convert degrees, minutes, seconds into decimal degrees.
>>> dms_to_decimal(10, 10, 10)
10.169444444444444
>>> dms_to_decimal(8, 9, 10, 'S')
-8.152777777777779
"""
return (-1 if sign[0] in 'SWsw' else 1) * (
float(degrees) +
float(minutes) / 60 +
float(seconds) / 3600
)
def decimal_to_dms(decimal):
"""Convert decimal degrees into degrees, minutes, seconds.
>>> decimal_to_dms(50.445891)
[Fraction(50, 1), Fraction(26, 1), Fraction(113019, 2500)]
>>> decimal_to_dms(-125.976893)
[Fraction(125, 1), Fraction(58, 1), Fraction(92037, 2500)]
"""
remainder, degrees = math.modf(abs(decimal))
remainder, minutes = math.modf(remainder * 60)
return [Fraction(n) for n in (degrees, minutes, remainder * 60)]
Although I am currently working on an alternative to pyexiv2 that uses GObject introspection to access the exiv2 library much more directly, called GExiv2, and would love to have some feedback on it. Both gexiv2 and pyexiv2 are wrappers around the same exiv2 library, but the difference is that pyexiv2 is a very large project with lots of glue, only works in python, and is on the brink of abandonment*; whereas gexiv2 is light and nimble, accessible from any programming language, and is well maintained thanks to it's use by Shotwell.
Hope this helps!
* pyexiv2's author, Olivier Tilloy, has asked me for help with maintainership as he no longer has much time

My version, a little lengthy...
from fractions import Fraction
import pyexiv2
try:
metadata = pyexiv2.metadata.ImageMetadata(image_file)
metadata.read();
thumb = metadata.exif_thumbnail
try:
latitude = metadata.__getitem__("Exif.GPSInfo.GPSLatitude")
latitudeRef = metadata.__getitem__("Exif.GPSInfo.GPSLatitudeRef")
longitude = metadata.__getitem__("Exif.GPSInfo.GPSLongitude")
longitudeRef = metadata.__getitem__("Exif.GPSInfo.GPSLongitudeRef")
latitude = str(latitude).split("=")[1][1:-1].split(" ");
latitude = map(lambda f: str(float(Fraction(f))), latitude)
latitude = latitude[0] + u"\u00b0" + latitude[1] + "'" + latitude[2] + '"' + " " + str(latitudeRef).split("=")[1][1:-1]
longitude = str(longitude).split("=")[1][1:-1].split(" ");
longitude = map(lambda f: str(float(Fraction(f))), longitude)
longitude = longitude[0] + u"\u00b0" + longitude[1] + "'" + longitude[2] + '"' + " " + str(longitudeRef).split("=")[1][1:-1]
latitude_value = dms_to_decimal(*metadata.__getitem__("Exif.GPSInfo.GPSLatitude").value + [metadata.__getitem__("Exif.GPSInfo.GPSLatitudeRef").value]);
longitude_value = dms_to_decimal(*metadata.__getitem__("Exif.GPSInfo.GPSLongitude").value + [metadata.__getitem__("Exif.GPSInfo.GPSLongitudeRef").value]);
print "--- GPS ---"
print "Coordinates: " + latitude + ", " + longitude
print "Coordinates: " + str(latitude_value) + ", " + str(longitude_value)
print "--- GPS ---"
except Exception, e:
print "No GPS Information!"
#print e
# Check for thumbnail
if(thumb.data == ""):
print "No thumbnail!"
except Exception, e:
print "Error processing image..."
print e;

Related

OpenPyXl how to format cell as date

I have a problem with a report I am reading with openpyxl. Sometimes cell formats in a file I receive are not correct, and instead of "10.03.2020" (cell format Date) I get "43900,88455" (cell format General).
I tried google, openpyxl documentation and StackOverflow but that did not bring me any closer to the solution. Would you be able to help and advise how to switch cell format to Short Date, please?
Below did not work, I tried many other ideas but still in a dead end. I need correct dates for other operations within this script.
def sanitizeDates(self):
# pass
for i in range(3, self.fileLength-1):
self.mainWs.cell(i, 4).number_format = 'dd/mm/yyyy'
self.mainWs.cell(i, 16).number_format = 'dd/mm/yyyy'
Copy comment: So I have tried
print("Cell (4) type is: " + str(type(self.mainWs.cell(i, 4).value)) + " and current value is " + str(self.mainWs.cell(i, 4).value))
print("Cell (16) type is: " + str(type(self.mainWs.cell(i, 16).value)) + " and current value is " + str(self.mainWs.cell(i, 16).value))
that results in Cell (4) type is: <class 'datetime.datetime'>
and current value is 2020-03-10 22:41:41
Cell (16) type is: <class 'float'> and current value is 43900.9475
Excel displays it as "General" = "43900,88455"
So, I have figured this out after all. #stovfl thanks for your hints, this brought me to an idea what to look for and as a result, how to solve my issue.
Original thread on StackOverflow is available here: https://stackoverflow.com/a/29387450/5210159
Below is a working code:
def sanitizeDates(self):
for i in range(3, self.fileLength - 1):
# Check which cells should be sanitized and proceed
if isinstance(self.mainWs.cell(i, 4).value, float) and isinstance(self.mainWs.cell(i, 16).value, float):
print(str(self.mainWs.cell(i, 4).value) + " / " + str(self.mainWs.cell(i, 16).value) + " -> " + str(self.convertDate(self.mainWs.cell(i, 4).value)) + " / " + self.convertDate(self.mainWs.cell(i, 16).value))
self.mainWs.cell(i, 4).value = self.convertDate(self.mainWs.cell(i, 4).value)
self.mainWs.cell(i, 16).value = self.convertDate(self.mainWs.cell(i, 16).value)
elif isinstance(self.mainWs.cell(i, 4).value, float):
print(str(self.mainWs.cell(i, 4).value) + " -> " + str(self.convertDate(self.mainWs.cell(i, 4).value)))
self.mainWs.cell(i, 4).value = self.convertDate(self.mainWs.cell(i, 4).value)
elif isinstance(self.mainWs.cell(i, 16).value, float):
print(str(self.mainWs.cell(i, 16).value) + " -> " + str(self.convertDate(self.mainWs.cell(i, 16).value)))
self.mainWs.cell(i, 16).value = self.convertDate(self.mainWs.cell(i, 16).value)
def convertDate(self, dateToConvert):
# Thank you, StackOverflow <3
# https://stackoverflow.com/questions/29387137/how-to-convert-a-given-ordinal-number-from-excel-to-a-date
epochStart = datetime(1899, 12, 31)
if dateToConvert >= 60:
dateToConvert -= 1 # Excel leap year bug, 1900 is not a leap year
return epochStart + timedelta(days=dateToConvert)
After execution I get following results:
43899.89134259259 -> 2020-03-09 21:23:32
43900.9475 -> 2020-03-10 22:44:24

SPSS/Python - Accessing variable labels

I am trying to access variable labels in for loop in SPSS using Python. The for loop iterates over a range of variables, deleting 1-3 and renaming 4 and 5 in a sequence of 5 variables. This works fine, but now when trying to access the variable labels via SPSS I am running into the 'unicode object has no attribute keyes' error.
I recognize that I need to somehow refer to the key instead of the string in my array, but as a novice programmer I am struggling to figure out how to update my existing code:
begin program.
import spss, spssaux
vdict=spssaux.VariableDict()
mylist=vdict.range(start="M10", end="ENDOK_D")
nvars = len(mylist)
mycounter = 1
durations = ""
for i in range(nvars):
myvar = mylist[i]
if (mycounter < 4):
spss.Submit("delete variables %s." % myvar)
mycounter +=1
elif (mycounter == 4):
varlabel = mylist[i].VariableLabel
spss.Submit('variable labels %s "%s" [TimeStamp]' % (myvar,varlabel) + ".")
if (myvar.endswith("_C")): mynewvar = myvar[:-2] + "_TS"
spss.Submit("rename variables (%s = %s)" % (myvar,mynewvar) + ".")
spss.Submit("formats %s (DATETIME28.4)" % (mynewvar) + ".")
mycounter +=1
elif (mycounter == 5):
varlabel = mylist[i].VariableLabel
spss.Submit('variable labels %s "%s" [TimeStamp]' % (myvar,varlabel) + ".")
if (myvar.endswith("_D")): mynewvar = myvar[:-2] + "_TSD"
spss.Submit("rename variables (%s = %s)" % (myvar,mynewvar) + ".")
durations += mynewvar + " "
mycounter = 1
spss.Submit("alter type %s (F4.0)" % durations + ".")
end program.
Any help would be greatly appreciated.
The line
spss.Submit('variable labels %s "%s" [TimeStamp]' % (myvar,varlabel) + ".") is what's causing you trouble.
The [TimeStamp] bit is basically telling python to look for a key named "TimeStamp" in a dictionary but before the [TimeStamp] it doesn't find the right data structure - namely a dictionary - but a string which does not have keys. Maybe this might make it clearer:
myDict = {
"varname": "myVariable",
"label": "This is myVariable's label!",
"TimeStamp": "20190204-0814"
}
print (myDict["varname"])
print (myDict["TimeStamp"])
>>> myVariable
>>> 20190204-0814
Python is looking for a data structure like this and wants to look up the key "TimeStamp". Now, I assume you just want your labels to read "Whatever Variable Label was assigned [TimeStamp]"?
Simply change the two lines to
spss.Submit('variable labels %s "%s" + " [TimeStamp]"' % (myvar,varlabel) + ".")

Working code but pycharm marks tmF and tmR in list as "local variable referenced before assignment", why is this?

I've only recently started programming so there may be a simple answer to this question but I couldn't find it on here. My code works fine for what I want to do but as I am new to this I want to get into the practice of writing good readable code. I am using PyCharm and I noticed it marked some of the code below as not defined. I understand a little about global vs local variables and I guess it has something to do with this but I can't work out why this part of the code works at all if this is the case.
if len(primerF) < 14:
tmF = (no_A_F + no_T_F) * 2 + (no_C_F + no_G_F) * 4
float_tmF = float(tmF)
print("Forward primer tm: " + str(float_tmF))
elif len(primerR) > 13:
tmR = 64.9 + 41*(no_C_R + no_G_R - 16.4) / (no_A_R + no_T_R + no_G_R +
no_C_R)
print("Reverse primer tm: " + str(tmR))
if len(primerR) < 14:
tmR = (no_A_R + no_T_R) * 2 + (no_C_R + no_G_R) * 4
print("Reverse primer tm: " + str(tmR))
elif len(primerR) > 13:
tmR = 64.9 + 41*(no_C_R + no_G_R - 16.4) / (no_A_R + no_T_R + no_G_R +
no_C_R)
print("Reverse primer tm: " + str(tmR))
gc_F = (no_G_F + no_C_F)
gc_content_F = float(gc_F) / forward_length * 100
print("Forward GC content: " + str(gc_content_F) + "%")
gc__R = (no_G_R + no_C_R)
gc_content_R = float(gc__R) / reverse_length * 100
print("Reverse GC content: " + str(gc_content_R) + "%")
This block here is marked as name not defined and if I click on the bubble it says: "This inspection warns about local variables referenced before assignment".
list_tm = [**tmF**, **tmR**]
high_tm = max(list_tm)
low_tm = min(list_tm)
You're setting tmF and tmR in conditional blocks that do not cover all possibilities. For example, if primerF is >= 14, tmF will never be set before the list_tm = [tmF, tmR] statement. This will result in a runtime error, which pycharm is warning you about.
One way to fix this is to set default values of tmF and tmR at the top of your code, or to have full conditional coverage by setting tmF and tmR in else (not just elif) blocks.

Add function names to a string in Python

I'm just beginning to learn Python on my own, and I'm trying to write a code that will calculate the end time of a jog.
My code so far looks as follows:
def elapsed(t):
t = raw_input('Enter time (hh:mm:ss): ')
th, tm, ts = t.split(':')
return int(ts) + int(tm) * 60 + int(th) * 3600
def mile(m):
m = raw_input('How many miles? ')
return int(m)
start = elapsed('start')
warmup = elapsed('warmup')
wmile = mile('wmile')
tempo = elapsed('tempo')
tmile = mile('tmile')
cooloff = elapsed('cooloff')
cmile = mile('cmile')
hour = (start + warmup * wmile + tempo * tmile + cooloff * cmile) // 3600
minute = (start + warmup * wmile + tempo * tmile + cooloff * cmile - hour * 3600) // 60
second = (start + warmup * wmile + tempo * tmile + cooloff * cmile - hour * 3600) % 60
print('Your run ended at %02d:%02d:%02d' % (hour, minute, second))
In this code, the time prompts are all the same: "Enter time (hh:mm:ss):" I want each prompt to refer to its variable name, e.g., "Enter start time (hh:mm:ss)" or "Enter time (hh:mm:ss): (warmup)". Is there a way to do this?
Note: While this may technically be a duplicate, I have examined the similar questions, but decided that both the questions and the answers provided were on the more unspecific side, and therefore decided to ask my question anyway.
Yes, use the input to your function elapsed(t).
Right now it's being overwritten with the return from raw_input()
def elapsed(t):
t1 = raw_input('Enter time (hh:mm:ss): ({0})'.format(t))
th, tm, ts = t1.split(':')
return int(ts) + int(tm) * 60 + int(th) * 3600
or
def elapsed(t):
t1 = raw_input('Enter time (hh:mm:ss): (%s)' % t))
th, tm, ts = t1.split(':')
return int(ts) + int(tm) * 60 + int(th) * 3600
def enter_time(specify_string):
print("Please enter the", specify_string, 'time in format hh:mm:ss > ', end='')
hh, mm, ss = input().split(':')
return (hh, mm, ss)
start_time = enter_time('start')
finish_time = enter_time('finish')
>>>Please enter the start time in format hh:mm:ss >13:32:34
>>>Please enter the finish time in format hh:mm:ss >12:21:21
>>>start_time
(13, 32, 34)
Now you can call the function with string arguments in the function call, and it will generalise the function for different requirements.
It is better to move the time between functions in a more readable format, such as a tuple. You can make more functions, eg:
- Test valid time entered
- Convert tuple to seconds for calculations
- Convert seconds back to tuple format
ect.
Let me know if I misunderstood you.

How to make progress bar for a function in python [duplicate]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I wrote a simple console app to upload and download files from an FTP server using the ftplib.
I would like the app to show some visualization of its download/upload progress for the user; each time a data chunk is downloaded, I would like it to provide a progress update, even if it's just a numeric representation like a percentage.
Importantly, I want to avoid erasing all the text that's been printed to the console in previous lines (i.e. I don't want to "clear" the entire terminal while printing the updated progress).
This seems a fairly common task – how can I go about making a progress bar or similar visualization that outputs to my console while preserving prior program output?
Python 3
A Simple, Customizable Progress Bar
Here's an aggregate of many of the answers below that I use regularly (no imports required).
Note: All code in this answer was created for Python 3; see end of answer to use this code with Python 2.
# Print iterations progress
def printProgressBar (iteration, total, prefix = '', suffix = '', decimals = 1, length = 100, fill = '█', printEnd = "\r"):
"""
Call in a loop to create terminal progress bar
#params:
iteration - Required : current iteration (Int)
total - Required : total iterations (Int)
prefix - Optional : prefix string (Str)
suffix - Optional : suffix string (Str)
decimals - Optional : positive number of decimals in percent complete (Int)
length - Optional : character length of bar (Int)
fill - Optional : bar fill character (Str)
printEnd - Optional : end character (e.g. "\r", "\r\n") (Str)
"""
percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))
filledLength = int(length * iteration // total)
bar = fill * filledLength + '-' * (length - filledLength)
print(f'\r{prefix} |{bar}| {percent}% {suffix}', end = printEnd)
# Print New Line on Complete
if iteration == total:
print()
Sample Usage
import time
# A List of Items
items = list(range(0, 57))
l = len(items)
# Initial call to print 0% progress
printProgressBar(0, l, prefix = 'Progress:', suffix = 'Complete', length = 50)
for i, item in enumerate(items):
# Do stuff...
time.sleep(0.1)
# Update Progress Bar
printProgressBar(i + 1, l, prefix = 'Progress:', suffix = 'Complete', length = 50)
Sample Output
Progress: |█████████████████████████████████████████████-----| 90.0% Complete
Update
There was discussion in the comments regarding an option that allows the progress bar to adjust dynamically to the terminal window width. While I don't recommend this, here's a gist that implements this feature (and notes the caveats).
Single-Call Version of The Above
A comment below referenced a nice answer posted in response to a similar question. I liked the ease of use it demonstrated and wrote a similar one, but opted to leave out the import of the sys module while adding in some of the features of the original printProgressBar function above.
Some benefits of this approach over the original function above include the elimination of an initial call to the function to print the progress bar at 0% and the use of enumerate becoming optional (i.e. it is no longer explicitly required to make the function work).
def progressBar(iterable, prefix = '', suffix = '', decimals = 1, length = 100, fill = '█', printEnd = "\r"):
"""
Call in a loop to create terminal progress bar
#params:
iterable - Required : iterable object (Iterable)
prefix - Optional : prefix string (Str)
suffix - Optional : suffix string (Str)
decimals - Optional : positive number of decimals in percent complete (Int)
length - Optional : character length of bar (Int)
fill - Optional : bar fill character (Str)
printEnd - Optional : end character (e.g. "\r", "\r\n") (Str)
"""
total = len(iterable)
# Progress Bar Printing Function
def printProgressBar (iteration):
percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))
filledLength = int(length * iteration // total)
bar = fill * filledLength + '-' * (length - filledLength)
print(f'\r{prefix} |{bar}| {percent}% {suffix}', end = printEnd)
# Initial Call
printProgressBar(0)
# Update Progress Bar
for i, item in enumerate(iterable):
yield item
printProgressBar(i + 1)
# Print New Line on Complete
print()
Sample Usage
import time
# A List of Items
items = list(range(0, 57))
# A Nicer, Single-Call Usage
for item in progressBar(items, prefix = 'Progress:', suffix = 'Complete', length = 50):
# Do stuff...
time.sleep(0.1)
Sample Output
Progress: |█████████████████████████████████████████████-----| 90.0% Complete
Python 2
To use the above functions in Python 2, set the encoding to UTF-8 at the top of your script:
# -*- coding: utf-8 -*-
And replace the Python 3 string formatting in this line:
print(f'\r{prefix} |{bar}| {percent}% {suffix}', end = printEnd)
With Python 2 string formatting:
print('\r%s |%s| %s%% %s' % (prefix, bar, percent, suffix), end = printEnd)
Writing '\r' will move the cursor back to the beginning of the line.
This displays a percentage counter:
import time
import sys
for i in range(100):
time.sleep(1)
sys.stdout.write("\r%d%%" % i)
sys.stdout.flush()
tqdm: add a progress meter to your loops in a second:
>>> import time
>>> from tqdm import tqdm
>>> for i in tqdm(range(100)):
... time.sleep(1)
...
|###-------| 35/100 35% [elapsed: 00:35 left: 01:05, 1.00 iters/sec]
Write a \r to the console. That is a "carriage return" which causes all text after it to be echoed at the beginning of the line. Something like:
def update_progress(progress):
print '\r[{0}] {1}%'.format('#'*(progress/10), progress)
which will give you something like: [ ########## ] 100%
It is less than 10 lines of code.
The gist here: https://gist.github.com/vladignatyev/06860ec2040cb497f0f3
import sys
def progress(count, total, suffix=''):
bar_len = 60
filled_len = int(round(bar_len * count / float(total)))
percents = round(100.0 * count / float(total), 1)
bar = '=' * filled_len + '-' * (bar_len - filled_len)
sys.stdout.write('[%s] %s%s ...%s\r' % (bar, percents, '%', suffix))
sys.stdout.flush() # As suggested by Rom Ruben
Try the click library written by the Mozart of Python, Armin Ronacher.
$ pip install click # both 2 and 3 compatible
To create a simple progress bar:
import click
with click.progressbar(range(1000000)) as bar:
for i in bar:
pass
This is what it looks like:
# [###-------------------------------] 9% 00:01:14
Customize to your hearts content:
import click, sys
with click.progressbar(range(100000), file=sys.stderr, show_pos=True, width=70, bar_template='(_(_)=%(bar)sD(_(_| %(info)s', fill_char='=', empty_char=' ') as bar:
for i in bar:
pass
Custom look:
(_(_)===================================D(_(_| 100000/100000 00:00:02
There are even more options, see the API docs:
click.progressbar(iterable=None, length=None, label=None, show_eta=True, show_percent=None, show_pos=False, item_show_func=None, fill_char='#', empty_char='-', bar_template='%(label)s [%(bar)s] %(info)s', info_sep=' ', width=36, file=None, color=None)
I realize I'm late to the game, but here's a slightly Yum-style (Red Hat) one I wrote (not going for 100% accuracy here, but if you're using a progress bar for that level of accuracy, then you're WRONG anyway):
import sys
def cli_progress_test(end_val, bar_length=20):
for i in xrange(0, end_val):
percent = float(i) / end_val
hashes = '#' * int(round(percent * bar_length))
spaces = ' ' * (bar_length - len(hashes))
sys.stdout.write("\rPercent: [{0}] {1}%".format(hashes + spaces, int(round(percent * 100))))
sys.stdout.flush()
Should produce something looking like this:
Percent: [############## ] 69%
... where the brackets stay stationary and only the hashes increase.
This might work better as a decorator. For another day...
Check this library: clint
it has a lot of features including a progress bar:
from time import sleep
from random import random
from clint.textui import progress
if __name__ == '__main__':
for i in progress.bar(range(100)):
sleep(random() * 0.2)
for i in progress.dots(range(100)):
sleep(random() * 0.2)
this link provides a quick overview of its features
Here's a nice example of a progressbar written in Python: http://nadiana.com/animated-terminal-progress-bar-in-python
But if you want to write it yourself. You could use the curses module to make things easier :)
[edit]
Perhaps easier is not the word for curses. But if you want to create a full-blown cui than curses takes care of a lot of stuff for you.
[edit]
Since the old link is dead I have put up my own version of a Python Progressbar, get it here: https://github.com/WoLpH/python-progressbar
import time,sys
for i in range(100+1):
time.sleep(0.1)
sys.stdout.write(('='*i)+(''*(100-i))+("\r [ %d"%i+"% ] "))
sys.stdout.flush()
output
[ 29% ] ===================
Install tqdm.(pip install tqdm)
and use it as follows:
import time
from tqdm import tqdm
for i in tqdm(range(1000)):
time.sleep(0.01)
That's a 10 seconds progress bar that'll output something like this:
47%|██████████████████▊ | 470/1000 [00:04<00:05, 98.61it/s]
and, just to add to the pile, here's an object you can use:
Add the following to a new file progressbar.py
import sys
class ProgressBar(object):
CHAR_ON = '='
CHAR_OFF = ' '
def __init__(self, end=100, length=65):
self._end = end
self._length = length
self._chars = None
self._value = 0
#property
def value(self):
return self._value
#value.setter
def value(self, value):
self._value = max(0, min(value, self._end))
if self._chars != (c := int(self._length * (self._value / self._end))):
self._chars = c
sys.stdout.write("\r {:3n}% [{}{}]".format(
int((self._value / self._end) * 100.0),
self.CHAR_ON * int(self._chars),
self.CHAR_OFF * int(self._length - self._chars),
))
sys.stdout.flush()
def __enter__(self):
self.value = 0
return self
def __exit__(self, *args, **kwargs):
sys.stdout.write('\n')
Can be included in your program with:
import time
from progressbar import ProgressBar
count = 150
print("starting things:")
with ProgressBar(count) as bar:
for i in range(count + 1):
bar.value += 1
time.sleep(0.01)
print("done")
Results in:
starting things:
100% [=================================================================]
done
This may be "over the top", but is handy when used frequently.
Run this at the Python command line (not in any IDE or development environment):
>>> import threading
>>> for i in range(50+1):
... threading._sleep(0.5)
... print "\r%3d" % i, ('='*i)+('-'*(50-i)),
Works fine on my Windows system.
Try to install this package: pip install progressbar2 :
import time
import progressbar
for i in progressbar.progressbar(range(100)):
time.sleep(0.02)
progresssbar github: https://github.com/WoLpH/python-progressbar
http://code.activestate.com/recipes/168639-progress-bar-class/ (2002)
http://code.activestate.com/recipes/299207-console-text-progress-indicator-class/ (2004)
http://pypi.python.org/pypi/progressbar (2006)
And a lot of tutorials waiting to be googled.
based on the above answers and other similar questions about CLI progress bar, I think I got a general common answer to all of them. Check it at https://stackoverflow.com/a/15860757/2254146
In summary, the code is this:
import time, sys
# update_progress() : Displays or updates a console progress bar
## Accepts a float between 0 and 1. Any int will be converted to a float.
## A value under 0 represents a 'halt'.
## A value at 1 or bigger represents 100%
def update_progress(progress):
barLength = 10 # Modify this to change the length of the progress bar
status = ""
if isinstance(progress, int):
progress = float(progress)
if not isinstance(progress, float):
progress = 0
status = "error: progress var must be float\r\n"
if progress < 0:
progress = 0
status = "Halt...\r\n"
if progress >= 1:
progress = 1
status = "Done...\r\n"
block = int(round(barLength*progress))
text = "\rPercent: [{0}] {1}% {2}".format( "#"*block + "-"*(barLength-block), progress*100, status)
sys.stdout.write(text)
sys.stdout.flush()
Looks like
Percent: [##########] 99.0%
I am using progress from reddit. I like it because it can print progress for every item in one line, and it shouldn't erase printouts from the program.
Edit: fixed link
A very simple solution is to put this code into your loop:
Put this in the body (i.e. top) of your file:
import sys
Put this in the body of your loop:
sys.stdout.write("-") # prints a dash for each iteration of loop
sys.stdout.flush() # ensures bar is displayed incrementally
I recommend using tqdm - https://pypi.python.org/pypi/tqdm - which makes it simple to turn any iterable or process into a progress bar, and handles all messing about with terminals needed.
From the documentation: "tqdm can easily support callbacks/hooks and manual updates. Here’s an example with urllib"
import urllib
from tqdm import tqdm
def my_hook(t):
"""
Wraps tqdm instance. Don't forget to close() or __exit__()
the tqdm instance once you're done with it (easiest using `with` syntax).
Example
-------
>>> with tqdm(...) as t:
... reporthook = my_hook(t)
... urllib.urlretrieve(..., reporthook=reporthook)
"""
last_b = [0]
def inner(b=1, bsize=1, tsize=None):
"""
b : int, optional
Number of blocks just transferred [default: 1].
bsize : int, optional
Size of each block (in tqdm units) [default: 1].
tsize : int, optional
Total size (in tqdm units). If [default: None] remains unchanged.
"""
if tsize is not None:
t.total = tsize
t.update((b - last_b[0]) * bsize)
last_b[0] = b
return inner
eg_link = 'http://www.doc.ic.ac.uk/~cod11/matryoshka.zip'
with tqdm(unit='B', unit_scale=True, miniters=1,
desc=eg_link.split('/')[-1]) as t: # all optional kwargs
urllib.urlretrieve(eg_link, filename='/dev/null',
reporthook=my_hook(t), data=None)
import sys
def progresssbar():
for i in range(100):
time.sleep(1)
sys.stdout.write("%i\r" % i)
progressbar()
NOTE: if you run this in interactive interepter you get extra numbers printed out
lol i just wrote a whole thingy for this
heres the code keep in mind you cant use unicode when doing block ascii i use cp437
import os
import time
def load(left_side, right_side, length, time):
x = 0
y = ""
print "\r"
while x < length:
space = length - len(y)
space = " " * space
z = left + y + space + right
print "\r", z,
y += "█"
time.sleep(time)
x += 1
cls()
and you call it like so
print "loading something awesome"
load("|", "|", 10, .01)
so it looks like this
loading something awesome
|█████ |
With the great advices above I work out the progress bar.
However I would like to point out some shortcomings
Every time the progress bar is flushed, it will start on a new line
print('\r[{0}]{1}%'.format('#' * progress* 10, progress))
like this:
[] 0%
[#]10%
[##]20%
[###]30%
2.The square bracket ']' and the percent number on the right side shift right as the '###' get longer.
3. An error will occur if the expression 'progress / 10' can not return an integer.
And the following code will fix the problem above.
def update_progress(progress, total):
print('\r[{0:10}]{1:>2}%'.format('#' * int(progress * 10 /total), progress), end='')
For python 3:
def progress_bar(current_value, total):
increments = 50
percentual = ((current_value/ total) * 100)
i = int(percentual // (100 / increments ))
text = "\r[{0: <{1}}] {2}%".format('=' * i, increments, percentual)
print(text, end="\n" if percentual == 100 else "")
function from Greenstick for 2.7:
def printProgressBar (iteration, total, prefix = '', suffix = '',decimals = 1, length = 100, fill = '#'):
percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))
filledLength = int(length * iteration // total)
bar = fill * filledLength + '-' * (length - filledLength)
print'\r%s |%s| %s%% %s' % (prefix, bar, percent, suffix),
sys.stdout.flush()
# Print New Line on Complete
if iteration == total:
print()
Code for python terminal progress bar
import sys
import time
max_length = 5
at_length = max_length
empty = "-"
used = "%"
bar = empty * max_length
for i in range(0, max_length):
at_length -= 1
#setting empty and full spots
bar = used * i
bar = bar+empty * at_length
#\r is carriage return(sets cursor position in terminal to start of line)
#\0 character escape
sys.stdout.write("[{}]\0\r".format(bar))
sys.stdout.flush()
#do your stuff here instead of time.sleep
time.sleep(1)
sys.stdout.write("\n")
sys.stdout.flush()
The python module progressbar is a nice choice.
Here is my typical code:
import time
import progressbar
widgets = [
' ', progressbar.Percentage(),
' ', progressbar.SimpleProgress(format='(%(value_s)s of %(max_value_s)s)'),
' ', progressbar.Bar('>', fill='.'),
' ', progressbar.ETA(format_finished='- %(seconds)s -', format='ETA: %(seconds)s', ),
' - ', progressbar.DynamicMessage('loss'),
' - ', progressbar.DynamicMessage('error'),
' '
]
bar = progressbar.ProgressBar(redirect_stdout=True, widgets=widgets)
bar.start(100)
for i in range(100):
time.sleep(0.1)
bar.update(i + 1, loss=i / 100., error=i)
bar.finish()
i wrote a simple progressbar:
def bar(total, current, length=10, prefix="", filler="#", space=" ", oncomp="", border="[]", suffix=""):
if len(border) != 2:
print("parameter 'border' must include exactly 2 symbols!")
return None
print(prefix + border[0] + (filler * int(current / total * length) +
(space * (length - int(current / total * length)))) + border[1], suffix, "\r", end="")
if total == current:
if oncomp:
print(prefix + border[0] + space * int(((length - len(oncomp)) / 2)) +
oncomp + space * int(((length - len(oncomp)) / 2)) + border[1], suffix)
if not oncomp:
print(prefix + border[0] + (filler * int(current / total * length) +
(space * (length - int(current / total * length)))) + border[1], suffix)
as you can see, it have: length of bar, prefix and suffix, filler, space, text in bar on 100%(oncomp) and borders
here an example:
from time import sleep, time
start_time = time()
for i in range(10):
pref = str((i+1) * 10) + "% "
complete_text = "done in %s sec" % str(round(time() - start_time))
sleep(1)
bar(10, i + 1, length=20, prefix=pref, oncomp=complete_text)
out in progress:
30% [###### ]
out on complete:
100% [ done in 9 sec ]
Putting together some of the ideas I found here, and adding estimated time left:
import datetime, sys
start = datetime.datetime.now()
def print_progress_bar (iteration, total):
process_duration_samples = []
average_samples = 5
end = datetime.datetime.now()
process_duration = end - start
if len(process_duration_samples) == 0:
process_duration_samples = [process_duration] * average_samples
process_duration_samples = process_duration_samples[1:average_samples-1] + [process_duration]
average_process_duration = sum(process_duration_samples, datetime.timedelta()) / len(process_duration_samples)
remaining_steps = total - iteration
remaining_time_estimation = remaining_steps * average_process_duration
bars_string = int(float(iteration) / float(total) * 20.)
sys.stdout.write(
"\r[%-20s] %d%% (%s/%s) Estimated time left: %s" % (
'='*bars_string, float(iteration) / float(total) * 100,
iteration,
total,
remaining_time_estimation
)
)
sys.stdout.flush()
if iteration + 1 == total:
print
# Sample usage
for i in range(0,300):
print_progress_bar(i, 300)
Well here is code that works and I tested it before posting:
import sys
def prg(prog, fillchar, emptchar):
fillt = 0
emptt = 20
if prog < 100 and prog > 0:
prog2 = prog/5
fillt = fillt + prog2
emptt = emptt - prog2
sys.stdout.write("\r[" + str(fillchar)*fillt + str(emptchar)*emptt + "]" + str(prog) + "%")
sys.stdout.flush()
elif prog >= 100:
prog = 100
prog2 = prog/5
fillt = fillt + prog2
emptt = emptt - prog2
sys.stdout.write("\r[" + str(fillchar)*fillt + str(emptchar)*emptt + "]" + str(prog) + "%" + "\nDone!")
sys.stdout.flush()
elif prog < 0:
prog = 0
prog2 = prog/5
fillt = fillt + prog2
emptt = emptt - prog2
sys.stdout.write("\r[" + str(fillchar)*fillt + str(emptchar)*emptt + "]" + str(prog) + "%" + "\nHalted!")
sys.stdout.flush()
Pros:
20 character bar (1 character for every 5 (number wise))
Custom fill characters
Custom empty characters
Halt (any number below 0)
Done (100 and any number above 100)
Progress count (0-100 (below and above used for special functions))
Percentage number next to bar, and it's a single line
Cons:
Supports integers only (It can be modified to support them though, by making the division an integer division, so just change prog2 = prog/5 to prog2 = int(prog/5))
Here's my Python 3 solution:
import time
for i in range(100):
time.sleep(1)
s = "{}% Complete".format(i)
print(s,end=len(s) * '\b')
'\b' is a backslash, for each character in your string.
This does not work within the Windows cmd window.

Categories