class progressBar:
def __init__(self, minValue = 0, maxValue = 10, totalWidth=12):
self.progBar = "[]" # This holds the progress bar string
Why "hold a string" -- since strings are immutable in Python, every
time you modify it you have to create a whole new string.
def __str__(self):
return str(self.progBar)
You don't need str() for a string data type. But why not compute the
string here, so you only need a local form /when/ needed.
def myReportHook(count, blockSize, totalSize):
Some generic function... based on the indentation, it is not a
method of the progress bar class... apparently being called by EACH
thread
import sys
global prog
prog = ""
if prog == "":
prog = progressBar(0,totalSize,50)
What have we here?
First you are declaring that "prog" is a global, which also makes it
shared... You initialize it to an empty string, and then you test for it
to be empty... IF it is empty (well, it will always be empty since you
initialized it) you create a progress bar INSTANCE (which is not a
string, BTW).
prog.updateAmount(count*blockSize)
You then update it based upon some arguments...
sys.stdout.write (str(prog))
sys.stdout.write ("\r")
Then you write its string form out. Using two I/O calls (which means
a thread swap could take place between them).
Here's the function, download_from_web() which calls the progress bar:
progressbar.myReportHook(counter, block_size, size)
This should fail, since 1) the indentation meant myReportHook() is
NOT a method of progressbar and, 2) you are invoking it as a class
method, not an instance method. The only progress bar instance I've seen
in the entire listing is the one you create /inside/ of myReportHook(),
and you keep recreating that one from scratch each time you call.
Yes, you're correct. That's what I'm also suspecting. I tried to do some minor
changes but couldn't succeed.
Request you to, if you reply with code, give a little explanation so that I can
understand and learn from it.
Don't know how much you can get from this -- I stripped out all the
network stuff and fake it with randomly generated sleeps, packet sizes,
and file sizes (no real files, just sizes to play with). Watch out for
line wraps...
-=-=-=-=-=-=-=-
import random
import threading
import time
import Queue
class ProgressBar(object):
def __init__(self, minValue = 0, maxValue = 10, width = 10):
#width does NOT include the two places for [] markers
self.min = minValue
self.max = maxValue
self.span = float(self.max - self.min)
self.width = width
self.value = self.min
def updateValue(self, newValue):
#require caller to supply a value!
self.value = max(self.min, min(self.max, newValue))
def __str__(self):
#compute display fraction
percentFilled = ((self.value - self.min)
/ self.span)
widthFilled = int(self.width * percentFilled + 0.5)
return ("[" + "#"*widthFilled + " "*(self.width - widthFilled) +
"]"
+ " %5.1f%% of %6s" % (percentFilled * 100.0, self.max))
def downloadFromWeb(URL):
#this is a dummy routine which merely uses random number
#generation to determine "size" and rate of reception of
#a file transfer
size = random.randint(2**10, 2**15)
#"file size" is between 1,024 bytes and 32,767 bytes
packet = random.randint(10, 10 + (size / 64))
#"packet size is anywhere from 10 bytes to ...
latency = random.randint(1, 30) / 10.0
#"network latency" is between 0.1 and 3.0 seconds per packet
#create a progress bar instance
myProgress = ProgressBar(maxValue = size, width = 30)
print "\nDownloading %s with size, packet, latency %6s, %5s, %5.2f "
% (URL, size, packet, latency)
print "%20s: %s \r" % (URL, str(myProgress)),
for i in xrange(0, size, packet):
time.sleep(latency)
myProgress.updateValue(i)
print "%20s: %s \r" % (URL, str(myProgress)),
myProgress.updateValue(size)
print "%20s: %s \r" % (URL, str(myProgress)),
print "\n%20s: COMPLETED" % URL
completionQueue.put(URL)
#create queue for completion notification
completionQueue = Queue.Queue()
if __name__ == "__main__":
#create a random number (between 5 and 20) of download "URL"s
URLs = [ "File Number = %3s" % i
for i in xrange(random.randint(5, 20)) ]
for U in URLs:
#create "download" thread for each URL
threading.Thread(target=downloadFromWeb,
args=(U,)).start()
while URLs:
#loop while any of the "URL"s are still being processed
#note use of blocking get() call; only proceed when
#data is available
CU = completionQueue.get()
URLs.remove(CU)
-=-=-=-=-=-=-=-=-
If you try running this, be advised: you can not break execution --
it runs until complete and depending on how the random numbers fall,
that could take some time. (If you do manage to kill a thread, the main
routine will never exit as it never receives the "completed" message for
that thread).
Part one: the ProgressBar class... Just the __init__(),
updateValue(), and __str__() methods.
Part two: the fake downloadFromWeb() function. This takes one
argument (in my test version) -- called a URL, but it is just a /unique/
string (basically, the thread number with a text addition). This gets
reported later with the progress bar AND is the text of the "completed"
message that gets queued for the main thread. Generate random numbers
for size, packet, latency. CREATE A PROGRESS BAR INSTANCE with the
specified size and some display width (not counting the []). Print out
(on a new line) the information of this function invocation -- the URL,
and size, packet, latency. Then print out the URL and progress bar
(using a trailing , to prevent a new line). Go into a loop from 0 to
size, stepping by the packet length. In the loop, sleep for latency (to
emulate network load delays in receiving a packet), update the progress
bar value to the current loop value, display the URL/progress bar (again
with , to prevent line feed). At end of loop, update progress bar to
size (100%) and display it, then display a completed message on a new
line. Finally, queue the URL so the main program can tell that this
thread has completed (function exits, so thread exits).
Part three: create the completion queue (I don't have to declare it
global in part two because I'm not going to rebind it, only use methods
that it has).
Part four: main program. Create a list of between 5 and 20 "URL"s;
then for each URL create and start a thread (Note that I'm NOT keeping a
list of each thread object). After all the threads have been started and
the list of URLs is NOT empty loop: get a value from the completion
queue (this blocks until some data is put into the queue). Then, since
the only data that should be placed in the queue is a URL that came from
the URL list, remove the returned URL. When all threads have completed,
the URL list is empty, and the main program exits.
Note: while each thread has its own progress bar, the fact that they
display the updates with a new line means that each thread /displays/ on
the same screen line (except when the initialization puts out new lines,
and when the completions put out new lines). If you want each thread to
put the progress bar on a different line of the screen, you will have to
find some sort of cursor control code (I don't recall if curses is
available for Windows) and have each thread embed the cursor position
into the string that gets written to the screen (you do NOT want to
split that I/O into separate calls as a thread swap could take place and
relocate the cursor...
--
Wulfraed Dennis Lee Bieber KD6MOG
(e-mail address removed) (e-mail address removed)
HTTP://wlfraed.home.netcom.com/
(Bestiaria Support Staff: (e-mail address removed))
HTTP://www.bestiaria.com/