views:

222

answers:

5

I'm writing a little genetic algorithm in Java, as a school assignment. Up until now I've pretty much stuck to doing console applications. However I think a UI would be really helpful for this program, so I'd like to make one. I'm having trouble figuring out how to reconcile a GUI which is event-driven, and a console application which has a beginning and end.

Ideally I'd like to have a bunch of text boxes for settings, and then a Start button. Once you hit Start, the algorithm would start running and the GUI would update on a set interval with the latest program state. How the heck do I accomplish this without the algorithm freezing the GUI or vice-versa? I don't want either one waiting on the other.

How do I get my main loop to not freeze the GUI while the algorithm is running? I assume they need to be in separate threads, but I've never messed with threads before. That seems too complex for this task, which must be commonplace.

+6  A: 

You're on to something with threads. GUI programming mandates threads, in most cases -- luckily, Java's threading API isn't too terrible (Python's is modeled on it, so it's doing something right).

Don't be intimidated by threading, though -- it's intermediate, I'd say, but is something that every programmer should understand.

There's a lot of information out there that would predispose you against threads. GUI applications, however, are one area where they are phenomenally useful. Opponents of threading would lead you to believe that an event-programming model would help you in this case, when really, it will not. The solutions that most who say "threading sucks" propose are often worse than threading itself.

You could try to kludge your solution into a single thread, but it would require your CPU-intensive code to yield to the GUI at a predictable interval. That solution sucks. EDIT: Since others are suggesting that approach, let me elaborate on why it sucks: Unbeknownst to you, something is always being updated in a GUI. When you move a window on top and then back off, the entire region under that window is invalidated and code must execute -- in your process -- to redraw that section. Even if you are updating the GUI very quickly, this provides a negative user experience as simple GUI operations block entirely. Buttons highlight when you mouse over, sometimes. A user right clicks. All of these things require CPU time to implement, and if your solitary thread is chewing away on your GA, they will not happen. GUI code being executed is not only your code.

Here's what appears to be a very useful article on the topic.

Two lessons in the trail on the topic are:

  1. Concurrency
  2. Concurrency in Swing
Jed Smith
+1 for Concurrency in Swing article
Taylor Leese
+3  A: 

When I was writing a ray-tracer for one of my computer graphics classes in college, I had a long-running task, and I wanted to update the display periodically as the tracer was drawing. I used two separate threads - one thread sleeps, and updates (say every 500 ms); the other thread does the actual raytracing. The key is to synchronize on a common object - in my case, accessing my image buffer was the point of synchronization (one thread can't make changes to the image buffer without first waiting until the other thread is done reading).

For your GA processing, you might have something like this (pseudocode):

Supposing you have some object, generationHistoryObject, which stores the state that you want to display in your GUI, then:

(in Thread #1:)

Generation newGeneration = doMutationAndTestThisGeneration(lastGeneration);
synchronized (generationHistoryObject) {
 generationHistoryObject.updateWithNextGeneration(newGeneration);
}

(in Thread #2:)

while (!programIsDone()) {
 synchronized (generationHistoryObject) {
  drawGuiForCurrentState(generationHistoryObject);
 }
 Thread.sleep(500);
}

The idea is that you do the time-consuming work for each generation in isolation, then update the part that the GUI has to access in the synchronized block (making the GUI wait to draw until the update is done).

RMorrisey
I can just slap on "synchronized" and two threads can access the same object? That seems too easy!
Ross
@Ross: After you dig deep into threading -- say, in about a year -- promise me that you'll come back to SO and read that comment to yourself again.
Jed Smith
It's a promise.
Ross
It's almost that simple... synchronized(object) will prevent two threads from entering a block that is synchronized on the same *instance* of an object. If you are synchronizing on multiple resources, you have to be careful about threads getting stuck in a deadlock, but you probably don't need to worry here. I should mention that my raytracer used JOGL, which does its rendering passes in a while loop, and isn't really event driven in the same manner as Swing.
RMorrisey
Definitely check out some of the articles on concurrency and swing that other people have posted, too.
RMorrisey
In your example, it would be preferable if Thread #2 is not the Event Dispatch Thread (EDT). Calling Thread.sleep(500) on the EDT will result in a less-responsive UI. A better option is to replace Thread #2 with a javax.swing.Timer, which accomplishes the same thing but doesn't block the EDT. Note that if you use a javax.swing.Timer (as opposed to a java.util.Timer) its action will be invoked on the EDT.
Sam Barnum
+1  A: 

Since your application has to do with genetic algorithms, you could update the GUI every generation or so. You could accomplish this by implementing a next() method in your algorithm code, and calling it from the GUI. This should be simple enough.

However, if you really don't want the GUI to freeze while waiting for the algorithm, you should go for threads.

carnieri
+4  A: 

Sorry - it would seem like background tasks would be an easy and obvious sort of thing. Unfortunately, the Java Swing GUI threading model is a bit complicated. There have been some improvements in this area, but one still has to have some knowledge of threading first.

If you have time, I'd suggest reading the chapter on threading in Filthy Rich Clients - Painless Threading through SwingWorker.

alt text

If your impatient, just read the JavaDoc on SwingWorker. If you're really impatient, just copy the meaning of life example from the JavaDoc sample usage.

brianegge
+2  A: 

Your problem with Swing is that it is single threaded (which is a good thing) so you want to get your work out of the Swing thread so your application stays responsive.

The thing you need to do is to convert your core algorithm to a Runnable as it can be handled easily by a SwingWorker and with the newer Executors (see Executors for a lot of preconfigured ones). You can also create investigate how to make a PrintStream to a JTextPanel so you can just use standard println statements to output your current status information.

If you want to add a stop button you need to understand the thread model, so you know how to control it. The Java Tutorial has good material on this as well as Swing programming in general. Strongly recommended.

http://java.sun.com/docs/books/tutorial/uiswing/concurrency/index.html

Thorbjørn Ravn Andersen