We closed this forum 18 June 2010. It has served us well since 2005 as the ALPHA forum did before it from 2002 to 2005. New discussions are ongoing at the new URL http://forum.processing.org. You'll need to sign up and get a new user account. We're sorry about that inconvenience, but we think it's better in the long run. The content on this forum will remain online.
IndexProgramming Questions & HelpSyntax Questions › Background task with progress bar
Page Index Toggle Pages: 1
Background task with progress bar (Read 1272 times)
Background task with progress bar
Nov 23rd, 2009, 6:41am
 
Hi,

I'm working on sketch with a Background thread, which updates a progress bar in the main thread. But I'm fairy new to all this java stuff.

The goal is to perform heavy calculations, based on where the user clicked, but without the sketch locking up. Also I want to display a small progress bar to indicate how far the calculation is proceeding.

I tried to use SwingWorker because from the description, this seemed to do exactly what I needed, but I couldn't get it to work with processing, so I gave up.

In the end I managed to create the following sketch with FutureTask, but I have still some questions:
  • 1. to update the progress bar I call getProgress() from the main thread. But since the background thread is also manipulating completedPercentage in setProgress(int, int) I'm not sure if this is (thread)safe. Is there a better or safer way to get progress feedback from a background thread like this?
  • 2. This code is not yet doing any heavy calculation. It's mainly sleeping and reversing a string. When it would be doing much more intensive calculations (e.g. calculate prime numbers based on the mouse position) do I then need to keep a Thread.currentThread().sleep() statement in the code, to allow the main thread to do its drawing? And how long should it sleep for then?


The code:

background_process_sketch.pde
Code:

import java.util.concurrent.*;

boolean backgroundTaskRunning = false;
int progressBarValue = 100; // from 0 to 100
ExecutorService executor;
BackgroundTask task;

void setup() {
 size (300, 300);
 smooth();
 noStroke();
}

void draw() {
 // try every loop
 if (backgroundTaskRunning) {
   if (!task.isDone()) {
     //println("Task not yet completed.");
   } else {
     println("Here is the result..." + task.getResult());
     executor.shutdown();
     backgroundTaskRunning = false;
   }
 }
 
 background(150);
 displayProgressBar();
 fill (255);
 ellipse (mouseX, mouseY, 20, 20);
}

void mousePressed() {
 if (!backgroundTaskRunning) {
   println("Start a background process");
   executor = Executors.newSingleThreadExecutor();
   task = new BackgroundTask("foobar");
   executor.execute(task.future);
   backgroundTaskRunning = true;
 }
}

void displayProgressBar() {
 if (backgroundTaskRunning) {
   int progressX = 195;
   int progressY = 290;
   int progressHeight = 8;
   fill (100);
   progressBarValue = task.getProgress();
   rect(progressX, progressY, progressBarValue, progressHeight);
 }
}

void displayAnswer(int answer) {
 println("We have an answer: ");
 print(answer);
}


BackgroundTask.java
Code:

//BackgroundTask.java

import java.util.concurrent.*;

public class BackgroundTask
{
 SlowStringReverser reverser = new SlowStringReverser();
 FutureTask<String> future;
 int completedPercentage;
 
 BackgroundTask(String argument) {
   completedPercentage = 0;
   try {
     createTask(argument);
   } catch (InterruptedException ie) {
     System.out.println(ie);
   }
 }

 void createTask(final String target) throws InterruptedException {
   future = new FutureTask<String> (
     new Callable<String>()
     {
       public String call()
       {
         return reverser.reverseString(target);
       }
     });
 }
 
 boolean isDone() {
   return future.isDone();
 }
 
 String getResult() {
   try {
     try {
       return future.get();
     } catch (ExecutionException ex) {}
   } catch (InterruptedException ie) {
     System.out.println(ie);
   }
   return "";
 }

 int getProgress() {
   return completedPercentage;    
 }
 
 void setProgress(int currentValue, int maxValue) {
   completedPercentage = (currentValue * 100) / maxValue;
 }
 
 
 //-----------------------------

 class SlowStringReverser {
   StringBuffer orgString;
   StringBuffer reversedString;

   SlowStringReverser(String orgStr) {
     orgString = new StringBuffer(orgStr);
   }

   SlowStringReverser() {
   }

   public String reverseString(String str) {
     orgString = new StringBuffer(str);
     reversedString = new StringBuffer();
     for (int i = (orgString.length() - 1); i >= 0; i--) {
       reversedString.append(orgString.charAt(i));
       //setProgressBarValue((orgString.length() - i), orgString.length());
       setProgress((orgString.length() - i), orgString.length());
       System.out.println("Reversing one character per second." + reversedString);
       try {
         Thread.currentThread().sleep(1000);
       } catch (InterruptedException ie) {}
     }
     return reversedString.toString();
   }
 }
}



Thanks!
Dirk
Re: Background task with progress bar
Reply #1 - Nov 23rd, 2009, 10:15am
 
Dirk wrote on Nov 23rd, 2009, 6:41am:
  • 1. to update the progress bar I call getProgress() from the main thread. But since the background thread is also manipulating completedPercentage in setProgress(int, int) I'm not sure if this is (thread)safe. Is there a better or safer way to get progress feedback from a background thread like this
  • 2. This code is not yet doing any heavy calculation. It's mainly sleeping and reversing a string. When it would be doing much more intensive calculations (e.g. calculate prime numbers based on the mouse position) do I then need to keep a Thread.currentThread().sleep() statement in the code, to allow the main thread to do its drawing And how long should it sleep for then



1. Yes, this is perfectly thread-safe. You only write from one thread, and read from another. It would be a problem if you write from two threads.

2. I think if you set a fixed framerate, there is no need for a sleep. (Rule of thumb: if you _need_ a sleep, you are doing it wrong)
Re: Background task with progress bar
Reply #2 - Nov 23rd, 2009, 3:47pm
 
Ah, Thanks a bunch!
I'm now skipping the sleep() and suddenly it's a challenge to write background threading code that will actually take so long to witness the progress bar filling up.
 Smiley

Now I ran into another problem, which is somewhat related.
How can I access sketch classes (defined in a .pde file) from this .java file?

Say I have a TestClass.pde
Code:

class TestClass {
 int foo;
 
 TestClass (int myfoo) {
   foo = myfoo;
 }
 [...]
}


and now I would like to use this class in the BackgroundTask.java of above.

If I mention the TestClass in the class BackgroundTask it complains:
Cannot find a class or type named "TestClass".
And import TestClass tells me:
The package "TestClass" does not exist. [...]

How can I use import this .pde (sketch) class in the .java code?

thanks,
Dirk
Re: Background task with progress bar
Reply #3 - Nov 24th, 2009, 11:12am
 
The TestClass class is an innerclass of a subclass (your project) of PApplet if I'm not mistaken.

So refer to it as <name_of_you_project>.TestClass.

If you have created a project called TestProject:
TestProject.TestClass should work.
Re: Background task with progress bar [SOLVED]
Reply #4 - Nov 24th, 2009, 4:04pm
 
Thanks,
that works to access the class.

It was a bit more tricky to also create new instance of that TestClass. But I also managed to do that by supplying an instance of my sketch class. In the .pde I create the background task like this:
Code:

// name_of_my_sketch.pde
task = new BackgroundTask(this, calculateFromValue);


And then in the .java file
Code:

// BackgroundTask.java
 BackgroundTask(name_of_my_sketch mainSketch, int arg) {
   tc = mainSketch.new TestClass(42);


Thanks a lot.
Page Index Toggle Pages: 1