This question already has answers here:
Prevent Swing GUI locking up during a background task
(5 answers)
Closed 6 years ago.
I have written code using swing libs, that when added an actionlistener, won't update a progressBar.
Without a button and action listener, it works great. How to force a progressBar update as simply and cleanly as possible? Appended code is an easy to understand example that sums up my problem. If you comment out an ActionPerformed method and execute the program from main, it works just fine.
Do not just paste code whithout explaining.
ps.: I have seen this: swing progressBar threading
public class Okno {
private JProgressBar progressBar = new JProgressBar(0,306);
JFrame f = new JFrame("JProgressBar Sample");
JButton b = new JButton("start");
ActionListener a = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
barupdate();
}
};
private void barupdate(){
for(int p = 1; p<308;p=p+2){
System.out.println(p);
progressBar.setValue(p);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private Okno(){
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
progressBar.setStringPainted(true);
f.add(progressBar, BorderLayout.SOUTH);
f.add(b, BorderLayout.NORTH);
b.addActionListener(a);
f.setSize(300, 300);
f.setVisible(true);
}
public static void main(String[] args) {
Okno okno = new Okno();
}
}
The problem is you have a loop where you are adjusting the progress bar setting that is being called from an action listener. The problem is, the bar won't update until after the listener is finished. And so you will get no updates. Not only that but you will bog down the gui because the window can't react to mouseclicks etc while you are in that action listener.
So the best way to handle this is instead to create a swing timer, in the action listener, and put the code for updating the button there, and start the timer in the action listener.
The timer should only update the bar once. and you should allow the fact that the swing timer will be called multiple times, to play the part of the repetitiveness. So you don't want to have any loops in your code.
Thread.sleep(50);
Don't use Thread.sleep(...). This will prevent the GUI from repainting itself until the loop has finished executing.
Instead you can use a SwingWorker.
Read the section from the Swing tutorial on Concurrency in Swing which has more information and contains a working example with a SwingWorker.
Also, look at the tutorial table of contents. There is a section on How to Use ProgressBars that also contains a working example. The tutorial is the first place to look for examples.
Related
For example, I want a JTextfield to display different random numbers continuously with start, stop and resume buttons. What is the possible solution to automatically update the JTextField continuously when the start button is pressed?
I tried using while loop inside the start button's action listener but it just makes the button stuck in the while loop.
This is the part of the code that I tried.
startButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
while(true){
textField.setText(String.valueOf(random.nextInt()));
}
}
});
Read Concurrency in Swing.
You can use a javax.swing.Timer to change the text of the JTextField.
A tiny example:
public class TimerExample {
public static void main(String[] args) {
SwingUtilities.invokeLater(()->{
JTextField field = new JTextField(10);
Timer timer = new Timer(100, e->{
field.setText(String.valueOf(Math.random()));
});
timer.start();
JOptionPane.showMessageDialog(null,field);
});
}
}
If you use while(true) in the Thread that runs the UI (this thread is called EDT - event dispatch thread), the thread won't be able to handle events since it is stucked inside the while loop.
I develop a complex music application in java (8) based on the Netbeans RCP 8.2 and I have a strange problem that occurs randomly.
I have a JFrame with a panel which contains many JComponents. I use the panel's InputMap/ActionMap to capture "a","b",...,"g" key presses and call an action.
The action retrieves the key char then shows a JDialog which contains a JTextField used to edit some text data.
Before showing the dialog with dialog.setVisible(true), the action calls dialog.prepare(char key) so that the JDialog can initialize itself before being shown. Actually dialog.prepare(char key) only appends the passed char (converted to uppercase) in the JTextField.
This works most of the time: I press for example "c" in the JFrame then the JDialog appears with "C" at the end of the JTextField.
But sometimes, maybe 1/20 times, I get "Cc" at the end of the JTextfield !
It's like the original key press event (which comes from a JComponent in the JFrame's panel and was processed using InputMap/ActionMap) was also redundantly processed by the JDialog.
I verified that it's not a keyboard hardware problem. I reproduced the issue on a second computer with Win8 (mine is Win10).
I tried without success 1/ using a KeyListener instead of InputMap/ActionMap
and 2/ use java.awt.EventQueue.invokeLater() to append the key char in the JTextField.
I created a small independant app (see below) to reproduce the issue and facilitate debugging...but this small app works fine, I could not reproduce the issue :-( Then I compared again with my real app code, and it's really the same code, except the real app is a complete Netbeans RCP application.
So could it be that Netbeans RCP impacts the way Swing handle key events ? It looks odd to me...
I'm lost, any hint/suggested test would be greatly appreciated !
/**
* Try to reproduce double key problem... Failed because this works OK !! :-(
*/
public class PbKeyDouble extends JFrame {
MyDialog dialog;
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
PbKeyDouble o = new PbKeyDouble();
o.setVisible(true);
}
});
}
public PbKeyDouble() {
// GUI INITIALIZATION
// Add a basic panel
JPanel panel = new JPanel();
getContentPane().add(panel);
panel.setPreferredSize(new Dimension(300, 200));
JButton button = new JButton("BUTTON");
panel.add(button);
// Button not used, it's only to simulate the real app where a component in the panel has the focus
button.requestFocusInWindow();
// If "A" or "B" key is pressed anywhere, MyAction.actionPerformed() will be called
panel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke("A"), "MyAction");
panel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke("B"), "MyAction");
panel.getActionMap().put("MyAction", new MyAction());
// Prepare JFrame
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
}
private class MyAction extends AbstractAction {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("EDT? " + SwingUtilities.isEventDispatchThread()); // Always prints TRUE
if (dialog == null) {
dialog = new MyDialog();
}
// Retrieve the key used to trigger the action
char c = e.getActionCommand().charAt(0);
// Prepare the dialog (insert the char)
dialog.prepare(c);
// Show dialog
dialog.setVisible(true);
}
}
private class MyDialog extends JDialog {
JTextField textfield;
/**
* A simple dialog with just a textfield.
*/
public MyDialog() {
textfield = new JTextField("Hello");
textfield.setColumns(100);
getContentPane().add(textfield);
pack();
setLocationRelativeTo(null);
}
/**
* Append the key (uppercased) at the end of the textfield
*/
public void prepare(char c) {
String text = textfield.getText();
textfield.setText(text + " " + Character.toUpperCase(c));
}
/**
* Overridden to add a global key binding on ESC key to exit the dialog.
* <p>
* This is only to facilitate the test where I need to try many times the process pressing "a" ESC "a" ESC etc.
*
* #return
*/
#Override
protected JRootPane createRootPane() {
JRootPane contentPane = new JRootPane();
contentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke("ESCAPE"), "actionCancel");
contentPane.getActionMap().put("actionCancel", new AbstractAction("Cancel") {
#Override
public void actionPerformed(ActionEvent e) {
setVisible(false);
}
});
return contentPane;
}
}
}
I found the problem, though it is still not logical to me. Explanation welcome !
All Swing components should be created and modified on the Event Dispatch Thread (EDT).
Yes, it was the case in my code and still it was not working...
To try to understand what happens I attached a KeyListener to the JTextField of the JDialog.
I found out that when it was working (key not doubled) my KeyListener received only a keyReleased() event. When it was not working (key doubled "Cc") my KeyListener received a keyTyped() event then keyReleased().
So I understand that AWT/Swing event handler mechanism "sends" each KeyEvent to the currently focused component (instead of to the component which the KeyEvent originates from). As I show the Dialog somewhere in the middle of the keyPressed/keyTyped/keyReleased sequence, sometimes the keyTyped was "wrongly" directed to the JTextField.
To solve this I executed the whole actionPerformed() code using SwingUtilities.invokeLater(), to make sure Dialog is shown after all EDT pending events are processed, and it seems to work so far...
I could find some good information in Java keybinding but what I don't understand is that it is recommended to use InputMap/ActionMap to avoid all the KeyListeners problems with focus changes etc. I used only InputMap/ActionMap and still it did not help...
So why InputMap does not react only to keyTyped() event ?
But sometimes, maybe 1/20 times, I get "Cc" at the end of the JTextfield !
Random issues are usually the result of threading issues.
All Swing components should be created and modified on the Event Dispatch Thread (EDT).
The code from your main() method is not executed on the EDT which could be the problem.
The code to create the GUI should be wrapped in a SwingUtilities.invokeLater(...).
Check out the Swing tutorial on Concurrency for more information.
I am working on a project that involves GUI. On the JFrame I have several JButtons that perform specific actions when clicked. Now the problem is, with the code I wrote, every time the buttons are clicked, they generate a new thread. I do this because I learned that JFrame would freeze if everything happens on the same thread, but I feel that creating that many threads isn't very efficient. Although the program functions pretty well for now, I still hope to cut off redundant pieces from the program. Should put all the GUI things in an "InvokeLater"? But aren't jButtons also in the GUI? Some actions the these Button perform required an existing GUI, so it's hard to separate them... Any ideas? Here is an example of the button.
JButton buttonA4= new JButton("Deact Crew");
buttonA4.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Thread hilo = new Thread(new Runnable()
{
public void run() {
DinnerList.deactivateGroup(tempJobCrew.getCrews());
studentTable.repaint();
}
});
hilo.start();
}
});
I have a JButton, lets call it "button" and added an ActionListener to it:
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
call();
}
});
It is correctly added to my frame etc. In that JFrame I also have a JLabel, and I want to change its text while the JButton method is working(because it takes ~30 secs to finish). How do I do that? Do I have to use some multi-thread-thingy?
Here is the basic principle(the JLabel is called output):
public void call(){
output.setText("test1");
try { Thread.sleep(1000);
} catch (InterruptedException e) {}
output.setText("test2");
}
This will result in the "output" Label being changed to "test2" after one second. How can I make it instantly display "test1"?
Don't use Thread.sleep(). This will prevent the GUI from repainting itself.
Do I have to use some multi-thread-thingy?
Yes.
For a long running task you need to start a separate Thread, so the GUI can remain responsive.
In this case you can use a SwingWorker.
Read the section from the Swing tutorial on Concurrency for more information and an example of using a SwingWorker.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Java GUI JProgressBar not painting
I have a GUI that has the GUI Locked while processing an Action Event, so I need a progress bar to show up. I can get the JDialog to show up but the progress bar won't show up. I used SwingUtilities.invokeLater() and invokeAndWait() but to no avail. The progress bar will not show up. Any hints or help would be appreciated.
pb = new JProgressBar(0, 100);
pb.setPreferredSize(new Dimension(175, 40));
pb.setString("Working");
pb.setStringPainted(true);
JLabel label = new JLabel("Progress: ");
JPanel center_panel = new JPanel();
center_panel.add(label);
center_panel.add(pb);
dialog = new JDialog((JFrame) null, "Working ...");
dialog.getContentPane().add(center_panel, BorderLayout.CENTER);
dialog.pack();
dialog.setLocationRelativeTo(this); // center on screen
dialog.toFront(); // raise above other java windows
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
dialog.setVisible(true);
pb.setIndeterminate(true);
}
});
Thread.sleep(5000);
template = AcronymWizardController
.sharedInstance().readAndDislpayDocx(contdFile);
parseDocxText(contdFile);
pb.setIndeterminate(false);
savedFile.setText(contdFile.toString());
dialog.dispose();
Swing is a single threaded API, that is, all the UI updates and modifications are performed from within a single thread (known as the Event Dispatching Thread or EDT). Anything that blocks this thread will stop it from processing additional updates, like repaints.
You have a number of choices. Your immediate requirement is to move the long running task off the EDT. To do this you can either use a SwingWorker or a Thread.
From your description, a SwingWorker will be easier.
For a simple example, check out JProgressBar won't update
For more information, you should check out Concurrency in Swing
You other choice would be to use something like a ProgressMonitor, example here