Sleep causing frame with invisible content [duplicate] - java

This question already has answers here:
java thread.sleep puts swing ui to sleep too
(3 answers)
Closed 5 years ago.
I have a frame with menubar menu, when I'm selecting a menu item I want the program to show a message and then after few seconds(by sleep function) automatically close this message.
But instead of this I'm getting empty message (jdialog with invisible content):
If remove closing message, it content will appear after sleeping time.
What I need to change to get proper result?
I want to get this:
Full working code:
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.util.concurrent.TimeUnit;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.SwingUtilities;
public class MessageSleepTest extends JDialog {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
public void run(){
new Frame("frame with menubar");
}
});
}
}
class Message extends JDialog {
public Message() {
this.setLayout(new GridLayout(0, 1));
this.add(new JLabel("Displaying this message for 3 seconds and then closing it...", JLabel.CENTER));
this.setAlwaysOnTop(true);
this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
}
class Frame extends JFrame{
public Frame(String title){
super(title);
setJMenuBar(new MenuBar());
setPreferredSize(new Dimension(500, 300));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null); // center the frame
setVisible(true);
}
}
class MenuBar extends JMenuBar implements ActionListener{
public static JMenuItem itmOpen;
public MenuBar() {
JMenu menuFile = new JMenu("File");
itmOpen = new JMenuItem("Open...");
itmOpen.addActionListener(this);
add(menuFile);
menuFile.add(itmOpen);
}
#Override
public void actionPerformed(ActionEvent e) {
JMenuItem source = (JMenuItem)e.getSource();
if(source == itmOpen){
JDialog message = new Message();
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
WindowEvent windowClosing = new WindowEvent(message, WindowEvent.WINDOW_CLOSING);
message.dispatchEvent(windowClosing);
}
}
}

You have a threading problem:
The sleep is performed on AWT's Event Dispatch Thread, which does all the event handling stuff in AWT and Swing. Therefore, when you click on the menu item, it creates the objects for the Message, but gets stuck with the setVisible() method, which in general sends an event to the JDialog to lay out and show itself. This message gets on the queue of the EDT, and after your event handler (the actionPerformed method) finishes, it gets processed. However the sleep is between the two.
So try something like this:
#Override
public void actionPerformed(ActionEvent e) {
JMenuItem source = (JMenuItem) e.getSource();
if (source == itmOpen) {
final JDialog message = new Message();
new Thread( new Runnable() {
#Override
public void run() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException ex) {
// Do nothing with it
}
WindowEvent windowClosing = new WindowEvent(message, WindowEvent.WINDOW_CLOSING);
message.dispatchEvent(windowClosing);
}
}).start();
}
}

Another solution with using swing.Timer
#Override
public void actionPerformed(ActionEvent e) {
JMenuItem source = (JMenuItem)e.getSource();
if(source == itmOpen){
JDialog message = new Message();
Timer timer = new Timer(1000 * 3, new ActionListener() {
public void actionPerformed(ActionEvent e) {
WindowEvent windowClosing = new WindowEvent(message, WindowEvent.WINDOW_CLOSING);
message.dispatchEvent(windowClosing);
}
});
timer.setRepeats(false);
timer.start();
}
}

Related

Get the in-focus tab after closing a tab

In a JTabbedPane, I associated a custom-made Data object to each added tab. I also have a corresponding Metadata object that shows up in another panel when the tab is selected. The problem I have now is when a tab is closed, the metadata panel shows the metadata of the Data object in the tab that just gets closed. Ideally, I want the panel to show the metadata for the in-focus tab that the user sees. However, the act of closing a tab means the “selected tab” is the tab being closed, so tabpane.getSelectedIndex() would not work. How can I get the tab that is in focus after closing a tab? Thank you in advance!
Devil is in the detail, which you provided none.
I did a quick test and discovered that, ChangeListener is called before ContainerListener, which is a real pain, but, it was always reporting the correct index.
So, what you need to do is marry the two together, so that, both will update the meta data pane when they are called.
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.addTab("One", new TabPane(tabbedPane));
tabbedPane.addTab("Two", new TabPane(tabbedPane));
tabbedPane.addTab("Three", new TabPane(tabbedPane));
tabbedPane.addTab("Four", new TabPane(tabbedPane));
tabbedPane.addContainerListener(new ContainerListener() {
#Override
public void componentAdded(ContainerEvent e) {
}
#Override
public void componentRemoved(ContainerEvent e) {
System.out.println("Removed " + e.getChild());
}
});
tabbedPane.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
System.out.println(tabbedPane.getSelectedIndex());
}
});
JFrame frame = new JFrame();
frame.add(tabbedPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TabPane extends JPanel {
private JTabbedPane parent;
public TabPane(JTabbedPane parent) {
this.parent = parent;
setLayout(new GridBagLayout());
JButton btn = new JButton("Close");
add(btn);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
parent.remove(TabPane.this);
}
});
}
}
}

