Skip to content

Commit

Permalink
Merge branch '1.0' into multiline-progressbar
Browse files Browse the repository at this point in the history
  • Loading branch information
ctongfei authored Apr 22, 2020
2 parents 4684ee4 + 5d0a2bf commit 6df1ef7
Show file tree
Hide file tree
Showing 9 changed files with 238 additions and 19 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

* `1.0.0`:
- Supports pausing and resuming progress bars (PR #63), thereby fixing #17. Thanks @mesat !

* `0.8.1`:
- Bugfixes:
- Fixed the bug of possible negative suffix length (PR #58). Thanks @kristofarkas !
Expand Down
6 changes: 5 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>me.tongfei</groupId>
<artifactId>progressbar</artifactId>
<version>0.8.1</version>
<version>1.0.0-SNAPSHOT</version>
<name>progressbar</name>
<description>A terminal-based progress bar for JVM</description>
<url>http://github.com/ctongfei/progressbar</url>
Expand Down Expand Up @@ -79,6 +79,10 @@
<id>mordechaim</id>
<name>Mordechai Meisels</name>
</developer>
<developer>
<id>mesat</id>
<name>Muhammet Sakarya</name>
</developer>
</developers>

<dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
import java.text.DecimalFormat;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;

/**
* Default progress bar renderer (see {@link ProgressBarRenderer}).
* @author Tongfei Chen
* @author Muhammet Sakarya
* @since 0.8.0
*/
public class DefaultProgressBarRenderer implements ProgressBarRenderer {
Expand All @@ -16,19 +18,22 @@ public class DefaultProgressBarRenderer implements ProgressBarRenderer {
private long unitSize;
private boolean isSpeedShown;
private DecimalFormat speedFormat;
private ChronoUnit speedUnit;

DefaultProgressBarRenderer(
ProgressBarStyle style,
String unitName,
long unitSize,
boolean isSpeedShown,
DecimalFormat speedFormat
DecimalFormat speedFormat,
ChronoUnit speedUnit
) {
this.style = style;
this.unitName = unitName;
this.unitSize = unitSize;
this.isSpeedShown = isSpeedShown;
this.speedFormat = speedFormat;
this.speedUnit = speedUnit;
}

// Number of full blocks
Expand All @@ -44,9 +49,9 @@ private int progressFractionalPart(ProgressState progress, int length) {

private String eta(ProgressState progress, Duration elapsed) {
if (progress.max <= 0 || progress.indefinite) return "?";
else if (progress.current == 0) return "?";
else if (progress.current - progress.start == 0) return "?";
else return Util.formatDuration(
elapsed.dividedBy(progress.current).multipliedBy(progress.max - progress.current)
elapsed.dividedBy(progress.current - progress.start).multipliedBy(progress.max - progress.current)
);
}

Expand All @@ -64,10 +69,30 @@ private String ratio(ProgressState progress) {
}

private String speed(ProgressState progress, Duration elapsed) {
if (elapsed.getSeconds() == 0) return "?" + unitName + "/s";
double speed = (double) progress.current / elapsed.getSeconds();
String suffix = "/s";
double elapsedSeconds = elapsed.getSeconds();
double elapsedInUnit = elapsedSeconds;
if (null != speedUnit)
switch (speedUnit) {
case MINUTES:
suffix = "/min";
elapsedInUnit /= 60;
break;
case HOURS:
suffix = "/h";
elapsedInUnit /= (60 * 60);
break;
case DAYS:
suffix = "/d";
elapsedInUnit /= (60 * 60 * 24);
break;
}

if (elapsedSeconds == 0)
return "?" + unitName + suffix;
double speed = (double) (progress.current - progress.start) / elapsedInUnit;
double speedWithUnit = speed / unitSize;
return speedFormat.format(speedWithUnit) + unitName + "/s";
return speedFormat.format(speedWithUnit) + unitName + suffix;
}

public String render(ProgressState progress, int maxLength) {
Expand Down
65 changes: 58 additions & 7 deletions src/main/java/me/tongfei/progressbar/ProgressBar.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@
import java.io.InputStream;
import java.io.PrintStream;
import java.text.DecimalFormat;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Spliterator;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledFuture;
Expand All @@ -36,15 +40,15 @@ public class ProgressBar implements AutoCloseable {
* @param initialMax Initial maximum value
*/
public ProgressBar(String task, long initialMax) {
this(task, initialMax, 1000, System.err, ProgressBarStyle.COLORFUL_UNICODE_BLOCK, "", 1, false, null);
this(task, initialMax, 1000, System.err, ProgressBarStyle.COLORFUL_UNICODE_BLOCK, "", 1, false, null, ChronoUnit.SECONDS, 0L, Duration.ZERO);
}

public ProgressBar(String task, long initialMax, ProgressBarStyle style) {
this(task, initialMax, 1000, System.err, style, "", 1, false, null);
this(task, initialMax, 1000, System.err, style, "", 1, false, null, ChronoUnit.SECONDS, 0L, Duration.ZERO);
}

public ProgressBar(String task, long initialMax, int updateIntervalMillis) {
this(task, initialMax, updateIntervalMillis, System.err, ProgressBarStyle.COLORFUL_UNICODE_BLOCK, "", 1, false, null);
this(task, initialMax, updateIntervalMillis, System.err, ProgressBarStyle.COLORFUL_UNICODE_BLOCK, "", 1, false, null, ChronoUnit.SECONDS, 0L, Duration.ZERO);
}

public ProgressBar(String task,
Expand All @@ -54,7 +58,7 @@ public ProgressBar(String task,
ProgressBarStyle style,
String unitName,
long unitSize) {
this(task, initialMax, updateIntervalMillis, os, style, unitName, unitSize, false, null);
this(task, initialMax, updateIntervalMillis, os, style, unitName, unitSize, false, null, ChronoUnit.SECONDS, 0L, Duration.ZERO);
}

public ProgressBar(
Expand All @@ -66,7 +70,7 @@ public ProgressBar(
String unitName,
long unitSize,
boolean showSpeed) {
this(task, initialMax, updateIntervalMillis, os, style, unitName, unitSize, showSpeed, null);
this(task, initialMax, updateIntervalMillis, os, style, unitName, unitSize, showSpeed, null, ChronoUnit.SECONDS, 0L, Duration.ZERO);
}

/**
Expand All @@ -88,7 +92,10 @@ public ProgressBar(
String unitName,
long unitSize,
boolean showSpeed,
DecimalFormat speedFormat
DecimalFormat speedFormat,
ChronoUnit speedUnit,
long startFrom,
Duration elapsed
) {
this(task, initialMax, updateIntervalMillis,
new DefaultProgressBarRenderer(style, unitName, unitSize, showSpeed, speedFormat),
Expand All @@ -102,17 +109,21 @@ public ProgressBar(
* @param task Task name
* @param initialMax Initial maximum value
* @param updateIntervalMillis Update time interval (default value 1000ms)
* @param startFrom Initial completed process value
* @param elapsed Initial elapsed second before
* @param renderer Progress bar renderer
* @param consumer Progress bar consumer
*/
public ProgressBar(
String task,
long initialMax,
int updateIntervalMillis,
long startFrom,
Duration elapsed,
ProgressBarRenderer renderer,
ProgressBarConsumer consumer
) {
this.progress = new ProgressState(task, initialMax);
this.progress = new ProgressState(task, initialMax, startFrom, elapsed);
this.target = new ProgressThread(progress, renderer, updateIntervalMillis, consumer);
// starts the progress bar upon construction
progress.startTime = Instant.now();
Expand Down Expand Up @@ -170,6 +181,24 @@ public ProgressBar maxHint(long n) {
return this;
}

/**
* Pauses this current progress.
*/
public ProgressBar pause() {
target.pause();
progress.pause();
return this;
}

/**
* Resumes this current progress.
*/
public ProgressBar resume() {
target.resume();
progress.resume();
return this;
}

/**
* Stops this progress bar.
* @deprecated Please use the Java try-with-resource pattern instead.
Expand Down Expand Up @@ -353,4 +382,26 @@ public static <T, S extends BaseStream<T, S>> Stream<T> wrap(S stream, ProgressB
return StreamSupport.stream(sp, stream.isParallel());
}

/**
* Wraps an array so that when iterated, a progress bar is shown to track the traversal progress.
* @param array Array to be wrapped
* @param task Task name
* @return Wrapped array, of type {@link Stream}.
*/
public static <T> Stream<T> wrap(T[] array, String task) {
ProgressBarBuilder pbb = new ProgressBarBuilder().setTaskName(task).setInitialMax(array.length);
return wrap(array, pbb);
}

/**
* Wraps an array so that when iterated, a progress bar is shown to track the traversal progress.
* For this function the progress bar can be fully customized by using a {@link ProgressBarBuilder}.
* @param array Array to be wrapped
* @param pbb An instance of a {@link ProgressBarBuilder}
* @return Wrapped array, of type {@link Stream}.
*/
public static <T> Stream<T> wrap(T[] array, ProgressBarBuilder pbb) {
return wrap(Arrays.stream(array), pbb);
}

}
25 changes: 24 additions & 1 deletion src/main/java/me/tongfei/progressbar/ProgressBarBuilder.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package me.tongfei.progressbar;

import java.text.DecimalFormat;
import java.time.Duration;
import java.time.temporal.ChronoUnit;

import static me.tongfei.progressbar.Util.createConsoleConsumer;

Expand All @@ -20,6 +22,9 @@ public class ProgressBarBuilder {
private long unitSize = 1;
private boolean showSpeed = false;
private DecimalFormat speedFormat;
private ChronoUnit speedUnit = ChronoUnit.SECONDS;
private long processed = 0;
private Duration elapsed = Duration.ZERO;

public ProgressBarBuilder() { }

Expand Down Expand Up @@ -64,6 +69,22 @@ public ProgressBarBuilder showSpeed(DecimalFormat speedFormat) {
return this;
}

public ProgressBarBuilder setSpeedUnit(ChronoUnit speedUnit) {
this.speedUnit = speedUnit;
return this;
}

/**
* Sets elapsed duration and number of processed units.
* @param startFrom amount of processed units
* @param elapsed duration of
*/
public ProgressBarBuilder startsFrom(long processed, Duration elapsed) {
this.processed = processed;
this.elapsed = elapsed;
return this;
}

public ProgressBar build() {
if (consumer == null)
consumer = createConsoleConsumer();
Expand All @@ -72,7 +93,9 @@ public ProgressBar build() {
task,
initialMax,
updateIntervalMillis,
new DefaultProgressBarRenderer(style, unitName, unitSize, showSpeed, speedFormat),
processed,
elapsed,
new DefaultProgressBarRenderer(style, unitName, unitSize, showSpeed, speedFormat,speedUnit),
consumer
);
}
Expand Down
21 changes: 20 additions & 1 deletion src/main/java/me/tongfei/progressbar/ProgressState.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package me.tongfei.progressbar;

import java.time.Duration;
import java.time.Instant;

/**
Expand All @@ -15,11 +16,19 @@ class ProgressState {
long max = 0;
Instant startTime = null;
String extraMessage = "";
long start = 0;
Duration elapsed;

ProgressState(String task, long initialMax) {
// 0 start current max
// [===============|=========> ]

ProgressState(String task, long initialMax, long startFrom, Duration elapsed) {
this.task = task;
this.max = initialMax;
if (initialMax < 0) indefinite = true;
this.start = startFrom;
this.current = startFrom;
this.elapsed = elapsed;
}

synchronized void setAsDefinite() {
Expand Down Expand Up @@ -64,9 +73,19 @@ synchronized long getMax() {
return max;
}

synchronized void pause() {
start = current;
elapsed = elapsed.plus(Duration.between(startTime, Instant.now()));
}

synchronized void resume() {
startTime = Instant.now();
}

// The progress, normalized to range [0, 1].
synchronized double getNormalizedProgress() {
if (max <= 0) return 0.0;
else if (current > max) return 1.0;
else return ((double)current) / max;
}

Expand Down
22 changes: 19 additions & 3 deletions src/main/java/me/tongfei/progressbar/ProgressThread.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
*/
class ProgressThread implements Runnable {

private ProgressState progress;
ProgressState progress;
private long updateInterval;
private ProgressBarRenderer renderer;
long updateInterval;
private ProgressBarConsumer consumer;
private boolean active = true;

private boolean paused;

ProgressThread(
ProgressState progress,
ProgressBarRenderer renderer,
Expand All @@ -24,6 +26,14 @@ class ProgressThread implements Runnable {
this.consumer = consumer;
}

void pause() {
paused = true;
}

void resume() {
paused = false;
}

private void refresh() {
String rendered = renderer.render(progress, consumer.getMaxProgressLength());
consumer.accept(rendered);
Expand All @@ -35,7 +45,13 @@ public void setActive(boolean active) {

@Override
public void run() {
if (!active) {
try {
while (!Thread.interrupted()) {
if (!paused)
refresh();
Thread.sleep(updateInterval);
}
} catch (InterruptedException ignored) {
refresh();
consumer.close();
TerminalUtils.closeTerminal();
Expand Down
Loading

0 comments on commit 6df1ef7

Please sign in to comment.