I have a console application in which I would like to put a non-deterministic progress bar on the command line while some heavy computations are done. Currently I simply print out a '.' for each iteration in a while loop similar to the following:
while (continueWork){
doLotsOfWork();
System.out.print('.');
}
which works but I was wondering if anyone had a better/cleverer idea since this can get to be a little bit annoying if there are many iterations through the loop.
Here an example to show a rotating progress bar and the traditional style :
import java.io.*;
public class ConsoleProgressBar {
public static void main(String[] argv) throws Exception{
System.out.println("Rotating progress bar");
ProgressBarRotating pb1 = new ProgressBarRotating();
pb1.start();
int j = 0;
for (int x =0 ; x < 2000 ; x++){
// do some activities
FileWriter fw = new FileWriter("c:/temp/x.out", true);
fw.write(j++);
fw.close();
}
pb1.showProgress = false;
System.out.println("\nDone " + j);
System.out.println("Traditional progress bar");
ProgressBarTraditional pb2 = new ProgressBarTraditional();
pb2.start();
j = 0;
for (int x =0 ; x < 2000 ; x++){
// do some activities
FileWriter fw = new FileWriter("c:/temp/x.out", true);
fw.write(j++);
fw.close();
}
pb2.showProgress = false;
System.out.println("\nDone " + j);
}
}
class ProgressBarRotating extends Thread {
boolean showProgress = true;
public void run() {
String anim= "|/-\\";
int x = 0;
while (showProgress) {
System.out.print("\r Processing " + anim.charAt(x++ % anim.length()));
try { Thread.sleep(100); }
catch (Exception e) {};
}
}
}
class ProgressBarTraditional extends Thread {
boolean showProgress = true;
public void run() {
String anim = "=====================";
int x = 0;
while (showProgress) {
System.out.print("\r Processing "
+ anim.substring(0, x++ % anim.length())
+ " ");
try { Thread.sleep(100); }
catch (Exception e) {};
}
}
}
Try using a carriage return, \r.
In GUI applications the approach is generally a spinning circle or bouncing/cycling progress bar. I remember many console applications using slashes, pipe and hyphen to create a spinning animation:
\ | / -
You could also use a bouncing character in brackets:
[-----*-----]
Of course, as the other answered mentioned, you want to use return to return to the start of the line, then print the progress, overwriting the existing output.
Edit: Many cooler options mentioned by Will in the comments:
Cooler ASCII Spinners?
If you know how much work you have to do and how much is left (or done), you might consider printing out a percent complete bar graph sort of progress bar. Depending on the scope of this project, this could simply be in ascii or you could also consider using graphics.
Related
I'm currently writing a program and I have this problem where I want to move the console's cursor to a specific location on the screen.
I quickly found out that this isn't possible in java so I wrote a C# script that would do this for me, but I only can run program in a separate process.
Is there a way to solve this?
Also I'm trying not to use any extra libraries like jline.
Here are some code snippets:
C#
using System;
namespace setCursor
{
public class program
{
static void Main(string[] args)
{
int x = Convert.ToInt16(args[0]);
int y = Convert.ToInt16(args[1]);
Console.SetCursorPosition(x ,y);
}
}
}
java
try
{
ProcessBuilder pb = new ProcessBuilder("setCursor", "0", "0");
Process p = pb.start();
p.waitFor();
for(int i = 0; i < 30; i++)
{
for(int j = 0; j < 120; j++)
{
Thread.sleep(1);
System.out.print(ContentOnTheScreen[i][j]);
}
}
}
catch (Exception e)
{
System.out.println(e);
}
Windows Terminal / console in recent version of Windows supports ANSI / VT codes so you could achieve movement of character position with System.out.print if your terminal is compatible. You will be able to tell by running this:
public class SetCursor {
private static String CSI = "\u001b[";
private static String at(int row, int col) {
return CSI+row+";"+col+"H";
}
public static void main(String[] args) throws InterruptedException {
System.out.println("HELLO");
System.out.print(at(1,1) + "ABCD");
System.out.print(at(10,5) + "EFGH");
System.out.println("WORLD");
for (int i = 0; i <= 100; i++) {
System.out.print(at(30,20) + " Progress: "+i+"%");
Thread.sleep(100);
}
}
}
It will either print the different values around the screen (running from a Windows Terminal Command Prompt), or if VT codes not supported (such as when running via IDE) the output might look strange:
HELLO
[1;1HABCD[10;5HEFGHWORLD
...
I have a Output class which just prints everything that it gets to print.
public class Output {
private static List<String> textList = new ArrayList<>();
private static Output output = null;
private Output() {
Runnable task = () -> {
int lastIndex = 0;
while (true) {
while (lastIndex < textList.size()) {
System.out.println(lastIndex + " - " + textList.size() + ": " + textList.get(lastIndex));
outputText(textList.get(lastIndex));
lastIndex ++;
}
}
};
new Thread(task).start();
}
private static void outputText(String text) {
synchronized (System.out) {
System.out.println(text);
}
}
public static void say(String text) {
if (output == null) {
output = new Output();
}
textList.add(text);
}
}
When I add something to print, everything works fine:
for (int i = 0; i < 10; i++) {
Output.say("" + i);
}
But when I add a Thread.sleep to the loop it stops on the first output:
for (int i = 0; i < 10; i++) {
Output.say("" + i);
Thread.sleep(100);
}
How can I prevent it? I mean, I'm stopping with sleep just the main thread and not the separate thread.
When you don’t synchronize threads correctly, there is no guaranty that threads see updates made by other threads. They may either completely miss updates or see only parts of them, creating an entirely inconsistent result. Sometimes they may even appear to do the right thing. Without proper synchronization (in the sense of any valid construct specified to be thread safe), this is entirely unpredictable.
Sometimes, the chances of seeing a particular behavior are higher, like in your example. In most runs, the loop without sleep will complete before the other thread even starts its work, whereas inserting sleep raises the chance of lost updates after the second thread has seen values. Once the second thread has seen a value for textList.size(), it might reuse the value forever, evaluating lastIndex < textList.size() to false and executing the equivalent of while(true) { }.
It’s funny that the only place where you inserted a construct for thread safety, is the method outputText that is called by a single thread only (and printing to System.out is synchronized internally in most environments anyway).
Besides, it’s not clear why you are creating an object of type Output that has no relevance here, as all fields and methods are static.
Your code can be corrected and simplified to
public static void main(String[] args) throws InterruptedException {
List<String> textList = new ArrayList<>();
new Thread( () -> {
int index=0;
while(true) synchronized(textList) {
for(; index<textList.size(); index++)
System.out.println(textList.get(index));
}
}).start();
for (int i = 0; i < 10; i++) {
synchronized(textList) {
textList.add(""+i);
}
Thread.sleep(100);
}
}
though it still contains the issues of you original code of never terminating due to the infinite second thread and also burning the CPU with a polling loop. You should let the second thread wait for new items and add a termination condition:
public static void main(String[] args) throws InterruptedException {
List<String> textList = new ArrayList<>();
new Thread( () -> {
synchronized(textList) {
for(int index=0; ; index++) {
while(index>=textList.size()) try {
textList.wait();
} catch(InterruptedException ex) { return; }
final String item = textList.get(index);
if(item==null) break;
System.out.println(item);
}
}
}).start();
for (int i = 0; i < 10; i++) {
synchronized(textList) {
textList.add(""+i);
textList.notify();
}
Thread.sleep(100);
}
synchronized(textList) {
textList.add(null);
textList.notify();
}
}
This is still only an academic example that you shouldn’t use in real life code. There are classes for thread safe data exchange provided by the Java API removing the burden of implementing such things yourself.
public static void main(String[] args) throws InterruptedException {
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(10);
String endMarker = "END-OF-QUEUE"; // the queue does not allow null
new Thread( () -> {
for(;;) try {
String item = queue.take();
if(item == endMarker) break;// don't use == for ordinary strings
System.out.println(item);
} catch(InterruptedException ex) { return; }
}).start();
for (int i = 0; i < 10; i++) {
queue.put(""+i);
Thread.sleep(100);
}
queue.put(endMarker);
}
I'm trying to make visual novel game but i'm stuck with text animation
I tried to make it on console application for example so help me with making it on libgdx.
here's my sample code
public class TestC {
private static String message = "help me with textanimation";
public static void main(String[] args) {
for (int i = 0; i < message.length(); i++) {
System.out.print(message.charAt(i));
try {
Thread.sleep(50);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Thanks in advance
Why don't you try to make it work with LibGDX and then ask help? It goes basicaly the same except instead of letting the program sleep you have to count time passed.
final float letterSpawnTime = .2f;
float timer = 0;
String completeText = "The complete text."
String drawText = "";
int stringIndex = 0;
public void update(float delta) {
timer += delta;
if (timer >= letterSpawnTime) {
drawText = drawText + completeText.charAt(stringIndex);
stringIndex++;
timer -= letterSpawnTime;
}
font.draw(someFont, drawText, x, y, etc...);
}
Something like that (written out of my head). To be a bit more efficient initialize the StringBuilder in the constructor once and just keep appending a single character to it instead of creating a new StringBuilder instance each time the you need to append a letter.
I'm writing a small function that will update my ProgressBar and a JLabel base on weighted random integers. The thread is sleeping correctly and the random values are coming through, however, my progress bar and jlabel are not updating.
It is also leaving my GUI unresponsive. It isn't locking my GUI completely (I'm able to click buttons, but it doesn't execute the functions associated with those buttons).
Throwing a print statement in the run() method is not printed out. So I'm thinking it never gets in there.
Here is my method so far:
private void updateProgressBar(int max) {
final int maxDiamonds = max;
int i = 0;
while(i <= max) {
//get random obstacle found
final Obstacle o = obstacle.next();
System.out.println("Obstacle: " + o.getName());
//get random number of diamonds for obstacle
int numDiamonds = Integer.parseInt(numFoundDiamonds.next());
System.out.println("Number of Diamonds: " + numDiamonds);
//set currentValue for the progress bar
final int currentValue = i;
for(int j = o.getRemovalTime(); j >= 0; j--) {
final int rSecs = j;
System.out.println("Time remaining to remove: " + rSecs);
try {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
progressBar.setString(currentValue + "/" + maxDiamonds);
statusLabel.setText("Found " + o.getName() + ". Removing: " + rSecs + " s Remaining.");
}
});
java.lang.Thread.sleep(1000);
} catch (InterruptedException e) {
JOptionPane.showMessageDialog(this, e.getMessage());
}
}
i += numDiamonds;
}
}
Perhaps I'm overlooking something simple.
I can also provide more code regarding the other classes used, but I feel that the issue lies in the code provided.
You're calling Thread.sleep(...) on the Swing event thread which will put the whole Swing thread and application to sleep. Use a background thread such as with a SwingWorker.
I am learning java so bear with me on this if it seems basic. I have a method which I am trying to edit to return a value which is 'read in' - I am trying to return 'move'. However, due to the setup of the code the return falls outside the code block and forces me to return a null. Can someone edit the code so that it returns the 'move' value? I have been working on this for 2 days and I can't work it out - the try and catch seem to be causing the problem
public Move listenToEngineMove()
{
synchronized(engineReadBuffer)
{
int numRows=engineReadBuffer.size();
if(numRows==0);
for(int kk=0; kk<numRows; kk++)
{
String row=engineReadBuffer.get(kk);
row=row.toLowerCase();
if((row.contains("move "))||(row.contains(" ... ")))
if((!row.contains("illegal"))&&(!row.contains("error")))
try {
String[] tokens=row.replaceAll("\\<.*\\>"," ").split("\\s+");
Move move = new Move(tokens[tokens.length-1]);
jcb.makeAIsMove(move);
System.out.println("thread.... " + row);
}
catch (Exception x) {
System.out.println("Exception! : "+x.getMessage());
}
}
engineReadBuffer.clear();
}
return null;
}
Try this:
public Move listenToEngineMove() {
Move move = null;
synchronized (engineReadBuffer) {
int numRows = engineReadBuffer.size();
if (numRows == 0) ; // what on earth is this?
for (int kk = 0; kk < numRows; kk++) {
String row = engineReadBuffer.get(kk);
row = row.toLowerCase();
if ((row.contains("move ")) || (row.contains(" ... ")))
if ((!row.contains("illegal")) && (!row.contains("error")))
try {
String[] tokens = row.replaceAll("\\<.*\\>", " ").split("\\s+");
move = new Move(tokens[tokens.length - 1]);
jcb.makeAIsMove(move);
System.out.println("thread.... " + row);
} catch (Exception x) {
System.out.println("Exception! : " + x.getMessage());
}
}
engineReadBuffer.clear();
}
return move;
}
I'd recommend that you replace this:
catch(Exception x){System.out.println("Exception! : "+x.getMessage());}
with this:
catch(Exception e){
e.printStackTrace(); // Or, better yet, logging with Log4J
}
The complete stack trace gives more info than the message.
This line looks like a mistake to me. The semi-colon at the end looks out of place.
if (numRows == 0) ; // what on earth is this?
Your code looks awful. I find it hard to read, because you aren't consistent with your indentation and general code style. Style matters; it makes your code easier to read and understand. Adopt a better style and stick with it.
You will need to move 'Move' just inside synchronized block, It is important to keep it inside synchronized block to stay thread safe.
public Move listenToEngineMove()
{
synchronized(engineReadBuffer)
{
Move move =null;
int numRows=engineReadBuffer.size();
if(numRows==0);
for(int kk=0; kk<numRows; kk++)
{
String row=engineReadBuffer.get(kk);
row=row.toLowerCase();
if((row.contains("move "))||(row.contains(" ... ")))
if((!row.contains("illegal"))&&(!row.contains("error")))
try {
String[] tokens=row.replaceAll("\\<.*\\>"," ").split("\\s+");
move = new Move(tokens[tokens.length-1]);
System.out.println("thread.... " + row);
}
catch(Exception x){System.out.println("Exception! : "+x.getMessage());}
}
engineReadBuffer.clear();
return move;//this is inside synchronized block
}
}