JProgressBar not visible until told to show - java

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. :)

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;
}
}

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 invokeLater (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.

In your thread call the progressBar.setVisible(true); inside SwingUtilities.invokeAndWait().

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().

Related

How to call a panel from a button with ActionListener

So I'm making a simple program that jumps from panel to panel and am using an actionlistener Button to make the jump. What kind of method or operation do I use to jump from panel to panel?
I tried to use setVisible(true); under the action listener, but I get just a blanks screen. Tried using setContentPane(differentPanel); but that doesn't work.
ackage Com.conebind.Characters;
import Com.conebind.Tech.TechA16;
import Com.conebind.Overviews.OverviewA16;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Char_A16 extends JFrame {
private JButton combosButton16;
private JButton techButton16;
private JButton overviewButton16;
private JLabel Image16;
private JPanel panel16;
private JPanel panelOverviewA16;
public Char_A16() {
overviewButton16.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
OverviewA16 overview16 = new OverviewA16();
overview16.setVisible(true);
overview16.pack();
overview16.setContentPane(new Char_A16().panelOverviewA16);
}
});
techButton16.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
//Todo
}
});
}
private void createUIComponents(){
Image16 = new JLabel(new ImageIcon("Android 16.png"));
}
public static void main (String[] args){
JFrame frame = new JFrame("Android 16");
frame.setContentPane(new Char_A16().panel16);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);}
}
The setContentPane(OverviewA16) doesn't work because there's not an object that defines the panel.
Please check this demo project showing how to use CardLayout with IntelliJ IDEA GUI Designer.
The main form has a method that switches between 2 forms displayed inside it:
public void showPanel(String id) {
final CardLayout cl = (CardLayout) cardPanel.getLayout();
cl.show(cardPanel, id);
}
Both forms are added to the card layout during the main form initialization:
FormOne one = new FormOne();
one.setParentForm(this);
cardPanel.add(one.getPanel(), FORM_ONE);
FormTwo two = new FormTwo();
two.setParentForm(this);
cardPanel.add(two.getPanel(), FORM_TWO);
final CardLayout cl = (CardLayout) cardPanel.getLayout();
cl.show(cardPanel, FORM_ONE);
A reference to the main parent form is passed to these 2 forms using setParentForm() method so that FormOne and FormTwo classes can access the showPanel() method of the MainForm.
In a more basic case you may have a button or some other control that switches the forms
located directly on the MainForm, then you may not need passing the main form reference to the subforms, but it can be still useful depending on your app logic.

Passing containers between JFrame and JDialog causing missing panels?

As for testing-reasons I tried to open a JDialog window with the panel and its contents of the main application frame. As I already had anything in there I wanted to see if I could simply set the JDialogs contentPane to the one I passed over. So in simplyfied form this came together:
testsforSO.java :
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Point;
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.JPanel;
public class testsforSO extends JFrame {
private static final long serialVersionUID = -3890178393751567629L;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
testsforSO frame = new testsforSO();
frame.setSize(300, 300);
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public testsforSO() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("I am the Frame");
getContentPane().setLayout(new BorderLayout(0, 0));
JPanel panel = new JPanel();
panel.setLayout(null);
JButton btnRestart = new JButton("Restart");
btnRestart.setBounds(10, 10, 50, 50);
btnRestart.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
testsforSO.main(null);
dispose();
}
});
panel.add(btnRestart);
getContentPane().add(panel);
repaint();
// -----------DELETE These comments-------------
// JDialog myDialg = new JDialog(this);
// myDialg.setContentPane(panel);
// myDialg.setVisible(true);
// myDialg.setSize(300,300);
// myDialg.setLocation(new Point(250, 250));
// myDialg.setTitle("I am Dialog from within the script");
myDialog.main(panel);
}
}
and myDialog.java :
import java.awt.Container;
import java.awt.EventQueue;
import javax.swing.JDialog;
public class myDialog extends JDialog {
private static final long serialVersionUID = 7079322237622743228L;
public static void main(Container myContainer) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
myDialog frame = new myDialog(myContainer);
frame.setVisible(true);
frame.setContentPane(myContainer);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public myDialog(Container myContainer) {
setContentPane(myContainer);
getContentPane().setLayout(null);
setBounds(200,200,200,200);
}
}
When starting the main frame I assumed it would contain the restarting button as well as the dialog does. But interestingly it was only the dialog with the button.
However when hitting it, the main frame properly restarted, a second dialog was set up and it contained the button again. This time however the main frame had the button as well, just without any function. Clicking on it does absolutely nothing.
Then I tried further and checked if that behaviour would change if I added the dialog directly into the main applications code (see the commented lines) and, starting the application once again only the dialog in its own class showed the button. Even hitting this one now restarted properly but the button won't show up on any other window except the lonely declared dialog.
What am I missing here and how could I refactor my code to work properly if even at all in this manner?
Understand that Swing components can only be present in one container, and while you may see the visual residue of a component in a container, the actual component is only present in the last container added to.
Myself, if I wanted dialog and jframe to have the same content pane components, I'd create a factory method to create the contentPane, and then use it to create two unique but identical contentPanes.
Also, I'd be remiss if I didn't mention something about your use of null layouts. While null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.
For instance, when I ran your code, this is the dialog that I saw:
You hard coded the button's size to be too small to show its text on my platform. If you had used layout managers, and called pack() on the top-level window, the button would show appropriately.

