My gui class extends JFrame. GUI contain two JButton say (start_button and view_button).
JButton start_button;
JButton view_button;
The action to be performed after pressing start_button takes some time(ranging from 2 min to 5 min).
start_button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//...
//action executing. May take 2 to 5 min to complete.
}
});
While this action is executing, I want to see the status log of this action in separate TextPane enclosed inside JDialogue. And this JDialogue is made visible by pressing the view_button.
view_button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
myJDialogue.setVisible(true);
}
});
However I am not able to access any of the gui component unless and until the action performed by start_button is finished. And hence I can't check the live status log by pressing view_button.
How can I do so while one action is running ? Please help. Thanks.
You need to execute your long running task in a separate Thread so you don't prevent the GUI from responding to events.
Read the section from the Swing tutorial on Concurrency for more information. Maybe a SwingWorker is the easiest approach as it creates the Thread for you and notifies you when the Thread is finished so you can update the GUI.
Related
I'm not confident with Java, but I'm developing a tool for monitoring our company Server HDisks because of some problems we have.
I've been creating a simple frame with a simple menu and a JPanel with some information, but it is not important.
My idea is simply: press the JButton of the JFrame, then my service (which is a class extends Thread and sleeps for a while each cycle) starts.
I made all the classes, but I have this problem: when I press the button, the background class starts, but I lose control of the main frame.
How can I separate the frame class between my background class?
then my service (which is a Class extends Thread and sleeps for a while each cycle) starts. I made all the classes, but I have this problem: when I press my JButton, the background class starts, but I loose control of the main JFrame.
Not exactly sure what "loose control of the main JFrame" means. But I'm guessing it no longer responds to user events.
This would be because your Thread.sleep is causing the Event Dispatch Thread (EDT) to sleep, so you are either:
not creating the Thread correctly, or
sleeping the wrong Thread.
In any case I would suggest that in the ActionListener you add to your button that you start a SwingWorker. A SwingWorker will create the Thread for you and it allows you to communicate properly with the frame.
Read the section from the Swing tutorial on Concurrency for more information and examples to get you started. The tutorial will explain more about the EDT.
Could you provide more information?
But for what I understood you should add an event listener to your JButton.
Example code:
BackGroundThread task = new BackGroundThread();
JButton btn = new JButton("Run task");
btn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
task.start();
}
});
Through some searching, I made about ActionListeners and GUI. I think I have come to a conclusion that all the changes you do that affect the GUI, in the ActionListener, actually happen when ActionListener comes to it's end
I am currently making a memory game. In the action listener of a card button I first flip the image (that changes it's icon) and the I check if a have a match and in the case I don't have one I close the card(change icon).
Since those two happen in the same actionListener as a result if I dont have a match I only see the first card I pick and he second seems to no react to flip.
Sorry if it's confusing how I explain it. I think that the problem is the actionListener but maybe I am wrong. What would you suggest?
c.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent ae){
selectedCard = c;
String imgName = "Images/"+c.getId()+".jpg";
URL imageURL = this.getClass().getResource(imgName);
if (imageURL != null) {
ImageIcon icons = new ImageIcon(imageURL);
c.setIcon(icons);
}
c.setEnabled(false);
c.setDisabledIcon(c.getIcon());
pl.singlePlay(c);
if(pl.getMatch()==false){
for (Cards cd : cards){
if(cd.getMatched()==false){
cd.setEnabled(true);
cd.setIcon(icon);
}
}
}
});
I have come to a conclusion that all the changes you do that affect the GUI, in the ActionListener, actually happen when ActionListener comes to it's end
Yes, all code invoked from any listener executes on the Event Dispatch Thread (EDT). This thread is responsible for updating the GUI and so the GUI can't be updated until the code has finished executing. Read the section from the Swing tutorial on Concurrency for more information.
Since those two happen in the same actionListener
If you want to schedule animation, that is show one Icon and then seconds later show another Icon, then you can use a Swing Timer. The Timer allows you to schedule another event. The tutorial link from above also has a section on How to Use Swing Timers to get you started.
In Java, I'm using an ActionListener for an array of JButtons. I would like for an earlier part of the ActionListener to set a new ImageIcon to a JButton, that change to be displayed immediately, then near the end of the ActionListener to set the JButton's ImageIcon back to null after a second long delay.
My problem is that none of the changes that happen to the JButton get displayed in the GUI window that it is set in until the ActionListener is completely finished, making the change in the JButton's ImageIcon unnoticeable. Is there any way to make an ActionListener commit a change to a JButton before it has finished executing the entire ActionListener, or should I be going about this differently?
The reason this is happening:
Swing repaints the buttons on the same thread (EDT) as the ActionListener is ran on. Hence if it is executing you ActionListener it cannot repaint since the thread is busy - as simple as that. You may have noticed that while your action listener is executing you also can't properly move your frames around etc. (GUI freezes up).
The solution:
Move heavy processing outside the EDT. It doesn't belong there anyway. As you could have guessed - use a background thread/thread pool for that. A good guide to it is Swing tutorial for concurrency
Notes:
As portrayed in the guide you do not want to modify components outside the EDT. As such the easiest strategy is to make a Runnable to execute on a background thread, start it, change the picture on the button and return without waiting for the task to finish.
public void actionPerformed(ActionEvent ae) {
Runnable task = new Runnable() {..};
executor.execute(task);
button.setIcon(newIcon);
return;
}
Note that this doesn't lock up the EDT for the task, hence allowing Swing to change the picture immediately.
This of course means that the user has no idea if the task has finished or not (And if there were any exceptions)! It is in the background after all! Hence there is an extra state of your execution: GUI is responsive and non-frozen, button is changed, but task is still running. In most applications this may be a problem (the user will spam the button or your background tasks may interleave). In that case you may want to use a SwingWorker to have a "processing" state as well.
public void actionPerformed(ActionEvent ae) {
new TaskWorker().execute();
button.setIcon(loadingIcon); //Shows loading. Maybe on button, maybe somewhere else.
return;
}
private class TaskWorker extends SwingWorker<Void, T> {
public Void doInBackground() {
//Do your task in the background here
}
protected void done() {
try {
get();
button.setIcon(doneIcon);
catch (<relevant exceptions>) {
button.setIcon(failedIcon);
}
}
}
Here done() is called on the EDT when doInBackground() is finished.
You could create a new Thread or a Thread from a Thread pool if you have one. And with that let the task work on a seperate Thread so that the ActionListener returns immediatly. And then in the other Thread you do your code and repaint the button. This is by the way a threory I'm not sure if it will work.
I am very new to Swing.
I have
itemActionButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg) {
itemAction();
}
});
But when the button is clicked, instead of running this action on another thread, I would like the parent's form's thread to wait until it the action is completed before refreshing, allowing additional clicks, etc.
How can I do this?
Code in the ActionListener executes on the EDT, which prevents the GUI from repainting and responding to other events.
If you have a long running task and you don't want to block the EDT then you need to use another Thread.
Read the section from the Swing tutorial on Concurrency for more information and a solution by using a SwingWorker.
I have a Blackjack game that I've made in Java and I want to signal the start of the game by clicking a button. All my action listeners work just fine and all that, but the problem lies in that I can't figure out how to start the game without it running completely within the actionPerformed method. Obviously, a function continuously running within the actionPerformed method will effectively disable the rest of my GUI. Here's a code snippet....
go.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
// START GAME SOMEHOW but must run outside of action listener
}
});
Obviously, a function continuously running within the actionPerformed method will effectively disable the rest of my GUI.
This is a valid observation and shows that you have understand the fundamental rule when working with Swing.
Your game is most likely event driven (correct me if I'm wrong) so the action performed by the button should just set the program in a new state, waiting for further events. This is nothing that should be time consuming, and is typically done directly by the EDT.
Of course, if you want to do a fancy start-new-game animation, that needs to be performed in a separate thread, in which case you simply start the animation thread (I would recommend using a SwingWorker though) from within the actionPerformed method, and then return.
In code, I imagine it would look something like this:
go.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Remove the menu components
someJPanel.removeAll();
// Show the game table
someJPanel.add(new GamePanel());
someJPanel.revalidate();
someJPanel.repaint();
// done. Wait for further user actions.
}
});
You game should probably start in its own thread and manage that itself (hard to say), but to get you going you could start your game in a new "external" thread, something like this in your actionPerformed:
public void actionPerformed(ActionEvent e) {
Thread thread = new Thread("Game thread") {
public void run() {
startGame(); //or however you start your game
}
};
thread.start();
}
I believe that you want to extend javax.swing.SwingWorker.
The non-ui start-up functionality would run in doInBackground and the done method would be called when it finishes to update the ui.
There's even an example in the javadoc Class Description to update a progressbar with the status of what's happening in start-up.