Java swing Loader image not displaying properly? [duplicate]

This question already has answers here:
Java swing GUI freezes
(5 answers)
Closed 5 years ago.
I have tried the below code .
Functionality-
Click on Button
It will call a method which will take some time to process.
Need to display Loader image so that user can see that processing is going on.
I tried below but if the loaderLabel1.setVisible(true); before method call doesnot show image and if we comment loaderLabel1.setVisible(false); then it shows loader image after method is finished.
Will actionPerformed method not apply the visibility to Label unless it completes? If yes is there any alternative for this problem?
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class TestLoader extends JPanel{
static ImageIcon loading1 = new ImageIcon("D:\\WORKSPACE\\STUDY\\images\\ajax-loader.gif");
static JLabel loaderLabel1 = new JLabel(loading1, JLabel.CENTER);
public TestLoader(){
super(new GridLayout(0, 1));
System.out.println("---------TEST ------");
JPanel submitPanel = new JPanel();
submitPanel.add(clearMessageButton);
this.add(submitPanel);
JPanel LOADER_PANEL = new JPanel();
loaderLabel1.setVisible(false);
LOADER_PANEL.add(loaderLabel1);
this.add(LOADER_PANEL);
}
JButton clearMessageButton = new JButton(new AbstractAction("Test Result") {
#Override
public void actionPerformed(ActionEvent arg0) {
loaderLabel1.setVisible(true);
TestMethod();
loaderLabel1.setVisible(false);
}});
public void TestMethod(){
System.out.println(" =========START TestMethod =========");
try {
Thread.currentThread().sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(" =========complete TestMethod =========");
}
/**
* #param args
*/
public static void main(final String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
TestLoader pf = new TestLoader();
pf.display();
}
});
}
private void display() {
JFrame frame = new JFrame("TEST LOADER");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(this);
frame.pack();
frame.setResizable(true);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
There is the class SwingWorker that allows you to perform Tasks in a different thread; here's an example of how your code could be changed to use a SwingWorker:
JButton clearMessageButton = new JButton(new AbstractAction("Test Result") {
#Override
public void actionPerformed(ActionEvent arg0) {
SwingWorker worker = new SwingWorker() {
#Override
protected Object doInBackground() throws Exception {
loaderLabel1.setVisible(true);
TestMethod();
return true;
}
public void TestMethod() {
System.out.println(" =========START TestMethod =========");
try {
Thread.currentThread().sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(" =========complete TestMethod =========");
}
protected void done() {
loaderLabel1.setVisible(false);
};
};
worker.execute();
}
});
Note the methods doInBackground() that does the work in the other thread and done() that is called after doInBackground() is finished.

Java Event Dispatch Thread blocked

I'm trying to change text of JLabel during program execution. I know that program execution blocks EDT so I use timer to do this work. But timer starts only after the cycle finishes it's execution although timer.start() is located before cycle in source code.
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.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
#SuppressWarnings("serial")
public class TestUpdate extends JFrame {
JLabel lab;
JButton btn;
public TestUpdate() {
super("Update test");
setDefaultCloseOperation(EXIT_ON_CLOSE);
btn = new JButton();
btn.setText("Start test");
btn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Timer tm = new Timer(1, new ActionListener() {
public void actionPerformed(ActionEvent e) {
lab.setText("Text is successfully changed");
lab.repaint();
}
});
tm.setRepeats(false);
tm.setInitialDelay(0);
tm.start();
long startTime=System.currentTimeMillis();
while (true) {
if (System.currentTimeMillis()-startTime >= 3000) break;
try {Thread.sleep(500);} catch (InterruptedException e1) {}
}
lab.setText("Three seconds elapsed");
btn.setEnabled(false);
}
});
add(btn,"South");
lab = new JLabel("Should be changed before 3 seconds elapsed");
lab.setHorizontalAlignment(SwingConstants.CENTER);
add(lab);
setSize(300, 200);
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(
new Runnable() {
public void run() {new TestUpdate(); } });
}
}
Timer event is executed by the EDT, so first event will be executed when current task (your action listener code with while loop) will complete. That is why you observe such behaviour.

unable to close JOptionPane properly in chain order