How I enable parent JFrame after close child?

I have a frame, on this frame I have a Menu with About MenuItem. When we select it the program opens a new JPanel with texts and with OK button and the enabled status of parent panel is set to false.
And now comes a problem. When we click on OK, then I want to close this About panel, and I want to turn to parent panel, and I want to enable it!
Please tell me, how?
Consider using a WindowListener that reacts to the closing event of the about-dialog. You can add this in your frame or in the constructor of your dialog, just set the variables accordingly.
myDialog.addWindowListener(new WindowAdapter() {
#Override
public void windowClosed(WindowEvent e) {
parentFrame.setEnabled(true);
}
});
If you really only have a switching JPanel, use a ButtonListener.
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
//Execute when button is pressed
frame.setEnabled(true);
}
});
As mentioned in the comments, using a modal JDialog would be a more elegant way of solving the problem of disabling a parent frame while a dialog is active. Here is a tutorial.
Why don't you use simply a JOptionPane (particularly the showMessageDialog method)? You can specify there an Object (for example a JPanel) which will be presented in a modal dialog. Take a look at this sample code I've written for you (I've used a JButton, but it will be the same for JMenuItem):
import java.awt.BorderLayout;
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.JOptionPane;
import javax.swing.JPanel;
public class AboutDialogDemo extends JFrame {
private final JButton btnAbout = new JButton("About...");
public AboutDialogDemo() {
final JFrame thisFrame = this;
btnAbout.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(thisFrame, new AboutPanel());
}
});
getContentPane().setLayout(new BorderLayout());
getContentPane().add(btnAbout, BorderLayout.PAGE_END);
pack();
}
public static void main(String[] args) {
AboutDialogDemo frame = new AboutDialogDemo();
frame.setSize(400, 400);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
class AboutPanel extends JPanel {
private final JLabel lblAbout = new JLabel("Sample about text");
public AboutPanel() {
setLayout(new BorderLayout());
add(lblAbout, BorderLayout.PAGE_START);
}
}
I hope you'll find it useful

I want to display a number in a textbox when a button is pressed

I want to use the text property in the button property field. this is the code i have tried but it does not work.
private void btnOneActionPerformed(java.awt.event.ActionEvent evt) {
String btnOneText = btnOne.getText( );
txtDisplay.setText(btnOneText);
}
I suggest you to read the oracle official tutorials that have good examples. How to use Buttons.
I made you an example of what you have to do.
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.JTextField;
public class TextFieldTest {
private JPanel panel;
public TextFieldTest(){
panel = new JPanel();
final JTextField textfield = new JTextField(10);
final JButton button = new JButton("Press me");
//here i add the action listener, that will listen the input event
button.addActionListener(new ActionListener(){
//this is anonymous class
#Override
public void actionPerformed(ActionEvent evt){
String text = button.getText();
textfield.setText(text);
}
});
panel.add(textfield);
panel.add(button);
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("Textfield example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLocationByPlatform(Boolean.TRUE);
frame.add(new TextFieldTest().panel);
//Display the window.
frame.pack();
frame.setVisible(Boolean.TRUE);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
See it's more simpler than using a gui-editor, then you understand what you do. It's better to do this first and later when you understand what you are doing use the netbeans gui-editor.

JFrame and visibility: issue with fading out and getting a screenshot

In an action (i.e. a method) executed by pressing a button deployed into a JFrame, I want to hide the java app and then to get a screenshot. Finally, once the screenshot is taken, I need to make the JFrame visible.
The method is the following:
public void myButtonPressedAction(){
//Hiding the JFrame
this.setVisible(false);
//Now I use Robot to get a screenshot using another method
//not reported for simplicity
myMethodToGetScreenshot();
//Making the JFrame visible
this.setVisible(true);
}
What happens is that, once visibility is set to false, the app starts to become invisible and immediately I get the screenshot: unfortunately, the screenshot also capture the JFrame while fading out (i.e., it is going to become invisible, the isVisible method returns true, but the JFrame is not completely invisible).
One possible solution is to insert a timer to put a delay between the call to setVisible(false) and the one to myMethodToGetScreenshot(). However, suppose that the system is busy, the delay could be undervalued; on the contrary, a larger delay will make my application slow!
How can I get the exact time instant such that the JFrame has been completely fade out, i.e. it is really invisible?
EDIT
This are initialized in the constructor:
String myPath= ...;//here I have a String that represent a path to a folder.
JPEGImageWriteParam JPEG_PARAMS_BEST_QUALITY = new JPEGImageWriteParam(null);
JPEG_PARAMS_BEST_QUALITY.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
JPEG_PARAMS_BEST_QUALITY.setCompressionQuality(1f);
This is the code for myMethodToGetScreenshot():
public void myMethodToGetScreenshot(){
BufferedImage capture = new Robot().createScreenCapture(screenArea);
ImageWriter writer = writerService.getWriter();
writer.setOutput(new FileImageOutputStream(new File(myPath+"screenshot.jpg")));
writer.write(null, new IIOImage(capture, null, null), JPEG_PARAMS_BEST_QUALITY);
}
This is the screenshot I get. You can see the JFrame fading out...
Then put some delay time. You can use Swing timer.
Here is a small demo:
import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class ScreenshotDemo {
JFrame frame = new JFrame();
JButton button = new JButton("Catch the screenshot");
Timer timer;
Robot robot;
JLabel label = new JLabel();
public ScreenshotDemo() {
try {
robot = new Robot();
} catch (AWTException e1) {
e1.printStackTrace();
}
// Keeps frame disposed for 3 seconds
timer = new Timer(3000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Rectangle size = new Rectangle(Toolkit.getDefaultToolkit()
.getScreenSize());
Image image = robot.createScreenCapture(size);
label.setIcon(new ImageIcon(image));
frame.setVisible(true);
}
});
timer.setRepeats(false);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
frame.setVisible(false);
timer.start();
}
});
frame.add(button, BorderLayout.NORTH);
frame.add(label, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// frame.pack();
frame.setSize(1024, 768);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new ScreenshotDemo();
}
});
}
}
Basically, you will hide your frame for some time (3 seconds in this demo). While frame is hidden you will take a snapshot.
Instead of setvisible you can use setExtendedState
//Minimize the JFrame
this.setExtendedState(JFrame.ICONIFIED);
//Now I use Robot to get a screenshot using another method
//not reported for simplicity
myMethodToGetScreenshot();
//Restore the JFrame
this.setExtendedState(JFrame.NORMAL);
You have to add a reasonable delay after hiding the frame and before taking the screenshot. Easiest way is to insert a call to robot.delay() before robot.createScreenCapture().
I'd give the ComponentListener a try (assuming this code goes to a member of a JFrame-extending class):
final Container contentPane = getContentPane();
ComponentAdapter componentAdapter = new ComponentAdapter() {
#Override
public void componentHidden(ComponentEvent arg0) {
myMethodToGetScreenshot();
contentPane.removeComponentListener(this);
}
};
contentPane.addComponentListener(componentAdapter);

Categories

Resources