fbielejec
fbielejec

Reputation: 3700

Command-line Progress bar with occupied indicator

I have a program processing a large, lengthy job. I know the size of the task up front so I display a progress bar. Yet the individual sub-tasks can take a long time, so in the end the progress bar can also stay idle for a long time, making it look like a program has stalled.

For the inpatient users out there I though I might include an indicator, which just continuously spins a simple ASCII animation, reassuring them that theres still some computing going on. I have a problem combining it with the progress bar updates though - if I use a carriage return it displays where my progress bar should be and I'd rather have it befor the PB, something like this:

Busy /
0     25    50    75    100%
|-----|-----|-----|-----|
[*****************  

The code:

public class Test {

    public static void main(String[] args) {

        try {

            int barLength = 100;
            int jobLength = 501;
            double stepSize = (double) barLength / (double) jobLength;

            String message = "Busy";
            RotatingProgressBar progressBar = new RotatingProgressBar(message);
            progressBar.start();

            System.out
                    .println("0                        25                       50                       75                       100%");
            System.out
                    .println("|------------------------|------------------------|------------------------|------------------------|");

            for (int i = 0; i <= jobLength; i++) {

                Thread.sleep(100);

                double progress = (stepSize * i) / barLength;
                updateProgress(progress, barLength);

            }

            progressBar.setShowProgress(false);

        } catch (Exception e) {
            e.printStackTrace();
        }

    }// END: main

    static void updateProgress(double progressPercentage, int barLength) {

        System.out.print("\r[");
        int i = 0;
        for (; i < (int) (progressPercentage * (barLength - 1)); i++) {
            System.out.print("*");
        }

        for (; i < barLength - 1; i++) {
            System.out.print(" ");
        }

        System.out.print("]");
    }// END: updateProgress

    static class RotatingProgressBar extends Thread {

        private final String anim = "|/-\\";
        private boolean showProgress = true;
        private String message;

        public RotatingProgressBar(String message) {
            this.message = message;
        }

        public void run() {

            int i = 0;

            while (showProgress) {

                System.out.print("\r");
                System.out.print(message + anim.charAt(i++ % anim.length()));

                try {

                    Thread.sleep(10);

                } catch (Exception e) {
                    // do nothing
                }// END: try-catch

            }// END: while

        }// END: run

        public void setShowProgress(boolean showProgress) {
            this.showProgress = showProgress;
        }

//      public void setCarriageColumn(int column) {
//          this.column = column;
//      }
    }// END: class

}// END: class

Upvotes: 2

Views: 610

Answers (2)

fbielejec
fbielejec

Reputation: 3700

I think I solved this, inspired by @Stefan Lindenberg and @nafas. I merged two methods into one, the busy indicator spins continuously as the last character of the progress bar, which I update with a progress:

class ProgressBar extends Thread {

    private static final String anim = "|/-\\";
    private boolean showProgress;
    double progressPercentage;
    private final int barLength;

    public ProgressBar(int barLength) {
        this.barLength = barLength;
        this.showProgress = true;
        this.progressPercentage = 0;
    }

    public void run() {

        int i = 0;

        while (showProgress) {

            String progress = "\r";
            int column = (int) (progressPercentage * barLength);
            for (int j = 0; j <= column; j++) {
                progress += ("*");
            }

            System.out.print(progress + anim.charAt(i++ % anim.length()));

            try {

                Thread.sleep(10);

            } catch (Exception e) {
                // do nothing
            }// END: try-catch

        }// END: while

    }// END: run

    public void setShowProgress(boolean showProgress) {
        this.showProgress = showProgress;
    }

    public void setProgressPercentage(double progressPercentage) {
        this.progressPercentage = progressPercentage;
    }
}// END: class

and this is then used like this:

        for (int i = 0; i <= jobLength; i++) {

            Thread.sleep(100);

            double progress = (stepSize * i) / barLength;
            progressBar.setProgressPercentage(progress);

        }

Looks ok.

Upvotes: 0

Stefan
Stefan

Reputation: 12453

You can use this method to delete chars that have been already printed to System.out:

 public void deleteChar(int times) {
    for (int i = 0; i < times; i++) {
        System.out.print((char) 8);
    }
}

But this (and other workarounds) are not going to work in all types of terminals/consoles. This will work in Windows cmd, powershell and most Unix terminals, but it will fail in Eclipse or Netbeans.

Upvotes: 1

Related Questions