Showing the progress of an operation is trivial in rich client applications using a progress bar control of some sort. But what about .NET console apps? The Console class offers 2 static methods Console.Write and Console.WriteLine to write out to the Console, but any subsequent calls to these methods will write the content out at the current location of the cursor.This doesn’t really help when you want the progress percentage to refresh in place instead. The trick is to write out the “\r” character in the call to Console.Write so that the cursor is repositioned to the begining of the line after the content is written out. Following is quick implementation of a method which shows percentage of progress, refreshing the percent value in place.
static void ShowPercentProgress(string message, int currElementIndex,
int totalElementCount) {
if (currElementIndex < 0 || currElementIndex >= totalElementCount) {
throw new InvalidOperationException("currElement out of range");
}
int percent = (100 * (currElementIndex + 1)) / totalElementCount;
Console.Write("\r{0}{1}% complete", message, percent);
if (currElementIndex == totalElementCount - 1) {
Console.WriteLine(Environment.NewLine);
}
}
and the caller of course.
static void Main(string[] args) {
for (int i = 0; i < 100; i++) {
ShowPercentProgress("Processing...", i, 100);
Thread.Sleep(100);
}
}
That’s all there is to it. Happy coding.UPDATE: This example wouldn’t be complete unless I provide an Python implmentation since that’s what I’m living and breathing these days.
def showProgress(message, it = [], currElement = 0):
percent = (100 * (currElement + 1)) / len(it)
sys.stdout.write("\r")
sys.stdout.write("%s %i complete" % (message, percent))
sys.stdout.flush()
def process(it, notifyCompletion = showProgress):
for item in it:
time.sleep(0.5)
notifyCompletion("Processing", it, it.index(item))
if __name__ == "__main__":
process(range(10));
UPDATE(4/27/11): I received a comment requesting a precise usage example of the above for copying files
The code below has been adapted to achieve that.
Disclamer: If you use this code, please add proper error handling as you see fit.
class Program {
static void Copyfiles(string[] sourceFiles, string destPath,
Action<string, long, long> reportProgress,
int blockSizeToRead = 4096) {
if (!Directory.Exists(destPath)) {
throw new DirectoryNotFoundException(destPath);
}
foreach (var sourceFile in sourceFiles) {
if (!File.Exists(sourceFile)) {
Console.WriteLine("{0} not found.. skipping", sourceFile);
}
FileInfo sourceFileInfo = new FileInfo(sourceFile);
string message = string.Format("Copying {0} ", sourceFileInfo.Name);
string destFilePath = Path.Combine(destPath, sourceFileInfo.Name);
byte[] buffer = new byte[blockSizeToRead];
using (var destfs = File.OpenWrite(destFilePath)) {
using (var sourcefs = File.OpenRead(sourceFile)) {
int bytesRead, totalBytesRead = 0;
while ((bytesRead = sourcefs.Read(buffer, 0, buffer.Length - 1)) >
0) {
destfs.Write(buffer, 0, bytesRead);
totalBytesRead += bytesRead;
if (reportProgress != null) {
reportProgress(message, totalBytesRead, sourceFileInfo.Length);
}
}
}
}
}
}
static void ShowPercentProgress(string message, long processed, long total) {
long percent = (100 * (processed + 1)) / total;
Console.Write("\r{0}{1}% complete", message, percent);
if (processed >= total - 1) {
Console.WriteLine(Environment.NewLine);
}
}
static void Main(string[] args) {
var files = Directory.GetFiles(@"C:\temp\source");
Copyfiles(files, @"C:\temp\dest", ShowPercentProgress);
}
}
Print | posted @ Sunday, February 21, 2010 10:50 AM