In the below code the JProgressBar displays correctly when the doSomething() is called from within main() but not when called as a result of an ActionEvent - the interface seems to freeze. What is the problem?
import java.awt.BorderLayout;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.util.Vector;
public class ThreadedDialog extends JFrame implements ActionListener{
private JDialog dlg;
private JButton button;
private void buildInterface(){
button = new JButton("do stuff;");
button.addActionListener(this);
this.getContentPane().setLayout(new BorderLayout());
this.getContentPane().add(BorderLayout.CENTER, button);
dlg = new JDialog(this, "Progress Dialog", true);
JProgressBar dpb = new JProgressBar(0, 500);
dlg.getContentPane().setLayout(new BorderLayout());
dlg.getContentPane().add(BorderLayout.CENTER, dpb);
dlg.getContentPane().add(BorderLayout.NORTH, new JLabel("Progress..."));
dlg.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
dlg.setSize(300, 75);
dlg.setLocationRelativeTo(this);
dpb.setIndeterminate(true);
}
public void doSomething(){
Thread t = new Thread(new Runnable(){
public void run() {
dlg.show();
}
});
t.start();
try {
for (int i=0; i<100; i++){
System.out.println("wtf is going on here?");
Thread.sleep(5000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
dlg.hide();
}
public static void main(String[] args) {
ThreadedDialog me = new ThreadedDialog();
me.buildInterface();
me.pack();
me.setVisible(true);
me.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//me.doSomething();
}
public void actionPerformed(ActionEvent event) {
doSomething();
}
}
Thanks
Everything you do with Swing components should be done on the event dispatch thread (EDT) (i.e. the thread used by Swing to call your events). You should launch threads to perform lengthy background operations.
In your code, you do the reverse : you try showing the dialog in another thread, and perform the long operation in the EDT.
Here's the fixed code :
package fr.free.jnizet.stackoverflow;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
public class ThreadedDialog extends JFrame implements ActionListener{
private JDialog dlg;
private JButton button;
private void buildInterface(){
button = new JButton("do stuff;");
button.addActionListener(this);
this.getContentPane().setLayout(new BorderLayout());
this.getContentPane().add(BorderLayout.CENTER, button);
dlg = new JDialog(this, "Progress Dialog", true);
JProgressBar dpb = new JProgressBar(0, 500);
dlg.getContentPane().setLayout(new BorderLayout());
dlg.getContentPane().add(BorderLayout.CENTER, dpb);
dlg.getContentPane().add(BorderLayout.NORTH, new JLabel("Progress..."));
dlg.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
dlg.setSize(300, 75);
dlg.setLocationRelativeTo(this);
dpb.setIndeterminate(true);
}
public void doSomething(){
// create a thread for the background task
Thread t = new Thread(new Runnable(){
public void run() {
try {
for (int i=0; i<100; i++){
System.out.println("wtf is going on here?");
Thread.sleep(5000);
}
}
catch (InterruptedException e) {
e.printStackTrace();
}
// when the background task is finished, hide the dialog in the EDT.
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
dlg.setVisible(false);
}
});
}
});
t.start();
// show the dialog in the EDT
dlg.setVisible(true);
}
public static void main(String[] args) {
// create the GUI in the EDT
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ThreadedDialog me = new ThreadedDialog();
me.buildInterface();
me.pack();
me.setVisible(true);
me.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
});
}
public void actionPerformed(ActionEvent event) {
doSomething();
}
}
You should read this tutorial, and learn to use SwingWorker for background tasks.
The problem here is that you are doing dlg.show() on the new thread that blocks that thread and you have Thread.sleep() on your main thread so pretty much everything is blocked. Besides JDialog.show() and hide() are deprecated and you might want to be using setVisible(true/false), but that was not causing any issue in your case. If you are trying to display a dialog with a progress bar for some time and then close it, here's the fixed code. There is a second thread now that takes care of hiding the dialog after some time leaving the main thread to do it's thing.
import java.awt.BorderLayout;
import javax.swing.*;
import java.awt.event.*;
public class ThreadedDialog extends JFrame implements ActionListener {
private JDialog dlg;
private JButton button;
private void buildInterface() {
button = new JButton("do stuff;");
button.addActionListener(this);
this.getContentPane().setLayout(new BorderLayout());
this.getContentPane().add(BorderLayout.CENTER, button);
dlg = new JDialog(this, "Progress Dialog", true);
JProgressBar dpb = new JProgressBar(0, 500);
dlg.getContentPane().setLayout(new BorderLayout());
dlg.getContentPane().add(BorderLayout.CENTER, dpb);
dlg.getContentPane().add(BorderLayout.NORTH, new JLabel("Progress..."));
dlg.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
dlg.setSize(300, 75);
dlg.setLocationRelativeTo(this);
dpb.setIndeterminate(true);
}
public void doSomething() {
Thread t1 = new Thread(new Runnable() {
public void run() {
dlg.setVisible(true);
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
try {
System.out.println("wtf is going on here?");
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
dlg.setVisible(false);
}
});
t1.start();
t2.start();
}
public static void main(String[] args) {
ThreadedDialog me = new ThreadedDialog();
me.buildInterface();
me.pack();
me.setVisible(true);
me.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// me.doSomething();
}
public void actionPerformed(ActionEvent event) {
doSomething();
}
}
Related
I have created a Java application that goes through hundreds of documents after user clicks "Run" button. Is there a way to terminate the program and leave the GUI running? All I want to be able to stop is the process of reading the documents.
System.exit(0) is not the solution I am looking for as my whole app closes.
It's difficult to say something without to see your application. But probably this piece of code will help you to understand how to implement what you want:
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.WindowConstants;
public class SwingWorkerTest implements Runnable {
private JButton cancelButton = new JButton("Cancel");
private JButton runButton = new JButton("Run");
private JLabel label = new JLabel("Press 'Run' to start");
private LongWorker longWorker;
#Override
public void run() {
JFrame frm = new JFrame("Long task test");
frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
cancelButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
longWorker.terminate();
}
});
runButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
longWorker = new LongWorker();
runButton.setEnabled(false);
cancelButton.setEnabled(true);
label.setText("Task in progress. Press 'Cancel' to terminate.");
longWorker.execute();
}
});
JPanel bottomPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING));
bottomPanel.add(runButton);
bottomPanel.add(cancelButton);
frm.add(label);
frm.add(bottomPanel, BorderLayout.SOUTH);
frm.setSize(400, 200);
frm.setLocationRelativeTo(null);
frm.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new SwingWorkerTest());
}
private class LongWorker extends SwingWorker<Void, Void> {
private volatile boolean terminated;
#Override
protected Void doInBackground() throws Exception {
// check special variable tov determine whether this task still active
for (int i = 0; i < 1000 && !terminated; i++) {
readFile();
}
return null;
}
#Override
protected void done() {
if (terminated) {
label.setText("Process terminated. Press 'Run' to restart.");
} else {
label.setText("Process done. Press 'Run' to restart.");
}
cancelButton.setEnabled(false);
runButton.setEnabled(true);
}
// dummy method - make 10 milliseconds sleep
private void readFile() {
try {
Thread.sleep(10);
} catch (InterruptedException ex) {
// Nothing here
}
}
public void terminate() {
terminated = true;
}
}
}
I am slightly confused, I have a jFrame of which I have made in Netbeans. This jFrame has a jLabel, of which is set to setVisible(false); from the beginning. Whenever a specific method is called, I then set the jLabel to setVisible(true); and then use a timer to set it to false again after 2 seconds. Apparently it won't work and I am unable to figure out why. I am aware of the repaint(); method, but can figure out how to make that work either.
I know the actual method for setting the visibility is called, as I have set it to print a line with the current state, which it does.
My actual code is the one below.
public JFram() {
initComponents();
setResizable(false);
jLabel2.setVisible(false);
}
static void tesMethod() {
try {
//function that does something
} finally {
new JFram().showHide(); //call function which is supposed to change the vissibility of jLabel
}
}
void showHide() {
jLabel2.setVisible(true);
System.out.println("reached show");
new java.util.Timer().schedule(
new java.util.TimerTask() {
#Override
public void run() {
jLabel2.setVisible(false);
System.out.println("reached timer");
}
},
2000
);
}
The code below here is how I tried to use the repaint(); method.
void showHide() {
jLabel2.setVisible(true);
jLabel2.repaint();
System.out.println("reached show");
new java.util.Timer().schedule(
new java.util.TimerTask() {
#Override
public void run() {
jLabel2.setVisible(false);
jLabel2.repaint();
System.out.println("reached timer");
}
},
2000
);
}
I think your problem lies mainly in you using a java.util.Timer instead of a javax.swing.Timer and probably you're blocking the Event Dispatch Thread (EDT).
You could try this code and compare it with yours, I also don't see where you're adding your JLabel to your frame.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class ShyLabel {
private JFrame frame;
private JLabel label;
private Timer timer;
private boolean isVisible;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new ShyLabel().createAndShowGui();
}
});
}
public void createAndShowGui() {
String labelText = "I'm a shy label that hides every 2 seconds";
isVisible = true;
frame = new JFrame(getClass().getSimpleName());
label = new JLabel(labelText);
timer = new Timer(2000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
label.setText(isVisible ? "" : labelText);
isVisible = !isVisible;
}
});
timer.setInitialDelay(2000);
timer.start();
frame.add(label);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
The below image is produced by the above code, however because of the time I recorded the GIF it looks really fast instead of taking 2 seconds as it should be...
May be it is a problem of layout.
As you set resizable to false before any layout calculation occurred, the label was ignored (as invisible) by the time of the first layout.
You could try revalidate().
The program hangs after trying to construct another instance of itself when I wait for the reference returned by the constructor to be set.
If I click on the button, the program will hang.
edit: removed silly second wait loop.
edit 2: change true to false when calling constructor. program seems to work now.
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.InvocationTargetException;
import javax.swing.*;
public class Problem extends JPanel {
public Problem(boolean wait) {
frame=new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
if(wait) try {
System.out.println("calling invoke and wait");
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
Problem.this.run();
}
});
} catch(InvocationTargetException|InterruptedException e) {
throw new RuntimeException(e);
}
else {
System.out.println("calling invoke later");
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Problem.this.run();
}
});
}
}
public String title() {
return "title";
}
public void addContent() {
JButton button=new JButton("click");
add(button,BorderLayout.CENTER);
button.addActionListener(new ActionListener() {
#Override public void actionPerformed(ActionEvent arg0) {
Runnable runnable=new Runnable() {
#Override public void run() {
System.out.println("before new "+Thread.currentThread());
problem=new Problem(false);
System.out.println("after new "+Thread.currentThread());
}
};
new Thread(runnable).start();
System.out.println("before first wait "+Thread.currentThread());
while (problem==null)
;
}
});
}
void run() {
frame.setTitle(title());
frame.getContentPane().add(this,BorderLayout.CENTER);
addContent();
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new Problem(false);
}
Problem problem;
public final JFrame frame;
private static final long serialVersionUID=1;
}
p1 = problem instance from main function
State 1: Creation - p1 is initialized. p1.problem is null
State 2: User clicks button. Eventually, in another thread, p1.problem is created. However, p1.problem.problem had never been initialized and won't be until the user clicks a button, which will never happen. I'm not sure what you're trying to do but it seems like your program is hanging on while (problem.problem==null);
I was reading the documentation on swing timers, when I came across some information about ActionListeners. When further researched, all I could find is how to create an ActionListener attached to a JButton, etc. How can you create a plain ActionListener, not attached to anything?
My timer is not working correctly, and I thought it may be because I was incorrectly using the ActionListener.
Here is my code:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
public class MyTimer {
ActionListener al = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
System.out.println("testing");
}
};
public MyTimer() {
Timer timer = new Timer(10, al);
timer.start();
}
public static void main(String[] args) {
MyTimer start = new MyTimer();
}
}
An ActionListener is just an interface
You can create an stand alone version by implementing it and then instanstanting it....
public class MyActionHandler implements ActionListener {
public void actionPerformed(ActionEvent evt) {
// do something...
}
}
And sometime in the future...
MyActionHandler handler = new MyActionHandler();
Or you could create an anonymous instance....
ActionListener al = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
// do something...
}
};
Take a look at Interfaces for more details
How can you create a plain actionlistner, not attached to anything?
Loot at this:
ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Hello World!");
}
};
// Using the listener with 2 seconds delay
java.swing.Timer timer = new java.swing.Timer(2000, listener);
timer.setRepeats(false);
// Start the timer
timer.start();
Try with this:
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
public class MyTimer {
ActionListener al = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
System.out.println("testing");
}
};
public MyTimer() {
Timer timer = new Timer(1000, al);
timer.start();
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
new MyTimer();
}
});
}
}
I have a function graphics() that creates my JFrame and two JRadioButtons and adds ActionListeners to them. This graphics is called from main() and graphics itself calls game().
public void game() throws Exception
{
jTextArea1.setLineWrap(true);
jTextArea1.setWrapStyleWord(true);
jTextArea1.setText("This is private information.");
jRadioButton1.setVisible(true);
jRadioButton2.setVisible(true);
try {
t.sleep(40000);
repaint();
} catch (InterruptedException e) {
// We've been interrupted: no more messages.
return;
}
After displaying "This is private information." in the text Area, I want the program execution to pause for 40 seconds, or until the user presses the JRadioButton, whichever is earlier. So I added an ActionListener and called t.interrupt() inside it.
private void jRadioButton1ActionPerformed(java.awt.event.ActionEvent evt) {
t.interrupt();
jRadioButton1.setVisible(false);
jRadioButton2.setVisible(false);
//System.out.println(t.interrupted());
jTextArea1.setText("Please wait...");
}
However, even after choosing the JRadioButton which should trigger the interrupt, that does not happen and t.interrupted returns false.
Any help would be appreciated.
Never, ever call Thread.sleep(...) on the Swing event thread as you will freeze the thread and effectively freeze your program. The solution is to consider use of a Swing Timer for the time-dependent portion of your requirement and using a SelectionListener for the JCheckBox or JRadioButton requirement.
For example:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.*;
public class PausingExecution extends JPanel {
private static final String SELECTED_TEXT = "Snafus are Better!!!";
private static final String UNSELECTED_TEXT = "Fubars Rule!!";
private static final String TIMES_UP = "Time's Up!!!!";
private static final int TIMER_DELAY = 10 * 1000;
private JTextField messageField = new JTextField(UNSELECTED_TEXT, 10);
private JCheckBox checkBox = new JCheckBox("Click Me");
public PausingExecution() {
add(messageField);
add(checkBox);
checkBox.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent iEvt) {
if (iEvt.getStateChange() == ItemEvent.SELECTED) {
messageField.setText(SELECTED_TEXT);
} else {
messageField.setText(UNSELECTED_TEXT);
}
}
});
Timer mySwingTimer = new Timer(TIMER_DELAY, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
messageField.setText(TIMES_UP);
checkBox.setEnabled(false);
}
});
mySwingTimer.setRepeats(false);
mySwingTimer.start();
}
private static void createAndShowGui() {
PausingExecution mainPanel = new PausingExecution();
JFrame frame = new JFrame("PausingExecution");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}