views:

109

answers:

3

Hey everyone,

I was just wondering if there was a way to use System.out.println(); or other methods to create a cool loading bar when I run my program with a batch file.

The real question here is how I can make this bar appear as if it's printing on only one line.

I don't want it to be spread over multiple lines like below:

[aaaaaccccccccccccccc] 25%
[aaaaaaaaaacccccccccc] 50%
[aaaaaaaaaaaaaaaccccc] 75%

One that stays cleanly in place would make things cleaner and friendlier.

Many thanks,

Justian

EDIT:

Ok. I managed to find this link here: http://stackoverflow.com/questions/60221/how-to-animate-the-command-line, but the answer:

  1. Is for C++, not Java
  2. Doesn't seem very efficient in Java, having to backspace each line in a loop?

Is there a better way to do this in Java?

EDIT:

Here's what I ended up going with:

static final int PROGRESSBAR_LENGTH = 20;

public static void drawProgressBar(int numerator, int denominator) {
    int percent = (int) (((double) numerator / (double) denominator) * 100);

    String bar = "[";
    int lines = round((PROGRESSBAR_LENGTH * numerator) / denominator);
    int blanks = PROGRESSBAR_LENGTH - lines;

    for (int i = 0; i < lines; i++)
        bar += "|";

    for (int i = 0; i < blanks; i++)
        bar += " ";

    bar += "] " + percent + "%";

    System.out.print(bar + "\r");
}

private static int round(double dbl) {
    int noDecimal = (int) dbl;
    double decimal = dbl - noDecimal;

    if (decimal >= 0.5)
        return noDecimal + 1;
    else
        return noDecimal;
}

Sample output:

[||||||||||||||||....] 80% (.'s = spaces)

A: 

I've done this using the backspace approach - it seems to work fine.

Just use System.out.print() instead of System.out.println() -- so you don't get carriage returns.

You'll need to keep track of how many backspaces you need to print, but it works fine.

BTW - to print a backspace in java, use:

System.out.print("\b");
desau
It seems like such a memory hog, though. If the process takes time as it is, why slow it down further with a loading bar?
Justian Meyer
I don't understand what you mean by memory hog. How is this using any additional memory? Perhaps a byte or two for storing the number of backspaces .. but really .. 2 bytes? If you're worried about that sort of memory constraints, you should probably be using assembly, not Java. Also - yes - more CPU cycles .. but again, pretty minimal. If you're worried about a couple extra CPU cycles, you probably don't want to print progress at all.
desau
Yeah, I meant cycles. Apologies. It's not so much that it'll cause much lag, so much as there's obviously a better way than that extra loop. The "\r" method works much better. Thanks for the help, regardless. :)
Justian Meyer
+2  A: 

As an alternative to printing the backspace character, you can use the carriage return character: 13 (decimal), 0xD (hexadecimal), or \r (escaped character).

There are three stipulations:

  1. You can't overrun your console width (80 character width is probably a safe assumption)
  2. Each iteration you must print as least as many visible characters as the previous iteration.
  3. When you want to move on (when you're done with your loading bar or whatever), you need to lead with a newline, or else you'll start overwriting your existing loading bar with new text.

So, something along these lines:

public static void loadMyProgram() {
    while(programStillLoading) {
        doSomeLoading();
        double loadPercentage = howCloseToDone();

        System.out.print("[");
        int i = 0;
        for( ; i < (int)(loadPercentage * 20); i++)
            System.out.print("=");
        for( ; i < 20; i++)
            System.out.print(" ");
        System.out.print("] " + (int)(loadPercentage * 100) + "%");

        System.out.print((char)13);
        //System.out.print((char)0xD); // Same as above, but in hex
        //System.out.print("\r"); // Same as above, but as the symbol
    }

    System.out.println();
}

The above will print [<bar>] <percent>%, where <bar> is 0..p '=' followed by 0..(20-p) ''. It will stay on a single line.

Assuming your calculation of the load percentage is monotonically increasing (in other words, it doesn't act like a Windows loading bar, going from 20% to 110% to 1% in the span of 3 seconds), you don't have to worry about ensuring your next output is equal to or longer than your previous output, because that will always be true. However, in this case you could take care of it with two spaces after the '%'.

Brian S
I ended up using this method. So nice and clean :). I'll post my code above. Thanks!
Justian Meyer
+1  A: 

This might be a place to start.

Usage:

ProgressBar pb = new PorgressBar(10,1);
pb.start();
while (loadingStuff) {
  doStuff();
  pb.update();
}
pb.finish();

The class:

public class ProgressBar {

      private static String[] s;
      private int pos, inc, size;

      public PorgressBar(int size, int increment) {
        this.size = size;
        this.increment = increment;
        s = new String[size+2];  
        Arrays.fill(s,"a");
        s[0] = "[";
        s[size+1] = "]";
      }

      public void update() {
          System.out.println('\r');
          if (pos+inc<size+2) {
              Arrays.fill(s,pos,pos+inc,"c");
              pos += inc;
          }
          for (String ch : s) {
              System.out.print(ch);
          }  
      }

      public void finish() {
          System.out.println();
      }
}
xagyg