I would like to display the button "OK" of this JOptionPane only after a certain amount of time (let's say for example 5 sec). (My aim is actually to let finish some thread work behind this other thread)
JOptionPane jop2 = new JOptionPane();
jop2.showMessageDialog(null, "Please wait 5s", "WAIT", JOptionPane.INFORMATION_MESSAGE);
I don't know at all how to do that, could you provide me some code working which will answer to this problem?
Thank you very much in advance!
There is no specific way to do this using JOptionPane. You will have to create a custom dialog and reveal the OK button after a fixed time. You could use one pass of a Swing timer.
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
button.setVisible(true);
}
};
Timer timer = new Timer(0, taskPerformer);
timer.setInitialDelay(5000);
timer.setRepeats(false);
timer.start();
It sounds like what you're looking for is a combination of the SwingWorker and the ProgressMonitor. The SwingWorker will preform your long running task (the 5 second one), and inform the user of how it's progressing using the ProgressMonitor. An example showing you how to get the two working together can be found here:
getting the cancel event of Java ProgressMonitor
Of course, if you're convinced you want to take the approach of displaying the continue button once the work is done, here's an example that should get you started in the right direction. You'll use a SwingWorker to alert your Dialog that the long running background task has completed.
import java.awt.*;
import java.awt.Dialog.ModalityType;
import java.awt.event.*;
import javax.swing.*;
public class TempProject extends Box{
public TempProject(){
super(BoxLayout.Y_AXIS);
//Contains the content of the Alert Dialog
Box info = Box.createVerticalBox();
info.add(new Label("Please wait 5 seconds"));
final JButton continueButton = new JButton("Continue");
info.add(continueButton);
//The alert to wait 5 seconds
final JDialog d = new JDialog();
d.setTitle("WAIT");
d.setModalityType(ModalityType.APPLICATION_MODAL);
d.setContentPane(info);
d.pack();
//The action of the "Continue Button"
continueButton.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent arg0) {
d.dispose();
}
});
continueButton.setVisible(false);
//Thread That Does Work
final SwingWorker sw = new SwingWorker<Integer, Integer>()
{
protected Integer doInBackground() throws Exception {
//Do long running thread work here
int i = 0;
while (i++ < 100) {
System.out.println(i);
Thread.sleep(100);
}
return null;
}
#Override
protected void done(){
// What to do when the long runnng thread is done
continueButton.setVisible(true);
}
};
//Button to start the long running task
JButton button = new JButton("Click Me");
button.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent arg0) {
sw.execute();
d.setVisible(true);
}});
add(button);
}
public static void main(String args[])
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setContentPane(new TempProject());
frame.setPreferredSize(new Dimension(500, 400));
frame.pack();
frame.setVisible(true);
}
});
}
}
you could use something like this to stop the code for 5 seconds
try {
Thread.sleep(5000); // do nothing for 5000 miliseconds (5 seconds)
} catch (InterruptedException e) {
e.printStackTrace();
}
Related
I have this actionPerformed method that draws two cards. In between of drawing of those two cards I want to pause the the program for a certain amount of time so that I will be able see drawing of cards one by one. I tried Thread.sleep() method but it just pauses the program after the execution of actionPerformed method.
Because a long-running operation (like pausing) in the Swing event thread will freeze the UI, this is not a recommended strategy. Instead, maybe consider using a Timer to fire a second event that corresponds to the drawing of the second card, as in the example below.
public static void main(String[] args) {
SwingUtilities.invokeLater(()-> {
JFrame frame = new JFrame();
JButton button = new JButton("Ok");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("First card");
Timer timer = new Timer(2000, new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Second card");
}
});
timer.setRepeats(false);
timer.start();
}
});
frame.add(button);
frame.pack();
frame.setVisible(true);
});
}
I created swing application that do some operations in performedAction methode, but when i create a thread that shows the progressBar, the progressBar will not visible during the performedAction methode, but at the end the progressBar will be visible with a value of 100% directly
main class:
import javax.swing.*;
import javax.swing.plaf.ButtonUI;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class main {
public static void main(String[] args) {
JFrame win = new JFrame("Test");
FlowLayout layout = new FlowLayout();
Button b1 = new Button("Click ");
win.add(b1);
b1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
window win = new window();
win.start();
try
{
Thread.sleep(2000);
}
catch(InterruptedException e5){}
}
});
win.setLayout(layout);
win.setSize(500, 300);
win.setLocationRelativeTo(null);
win.setVisible(true);
win.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
}
window class:
import javax.swing.*;
import java.awt.*;
public class window extends Thread{
public window(){
}
#Override
public void run() {
JFrame fen = new JFrame("New window");
FlowLayout layout = new FlowLayout();
fen.setLayout(layout);
Button b2 = new Button();
fen.setVisible(true);
fen.setSize(100, 100);
fen.setLocationRelativeTo(null);
fen.add(b2);
try
{
for(int i = 0; i <= 100; i++)
{
b2.setLabel("Button " + i);
Thread.sleep(10);
}
}
catch(InterruptedException e2){
}
}
}
Yes, it is safe to create a new Thread inside of an actionPerformed handler.
But, no thread other than the Event Dispatching Thread (EDT) must interact with the Swing components.
To create animations, or delays inside of Swing, you must use a javax.swing.Timer. Executing a Thread.sleep() on the EDT is never allowed, and won't do want you hope it will.
To return from a spawned thread to the EDT, you must use SwingUtilities.invokeLater() or SwingUtilities.invokeAndWait().
Using a SwingWorker is the preferred way to execute background tasks in Swing. It handles communication and publication of partial and final results from the background task to the EDT for displaying in the GUI components.
The following is a translation of your code to a working example, using a SwingWorker. Instead of AWT Button's, the Swing JButton is used. The program is created using invokeAndWait to ensure the main window construction occurs on the EDT. A lambda function is used, but you can replace this with new Runnable() { } inner class, if desired. The "Button 0" through "Button 100" progress reports are published from the SwingWorker background task, for processing in the EDT. Multiple results can be generated at by the background thread before the EDT has a chance to process them; here, we take only the last result to display in the button.
public class Main {
public static void main(String[] args) throws Exception {
SwingUtilities.invokeAndWait(() -> {
JFrame win = new JFrame("Test");
win.setLayout(new FlowLayout());
JButton b1 = new JButton("Click");
b1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
DoWork work = new DoWork();
work.execute();
}
});
win.add(b1);
win.setSize(500, 300);
win.setLocationRelativeTo(null);
win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
win.setVisible(true);
});
}
}
class DoWork extends SwingWorker<Void, String> {
JFrame fen;
JButton b2;
DoWork() {
fen = new JFrame("New window");
fen.setLayout(new FlowLayout());
b2 = new JButton();
fen.add(b2);
fen.setSize(100, 100);
fen.setLocationRelativeTo(null);
fen.setVisible(true);
fen.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
}
#Override
protected Void doInBackground() throws Exception {
for (int i = 0; i <= 100; i++) {
publish("Button " + i);
Thread.sleep(10);
}
return null;
}
#Override
protected void process(List<String> data) {
String last = data.get(data.size() - 1);
b2.setText(last);
}
#Override
protected void done() {
fen.dispose();
}
}
There are still a number of improvements that can be made to this code. A JLabel instead of a JButton for displaying the results, a JDialog for the progress window, or perhaps better a ProgressMonitor. These are left as an exercise to the student.
I am trying to make a program that displays 3 MessageDialog boxes at a time. I thought that if you put JOPtionPane.showMessageDialog in an actionListner class for a swing timer, it would show a new MessageDialog box every second.
So here is the code that I came up with:
package pracatice;
import java.awt.event.*;
import javax.swing.*;
public class practice extends JFrame
{
public static int num = 0;
public static TimerClass tc = new TimerClass();
public static Timer timer = new Timer(1000, tc);
public JPanel panel = new JPanel();
public JButton btn = new JButton("press");
public practice()
{
setSize(100,100);
setTitle("Test");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setPanel();
setVisible(true);
}
public void setPanel()
{
btn.addActionListener(new listener());
panel.add(btn);
add(panel);
}
public class listener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
num = 0;
System.out.println("starting timer");
timer.start();
}
}
public static class TimerClass implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
System.out.println("Adding 1 to num");
num++;
JOptionPane.showMessageDialog(null,"Test");
if(num == 3)
{
System.out.println("stopping the timer");
timer.stop();
}
}
}
public static void main(String[] args)
{
practice p = new practice();
System.out.println("created an instance of practice");
}
}
It works, but not the way I want it to. Instead of showing a new box every second it shows a new one 1 second after you press ok on the previous one.
So when I press "press" it waits 1 second and spawns the box. When I press "ok", it waits 1 second and spawns another one and so on. Any idea how to make the 3 boxes spawn 1 after another?
When using the showX methods of JOptionPane you are creating modal (blocking and one at a time) dialogues as stated by the documentation.
You can Use the JOptionPane directly by creating it manually instead of using the showX methods.
create a new one manually and set it to not be modal:
optionPane = new JOptionPane(...);
dialog = optionPane.createDialog(null, "the title");
dialog.setModal(false);
dialog.show();
The methods to create the dialogs (JOptionPane.show...) do not return until the user has somehow closed the dialogs. Given Swing is single threaded, no other Swing process can happen until this occurs. If you wish to have three dialogs open at once, use a non-modal dialog.
Is there a way how to use a dialog in Swing which prohibits any gui activity under it but at the same time DOES NOT stop execution on the thread where it was set to visible?
Yes it can be done .
dlg.setModal(false);
or
dlg.setModalityType(Dialog.ModalityType.MODELESS);
where dlg is instance of your JDialog .
The basic idea of a JDialog IS to block the underlying thread until the user reacts to it. If you need to run something on the UI thread which should not be interrupted, consider using an additional worker thread for it. This way, the UI will be blocked by the JDialog, but the underlying process won't.
Yes, there is a little trick to make it work. We simply deactivate modality and manually disable the JFrame we want to make unclickable.
private final static JDialog dialog; static {
JOptionPane pane = new JOptionPane();
pane.setOptions(new Object[]{}); // Removes all buttons
dialog = pane.createDialog(frame, ""); // Create dialog with pane
dialog.setModal(false); // IMPORTANT! Now the thread isn't blocked
dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
}
Now you can use it like this:
dialog.setVisible(true);
frame.setEnabled(false);
// Logic
dialog.setVisible(false);
frame.setEnabled(true);
Technically, no. Like MadProgrammer wrote in a comment, you are never expected to access any Swing component off-EDT, JDialogs included, therefore the situation you hinted at in the question can never happen (there can never be any thread other than EDT that sets a dialog visible).
You could make it seem like it is, though. That's what SwingUtilities.invokeLater(Runnable) is for (doc).
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class BlockingDialogDemo extends JFrame {
private Timer timer;
private JDialog blocker;
public BlockingDialogDemo() {
setTitle("Blocking Dialog");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 200);
setLocationRelativeTo(null);
blocker = new JDialog(this, true);
blocker.setLayout(new FlowLayout());
blocker.setUndecorated(true);
blocker.getRootPane().setBorder(BorderFactory.createMatteBorder(1, 1, 1, 1, Color.black));
blocker.add(new JLabel("I'm blocking EDT!"));
JProgressBar progress = new JProgressBar();
progress.setIndeterminate(true);
blocker.add(progress);
blocker.pack();
timer = new Timer(3000, new ActionListener() {
public void actionPerformed(ActionEvent e) {
doSomeWork();
}
});
timer.setRepeats(false);
timer.start();
}
private void doSomeWork() {
// this executes on-EDT
Runnable runnable = new Runnable() {
public void run() {
// this executes off-EDT - never ever access Swing components here
showBlocker();
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
System.out.println("Ummm.. I was sleeping here!");
} finally {
hideBlocker();
}
}
};
new Thread(runnable).start();
}
private void showBlocker() {
// this executes off-EDT
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// this executes on-EDT
blocker.setLocationRelativeTo(BlockingDialogDemo.this);
blocker.setVisible(true);
}
});
}
private void hideBlocker() {
// this executes off-EDT
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// this executes on-EDT
blocker.setVisible(false);
timer.restart();
}
});
}
public static void main(String[] args) {
// this is called off-EDT
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// this is called on-EDT
new BlockingDialogDemo().setVisible(true);
}
});
}
}
This works for me... sometimes:
public class NonBlockingModalDialogDemo extends JFrame{
JButton btnDoIt;
public NonBlockingModalDialogDemo() {
setTitle("NonBlockingModalDialog Demo");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300,300);
setLayout(new FlowLayout());
btnDoIt = new JButton("Non-Blocking Notify");
btnDoIt.addActionListener( new ActionListener(){
#Override
public void actionPerformed(ActionEvent arg0) {
JDialog asyncDialog = createNonBlockingModalDialog("Please wait while we do some work", "Please wait");
doWork(50);
//Once your done, just dispose the dialog to allow access to GUI
asyncDialog.dispose();
}
});
this.add(btnDoIt);
}
private JDialog createNonBlockingModalDialog(String message, String title)
{
final JDialog dialog = new JDialog();
dialog.setLayout(new FlowLayout());
dialog.add(new JLabel(message));
dialog.setTitle(title);
dialog.setModal(true);
dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
dialog.setAlwaysOnTop(true);
dialog.pack();
Runnable dialogDisplayThread = new Runnable() {
public void run() {
dialog.setVisible(true);
}};
new Thread(dialogDisplayThread).start();
//Need to wait until dialog is fully visible and then paint it
//or else it doesn't show up right
while(!dialog.isVisible()){/*Busy wait*/}
dialog.paint(dialog.getGraphics());
return dialog;
}
private void doWork(int amount) {
for(int i = 0; i < amount; i++)
{
System.out.println("Doing work step number " + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {}
}
System.out.println("done");
}
public static void main(String[] args) {
new NonBlockingModalDialogDemo().setVisible(true);
}
}
I don't really like that it has a busy wait in it to check if the Dialog is visible yet, but so far I haven't found a way around it. At any rate, the busy wait should not take very long at all, so it really shouldn't matter.
Edit:
I did something very similar to this and for some reason, on some machines, sometimes, it just blocks forever without even showing the dialog.
I haven't figured out the root cause, but this leads me to conclude that all the people who say "never modify the GUI outside of the Event Dispatch Thread" may be on to something.
Perhaps rather than trying to continue the work you need to do on the EDT, maybe you should try something like this:
https://stackoverflow.com/a/4413563/2423283
Which uses SwingWorker to spawn a new thread and then allows you to update the GUI components when you are done.
Let me explain what I am trying to do.
I have two classes extending JFrame, the StartJFrame and TestingJFrame. In the main method, I start up a StartJFrame. It has a single button, start. When that is pressed, I have it set up to hide that frame and start up the TestingJFrame. Right now I don't have anything in the TestingJFrame.
In that screen, I want to have a label in the bottom right corner that is a timer, starting on 45 seconds and counting down to 0. I also need to have some code run every 10th of a second, and collect some data. There will be two buttons in the TestingJFrame, Yes and No. When one of them is pressed, it should stop the timer and save the information.
The data is basically just doubles. I am only going to be collecting data once per run of the program. I have a UserData class that holds some information about the test subject, and a list of doubles, it is added to every 10th of a second. I have a general idea how to save the data in java.
My question is, how should I set up the timer, so that it will count down from 45 seconds, and when it reaches 0 or the user presses the yes or no button it will call a function to save the data? I think I can handle the saving data part.
Sorry if this is really easy, I'm new to Java (from c#) and Swing has been confusing me a bit.
The first part (show the count down and stopping the timer) is relatively easy...
public class TimerTest01 {
public static void main(String[] args) {
new TimerTest01();
}
public TimerTest01() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel label;
private Timer timer;
private long startTime;
public TestPane() {
setLayout(new GridLayout(0, 1));
label = new JLabel("...");
label.setHorizontalAlignment(JLabel.CENTER);
final JButton btn = new JButton("Start");
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (timer.isRunning()) {
timer.stop();
btn.setText("Start");
} else {
startTime = System.currentTimeMillis();
timer.restart();
btn.setText("Stop");
}
repaint();
}
});
add(label);
add(btn);
timer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
long endTime = (startTime + 45000);
long timeLeft = endTime - System.currentTimeMillis();
if (timeLeft < 0) {
timer.stop();
label.setText("Expired");
btn.setText("Start");
} else {
label.setText(Long.toString(timeLeft / 1000));
}
repaint();
}
});
}
}
}
Take a look at Swing Timer for more info
The second part of you question is to vague to reliably provide you with an answer. Where is the data coming from? How is collected??
You can use javax.swing.Timer to setup your timer. You can have a look at the official tutorial too.