I am writing a genetic algorithm that approximates an image with a polygon. While going through the different generations, I'd like to output the progress to a JFrame. However, it seems like the JFrame waits until the GA's while loop finishes to display something. I don't believe it's a problem like repainting, since it eventually does display everything once the while loop exits. I want to GUI to update dynamically even when the while loop is running.
Here is my code:
while (some conditions) {
//do some other stuff
gui.displayPolygon(best);
gui.displayFitness(fitness);
gui.setVisible(true);
}
public void displayPolygon(Polygon poly) {
BufferedImage bpoly = ImageProcessor.createImageFromPoly(poly);
ImageProcessor.displayImage(bpoly, polyPanel);
this.setVisible(true);
}
public static void displayImage(BufferedImage bimg, JPanel panel) {
panel.removeAll();
panel.setBounds(0, 0, bimg.getWidth(), bimg.getHeight());
JImagePanel innerPanel = new JImagePanel(bimg, 25, 25);
panel.add(innerPanel);
innerPanel.setLocation(25, 25);
innerPanel.setVisible(true);
panel.setVisible(true);
}
However, it seems like the JFrame
waits until the GA's while loop
finishes to display something. I don't
believe it's a problem like repainting
Yes, if the looping code execute on the EDT then the GUI can't repaint itself until the loop finishes. The looping code should execute in its own Thread so it doesn't block the EDT.
Read the section from the Swing tutorial on Concurrency for more information.
I think your problem is that Java won't let you update the GUI from another thread than the GUI thread itself. This causes grief to everybody at some point, but fortunately a reasonably convenient workaround is provided.
The idea is to pass the code that does the updating as a Runnable to the method SwingUtilities.invokeAndWait or SwingUtilities.invokeLater. Here's an example.
To run your GA at maximum speed and exploit parallelism, I guess invokeLater would be appropriate.
EDIT: Oh wait, camickr's solution hints that you're doing something else: You're running the GA in the GUI's thread. Well, that can only do one or the other, calculate or display. So the true solution would combine both changes:
Run the GA in a separate thread (you could run it in the thread used by main() after you've instantiated the GUI); and
Use invokeLater to communicate updates to the GUI thread (which camickr calls the EDT, or Event Dispatch Thread).
Related
I'm currently writing a application, which on a mouse click runs several methods which updates a JtextArea. The problem is even though I'm updating the text area with each method call, it doesn't actually update until everything in the mouseclick has run..
This can take quite a while to run through everything and I would like to see the text area update with each call instead of waiting until everything is done
public void mouseClicked(MouseEvent e) {
DataCollector dc = new DataCollector();
dataCollected.append("Begining Test...\n\n");
dataCollected.append("Collecting System Information... \n\n");
dataCollected.append(dc.getSystem());
... lots more like this...
}
it doesn't actually update until everything in the mouseclick has run.
That's totally correct. Your mouseClicked method is called on the GUI thread and this thread is the only thread which updates the GUI. So your "update textarea content" action is executed after your mouseClicked method finishes. Therefore your methods which run on the GUI thread should run extremely quick so other methods which wants to run on the GUI thread can do so.
You can start a new thread which runs in parallel to your normal code which will update your JTextArea. Read Lesson: Concurrency in Swing on how to work with threads in swing (and what the "Event Dispatch Thread" is).
I have a JLabel which I want to change momentarily, here is the code I have written to do so:
infoLabel.setText("Added");
try {
TimeUnit.MILLISECONDS.sleep(300);
}
catch(InterruptedException ex)
{
}
infoLabel.setText("Action"); //funny part is when I comment this line it works
My default text for the label is 'Action'
Swing is a single threaded frame work, that means, if you do anything that stops this thread, then it can't respond to any new events, including paint requests.
Basically, TimeUnit.MILLISECONDS.sleep(300) is causing the Event Dispatching Thread to be put to sleep, preventing it from processing any new paint requests (amongst other things).
Instead, you should use a javax.swing.Timer
Take a look at
Concurrency in Swing
How to use Swing Timers
For more details
For example...
infoLabel.setText("Added");
Timer timer = new Timer(300, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
infoLabel.setText("Action");
}
});
timer.setRepeats(false);
timer.start();
Note, 300 milliseconds is a really short time, you might like to start with a value a little larger like 2000, which is 2 seconds ;)
You're sleeping the Swing event thread putting the entire GUI to sleep. Don't do that. Use a Swing Timer instead.
Your application is run on a single thread, so when you sleep the thread, you prevent it from making any GUI updates.
Are you sure you are doing things properly? By doing everything (including sleep) in the GUI thread, it will always be busy and never get back to Java in order to let the GUI be redrawn.
Search for EDT (Event dispatch thread) for more info. Here is one question on the subject: Processing code doesn't work (Threads, draw(), noLoop(), and loop())
So I'm making a graphical card game. Each card is a JPanel with a button and two images associated with it. I have a flip method, which is the first thing I call in my action listener when a card is clicked on.
public void flip()
{
if(b1.getIcon() == card2) b1.setIcon(card1);
else b1.setIcon(card2);
revalidate();
repaint();
}
However, for some reason the card doesnt flip (meaning the icons don't change) until some point after I call this method. For example, when I put a Thread.sleep after I call flip, I would assume that my program would pause after flip is completed, but it doesnt! It sleeps with the images not yet switched, and only switches them some at point after the sleep time has ended.
This is causing some major problems when I have a human play an AI in this card game, because AI events are happening before cards flip on the screen, even though I am calling flip() before I do any AI code. Can anyone clue me in as to what is going on here?
Repaint is a request to Swing to redraw not a call to redraw. Under the covers repaint() is posting a repaint job onto a queue of events. Along with that repaint job things like mouse moves, mouse clicks, keyboard activites, etc are posted there too. The Swing thread comes by and periodically executes jobs from that queue to redraw the UI, deliver mouse and keyboard events to components, etc. In fact the swing thread delivers these events quite frequently, but it just depends on how much work your UI is doing on that Swing thread. When it's out delivering mouse clicks, keyboard events, and redraws it can't read from that queue. So if your code takes a long time to repsond to any event the ability for swing to respond timely to new events is reduced or all together blocked.
That's why if you execute a blocking service call over the network with the Swing event dispatch thread your UI stops drawing until that call returns. This is why its important to move long running jobs like network calls off the Swing Thread, and use SwingUtilities.invokeLater() when the call comes back so you can update the UI on the Event thread. (invokeLater() does this by posting a job onto the Swing event queue we talked about above).
This is also why it's a terrible idea to call sleep on the Swing event thread. If you put that thread to sleep it can't service events from the queue. And the repaint() you requested isn't going to be done while the Swing thread is sleeping.
First remove your sleep call. Second. Don't write code that depends on painting to finish before executing more logic. Repaint and revalidate are special calls, and Swing will combine requests to repaint/revalidate so it doesn't redraw 10 times in a row (wasting vital cpu time).
Swing can't redraw your panel until you let the thread that called you leave the method it first called you back on. The sooner the thread leaves that method the sooner your repaint() will happen.
You haven't explained why you want the repaint to happen immediately so I can't give you any advice on how to structure your code so you don't care about it. But I'm telling you, you shouldn't care that the repaint hasn't happened yet.
Totally agree with #chubbsondubs. I just wanted to add that if your goal is to have some event happen some time after flip is called, consider using a swing timers. As you are painfully aware, calling Sleep does not have the desired effect. But if instead, you
create a timer for say 1 second
create an action to be performed when the timer fires
start the timer
then you can create the delay that you want without blocking the event dispatch thread.
public void flip()
{
if(b1.getIcon() == card2) b1.setIcon(card1);
else b1.setIcon(card2);
revalidate();
repaint();
javax.swing.Timer t = new javax.swing.Timer(1000, new ActionListener() {
public void actionPerformed(ActionEvent e) {
// do something interesting
}
});
t.repeats(false);
t.start();
}
I'm just getting to grips with GUI programming in java. Here is a trivial program (from O'Reilly's "Head First Java") which on the face of it looks easy to understand, but there's an aspect of it which I don't follow.
import javax.swing.*;
public class Test {
public static void main(String[] args) {
JFrame frame=new JFrame();
JButton button = new JButton("click me");
frame.getContentPane().add(button);
frame.setSize(300,300);
frame.setVisible(true);
}
}
This simple program, when compiled and run, will open a window with a button on it.
What I don't understand is what is happening with the flow of execution. When I run this program, the static main method of the Test class runs, all the commands in main() are executed -- so why doesn't the process terminate after the window appears? Why am I still sitting on what looks like an infinite loop? What is looping?
If I add the line
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
then I find the result even more imcomprehensible. Now, of course, the program terminates
once I've closed the window. But again I don't see why. The frame will be on the stack but I don't see where the program flow is and just the existence of something on the stack is not enough to keep the program alive, surely? I'm missing something fundamental which as far as I can see is not covered in the book I'm reading. I am slightly surprised by this -- "Head first Java" has been very good up until now at pointing out subtleties and explaining what is really going on, but doesn't seem to address this point (at least not that I've spotted).
why doesn't the process terminate after the window appears?
Because the Java Virtual Machine exits only after all non-daemon threads have finished. While not apparent, there's in fact two threads in your program: the main thread, and the event dispatching thread, which does everything related to the Swing GUI components. The event dispatching thread keeps going as long as any GUI components are visible.
Actually the program, while it may work, is wrong, because you're creating and accessing Swing components from the main thread. You ought to be doing all GUI work in the event dispatching thread. That is, it should be something like:
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
JFrame frame=new JFrame();
JButton button = new JButton("click me");
frame.getContentPane().add(button);
frame.setSize(300,300);
frame.setVisible(true);
});
}
The Java process terminates when the last non-demon thread dies. Normally there is just one, the main thread. When you display Swing components additional non-demon threads for event dispatching and GUI shotdown are started. Those terminate when the last top-level component gets disposed. In your sample the main thread dies after leaving the main method. You can have a look into the threads with a debugger or jvisualvm from the JDK tools.
The rest of the GUI flow is event driven. When you e.g. click on the frame's close button an event is generated and sent to the appropriate listeners within the event dispatching thread.
Setting JFrame.EXIT_ON_CLOSE as default close operation is like adding a default event listener to the frame. A quite harsh one, it just shuts down the JVM without respect to the rest of the application's state.
Ive coded two Computers to play each other in Reversi, however when they play each other. The board only updates after the game has finished.
From some googling around I know its has something to do the AWT Event Thread, but I still have no idea how to force the JFrame to refresh.
My function works by changing the icons and then calling revalidate and repaint.
Any pointers would be wonderful.
If you start your AI game from an actionPerformed(), it is executed in EDT thread. You should move your logic (and your sleep()'s outside of EDT thread by starting a new Thread to allow Swing to repaint UI properly and post updates to UI as following:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
myUI.update(); // repaint(), etc. according to changed states
}
});
Also consider use of javax.swing.SwingWorker, javax.swing.Timer and take a look at Concurrency in Swing.
Sounds like a threading issue - Event Thread is not being given a chance to execute. I would start with some sleep commands in the logic / action threads to give time for the UI to update.
Also, you could have an "updateUI" thread and run that on an invokeAndWait to force it to update the display. Call that after each move has been completed.
If you need to force the application to redraw, you should invoke the repaint() method on the component containing the game board. This should cause Swing to repaint the board.