Reputation:
I'm trying to make an application which shows a JProgressBar
only while it is performing actions. My problem is, when the program is first opened, I set the JProgressBar
visibility to false
, then to true
when an action is being performed and after it is done, back to false
. It seems like it would work, and it does, just not when I make it not visible by default. If the visibility is true
by default then it works well, but that's not quite what I want. How could I make it so that it isn't visible until I set it to be visible?
SSCCE just incase my question wasn't clear enough:
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.JPanel;
import javax.swing.JProgressBar;
public class SmileBack {
private JFrame frame;
private JPanel panel, container;
private JButton loadButton;
private JProgressBar progressBar;
public static void main(String[] arguments) {
new SmileBack().constructFrame();
}
public void constructFrame() {
frame = new JFrame("RSTracker");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(getContentPane());
frame.pack();
frame.setVisible(true);
}
public JPanel getContentPane() {
panel = new JPanel(new BorderLayout());
progressBar = new JProgressBar();
progressBar.setIndeterminate(true);
//progressBar.setVisible(false); // doesn't work when this is uncommented
loadButton = new JButton("Load memberlist");
loadButton.setEnabled(true);
loadButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
new Thread(new Runnable() {
@Override
public void run() {
progressBar.setVisible(true);
// do my stuff here...
try {
Thread.sleep(2000); // just for the sake of example
} catch (InterruptedException e) {
e.printStackTrace();
}
progressBar.setVisible(false);
}
}).start();
}
});
container = new JPanel(new FlowLayout());
container.add(loadButton);
container.add(progressBar);
panel.add(container);
return panel;
}
}
Ignore the name, I was listening to that song while creating this. :)
Upvotes: 2
Views: 6494
Reputation: 109815
any events in your case (from Runnable#Thread
) doesn't invoke EventDispashThread
you have to wrapp that into invokeLater
, otherwise since JProgressBar
will be visible but after long running taks ended shouldn't be hidden
1) all changes to the GUI must be done on EDT
2) you can invoke EDV from Swing's Listeners, SwingWorker
's methods process
and done
and invoke changes to the GUI by using invokeLate
r (in special cases invokeAndWait
)
3) Runnable#Thread
by default doesn't invoke Swing's Methods nor EDT, there must be output to the GUI wrapped into invokeLater
(in special cases invokeAndWait
), more in the Concurency in Swing, inc. thread safe methods as are setText()
, append()
etc.
Upvotes: 2
Reputation: 168795
This is probably not the way it should be designed, but this code fixes the problem while still using the natural size (pack()
) needed to display the button and progress bar. This is achieved by setting the progress bar to invisible after pack is called, but before setting the GUI visible.
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.JPanel;
import javax.swing.JProgressBar;
public class SmileBack {
private JFrame frame;
private JPanel panel, container;
private JButton loadButton;
private JProgressBar progressBar;
public static void main(String[] arguments) {
new SmileBack().constructFrame();
}
public void constructFrame() {
frame = new JFrame("RSTracker");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(getContentPane());
// after this, everything is instantiated;
frame.pack();
setProgressBarVisibility(false);
frame.setVisible(true);
}
public void setProgressBarVisibility(boolean visible) {
progressBar.setVisible(visible);
}
public JPanel getContentPane() {
panel = new JPanel(new BorderLayout());
progressBar = new JProgressBar();
progressBar.setIndeterminate(true);
loadButton = new JButton("Load memberlist");
loadButton.setEnabled(true);
loadButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
new Thread(new Runnable() {
@Override
public void run() {
progressBar.setVisible(true);
// do my stuff here...
try {
Thread.sleep(2000); // just for the sake of example
} catch (InterruptedException e) {
e.printStackTrace();
}
progressBar.setVisible(false);
}
}).start();
}
});
container = new JPanel(new FlowLayout());
container.add(loadButton);
container.add(progressBar);
panel.add(container);
return panel;
}
}
Upvotes: 2
Reputation: 57381
In your thread call the progressBar.setVisible(true);
inside SwingUtilities.invokeAndWait()
.
Upvotes: 1
Reputation: 11767
The code you have posted works perfectly well. The problem is when you call frame.pack()
, the frame resizes to fit all visible component. When you have progressBar
visibility set to false, the frame ignores this component and sizes accordingly. So when progressBar.setVisible(true)
is called later, the component is shown but the frame is not big enough for you to see the component. If you just drag and increase the size of the frame, you can see the progressBar
I suggest that you provide explicit frame size like frame.setSize(200,400)
and dont call frame.pack()
.
Upvotes: 0