I am displaying a JOptionPane suppose A on a button click from JFrame, and again displaying another JOptionPane suppose B on a button click from JOptionPane A, and I have a button on JOptionPane B suppoce button1, on the click event of button1, I am using code JOptionPane.getRootFrame().dispose() for closing the JOptionPane B, but it closes both A and B, please help me how can close only B but not A.
here is my sample code
i want second JOptionPane must be open
import java.awt.Dimension;
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.JOptionPane;
import javax.swing.JPanel;
public class SampleCode extends JFrame {
public SampleCode() {
setSize(new Dimension(500, 500));
setLocation(450, 150);
but1 = new JButton("Click me");
add(but1);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
but1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
but1Function();
}
});
}
public static void main(String args[]) {
new SampleCode();
}
void but1Function() {
JPanel panel1 = new JPanel();
JButton but2 = new JButton("Open new dialog");
panel1.add(but2);
but2.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JPanel pan2 = new JPanel();
JButton but3 = new JButton("click me to close");
pan2.add(but3);
but3.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JOptionPane.getRootFrame().dispose();
}
});
JOptionPane.showMessageDialog(null, pan2);
}
});
JOptionPane jp = new JOptionPane(panel1, JOptionPane.CLOSED_OPTION,
JOptionPane.DEFAULT_OPTION, null, new Object[] {}, null);
JDialog dialog = jp.createDialog(null, "This one must be remain open");
dialog.setLocation(500, 200);
dialog.setSize(new Dimension(345, 200));
dialog.setVisible(true);
}
JButton but1;
}
You don't want to get the root frame nor dispose of it. You want to get the window that is displaying the JOptionPane, a Window that should be a modal JDialog. So instead, use SwingUtilities.getWindowAncestor(someComponentInJOptionPane), and call dispose() on that Window if you want to programmatically dispose of your JOPtionPane.
import java.awt.Component;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class OptionPaneFun {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#SuppressWarnings("serial")
public void run() {
final JPanel panel1 = new JPanel();
panel1.add(new JButton(new AbstractAction("Show new option pane") {
{
putValue(MNEMONIC_KEY, KeyEvent.VK_S);
}
#Override
public void actionPerformed(ActionEvent e1) {
final JPanel panel2= new JPanel();
panel2.add(new JButton(new AbstractAction("Dispose of this option pane") {
{
putValue(MNEMONIC_KEY, KeyEvent.VK_D);
}
#Override
public void actionPerformed(ActionEvent e2) {
Component comp = (Component) e2.getSource();
Window win = SwingUtilities.getWindowAncestor(comp);
if (win != null) {
win.dispose();
}
}
}));
JOptionPane.showMessageDialog(panel1, panel2);
}
}));
JOptionPane.showMessageDialog(null, panel1);
}
});
}
}
The static method "getRootFrame()" returns your root frame which is the only one and it's the same for both your components (A and B). What you need to do - you have to put two frames in your root frame (call them frameA and frameB) and put paneA to frameA and paneB to frameB. Instead of calling this static method just invoke frameB.dispose() on reference frameB which you already have.
Try to add
panel.validiate();
After the dispose command. I had the same problem once and it helped a lot when I used this trick.
Basically when you add this command, it is telling the frame to validate or actually do it.
Read the oracle docs for more info.

Update a JPopupMenu menu items while it is open

In my UI i have a JPopMenu with values as ,
for e.g A,B,C
The scenario is,
I opened the JPopupMenu and kept it open.
At back end with a timer running , it updates the content B to some other alphabet at frequent interval.
3.I want the JPopupMenu to get updated while it is kept open.
In current behavior if i close and open JPopupMenu the updated value shows up.
I tried repaint()but it doesn't do anything.
What is the best way to do this?? Am new to swings please help.
Menu items can change their content at run time just fine. Without seeing your code it's hard to tell what you're doing wrong, but here's a working example:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
public class PopupTest {
private static final String[] messages = {
"You are today's 1000th user!",
"You have won an internet!",
"Claim your prize!"
};
private PopupTest() {
JFrame frame = new JFrame("You have won");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel lbl = new JLabel("Check your prize!");
frame.setLocationByPlatform(true);
frame.add(lbl);
frame.pack();
final JPopupMenu menu = new JPopupMenu();
final JMenuItem item = new JMenuItem(messages[0]);
menu.add(item);
menu.add(new JMenuItem("Another item that does not work"));
final Timer timer = new Timer(1000, new ActionListener() {
int count;
#Override
public void actionPerformed(ActionEvent e) {
count++;
count %= messages.length;
item.setText(messages[count]);
}
});
menu.addPopupMenuListener(new PopupMenuListener() {
#Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
}
#Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
timer.stop();
}
#Override
public void popupMenuCanceled(PopupMenuEvent e) {
timer.stop();
}
});
lbl.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
maybeShowPopup(e);
}
#Override
public void mouseReleased(MouseEvent e) {
maybeShowPopup(e);
}
private void maybeShowPopup(MouseEvent e) {
if (e.isPopupTrigger()) {
menu.show(e.getComponent(), e.getX(), e.getY());
timer.start();
}
}
});
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new PopupTest();
}
});
}
}
Try to use .revalidate() with .repaint() it might help.
The docs suggest that the revalidate method is called every time something like size changes and manually calling it with repaint seems to solve problems like these.

Categories

Resources