I have a Java program compiled in a .jar, so the end user can't really just ctrl+c it in the console.
They have to end the java process in the task manager.
However, there is a much simpler way, isn't there?
public class Test extends JFrame {
private JPanel contentPane;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Test frame = new Test();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public Test() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
setContentPane(contentPane);
JButton go = new JButton("Go");
go.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Process p;
Runtime r = Runtime.getRuntime();
while(true) {
try {
p = r.exec("notepad.exe");
p.waitFor();
} catch (IOException | InterruptedException e1) {
e1.printStackTrace();
}
}
}
});
contentPane.add(go);
}
}
As you can see, all it does, once you press the Go button, is spawn a notepad process.
As soon as you close notepad, it spawns another one. I want it to do that.
However, there's no way to stop it halting. For example, pressing the X on the pane doesn't do anything.
How do I make it so that the X effectively closes the Java program, while keeping all the contingencies above?
1) the action performed is running on the EDT thread: the java thread executing all events/event handlers. As p.waitFor does not return immediately it will block all futher event handling
2) one should never run long running actions on the edt thread. In this case I suggest to spwan a new thread that will start the notepad.exe and wait for it in a different thread...
3) another point is: why do you want to wait for the notepad.exe to exit ? There is some subtle inconsistency here, from one perspective you want the application to continue normal processing (clicking on the x box should exit the application) and on the other hand you want your application not to continue normal processing as you wish to wait for the notepad to exit...
explain your contigencies a bit better
Related
I have been learning java GUI(swing to be precise) by referring online sources and practising.
Code:(p is a JButton)
p.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e)
{
System.out.println("Welcome to Guess the number Game");
System.out.println("You have 3 chances to guess a number between 0 and 10 excluding 10");
ne.remove(p);
ne.revalidate();
ne.repaint();
l.dispose(); //l is a JFrame
gamer2 game=new gamer2();
game.generatenum(); //works on the console
l.setVisible(true);
}});
Problems:
In some related questions I posted earlier,I learnt that any update/change done to java GUI will only be effective after the actionPerformed() is completely executed. But the thing is l.dispose() works or the Jframe l disappears even before generatenum() function is completely executed which means actionPerformed() is still not completed executing but the JFrame disappears. generatenum() runs on the console.The thing is button is removed only after actionPerformed() is completely executed but why is this different in case of the frame.
I am also aware that java control flows from line to line(atleast in the above example).The JFrame reappers since I have written l.setVisible(true);. But this happens before generatenum() is completely executed. The generatenum() will only stop running if I enter a suitable number on the console.So how is the control jumping to l.setVisible(true) before the previous line/function execution is completed.
What is generatenum()?
It is a function which accepts user input on the Eclipse console.It doesn't stop running unless it receives a valid input from the user.
void generatenum()
{
int ran=(int)(Math.random()*10);
System.out.println("For developer-no.selected "+ran);
getUserInput(ran);
}
void getUserInput(int k)
{
i++;
System.out.println("print now-Chance "+i);
g.gotValue=k;
InputStreamReader j=new InputStreamReader(System.in);
BufferedReader s=new BufferedReader(j);
try {
int getIt1=Integer.parseInt(s.readLine());
g.getIt=getIt1;
} catch (IOException e)
{
e.printStackTrace();
}
}
generatenum() works on the console.
The frame visibillity cahnges (wich is set to false on dispose) takes place not in the GUI Message Loop (called AWT Event-Dispatching Thread for AWT/Swing).
With the following test program one see the frame (window) disappear and reapear every second after the button is clicked once.
Everything on the frame (like the button in the test program) will not be repainted because the message loop is busy. The message loop (see link above) is the loop in wich all the GUI events are actually executed. In other frameworks like WPF framework or WinForms for C# or SWT for Java one gets an exception if a GUI is accesed from another thread.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Main {
public static void main(String[] args) {
final JFrame f1=new JFrame();
f1.setBounds(100,100,100,100);
final Button b = new Button();
b.setLabel("Test");
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e1) {
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
f1.setVisible(!f1.isVisible());
}
}
});
f1.add(b);
f1.setVisible(true);
}
}
I wrote a java program and made a GUI (my first one jeej).
This is my main methode:
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
ui frame = new ui();
frame.setVisible(true);
} catch (Exception e) {
ui.log("Something went wrong: " + e.getMessage());
}
}
});
}
The methode ui.log let's me write to a textArea.
My gui has one button which starts a pretty long methode which takes several minutes to complete.
In this long methode I want to be able to log to my textArea using ui.log().
It works, but It is only displayed when the methode ends and I want to see the results while the methode is running.
Both the button and the buttonEvent listener are made inside new ui();
JButton btnNewButton = new JButton("button1");
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
thveryLongMethodeThatIwantToLogDuringExecution();
}
});
Does anyone knows how I can log while the methode is running?
EDIT: I think it has something to do with threads but I'm not sure. I never done something with threads.
Don't perform your work on the EDT. Use the javax.swing.SwingWorker to run time-consuming background tasks.
I am trying to learn ProgressMonitor in Java Swing.
I created this simple test code -
public class ProgressMonitorTest extends JFrame
{
private JPanel contentPane;
private ProgressMonitor progressMonitor;
private JButton button;
private static ProgressMonitorTest frame;
private static boolean isFrameReady;
public JButton getButton()
{
return button;
}
public ProgressMonitor getProgressMonitor()
{
return progressMonitor;
}
/**
* Launch the application.
*/
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
try
{
frame = new ProgressMonitorTest();
frame.setVisible(true);
isFrameReady = true;
}
catch (Exception e)
{
e.printStackTrace();
}
}
});
while(!isFrameReady)
{
//
}
frame.getButton().addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
try
{
for(int i=0;i<=10;i++)
{
final int percent = i;
SwingUtilities.invokeAndWait(new Runnable()
{
#Override
public void run()
{
frame.getProgressMonitor().setProgress(percent * 10);
frame.getProgressMonitor().setNote("Completed " + percent*10 + "%.");
}
});
try
{
Thread.sleep(1000);
}
catch(Exception ee)
{
//
}
}
}
catch(Exception es)
{
//
}
}
});
}
/**
* Create the frame.
*/
public ProgressMonitorTest()
{
isFrameReady = false;
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
setTitle("Progress Monitor");
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
progressMonitor = new ProgressMonitor(frame, "Update in progress...", "", 0, 10);
button = new JButton("Click Here");
contentPane.add(button);
setContentPane(contentPane);
}
}
A few questions regarding this-
If I remove the isFrameReady check, the program says a NullPointerException at the line where I assign the button's action listener.
If I keep the above check, then clicking on the button does nothing.
Keeping the above check and then debugging this, I let it wait for some time before it gets to the line where the action listener. In this case, it works but immediately quits saying it can't call invokeAndWait from the event handling thread.
What am I missing in all this ? Can someone explain how to get this to work.
If I remove the isFrameReady check, the program says a
NullPointerException at the line where I assign the button's action
listener.
your use of isFrameReady ensures that you have created your frame successfully. inside your main, your posted request to event dispatch thread(EDT) using call EventQueue.invokeLater(new Runnable(){}): removing the check isFrameReady, you were going to call frame.getButton() in main thread but the frame have not been yet created by frame = new ProgressMonitorTest(); in the EDT and thus a NullPointerException occurs.
If I keep the above check, then clicking on the button does nothing.
you should understand by now, that above check is nothing to do with button click. The button is not doing anything because the GUI got freezed for violating swing's single threading rule. Put your incrementing for loop of the actionPerformed method inside another thread as the following code fragement shows and execute it from there. you will see that it works fine.
new Thread(){
public void run()
{
for(int i=0; i<10; i++)
{
//whatever you were doing.
}
}
}.start();
Keeping the above check and then debugging this, I let it wait for
some time before it gets to the line where the action listener. In
this case, it works but immediately quits saying it can't call
invokeAndWait from the event handling thread.
SwingUtitlies.invokeAndWait() blocks the current thread and waits until the EDT is done executing the task given to it. As actionPerformed() function is already running inside EDT, so calling SwingUtitlies.invokeAndWait() from the current thread:EDT would block the current thread:EDT which should not be allowed. Don't use invokeAndWait for this case. you should call SwingUtilities.invokeLater() instead.
However I don't think you will get anything until you understand Swing threading model. Read the javadoc and some internet resource. DO HAVE The book Filthy Rich Clients and try the example the book offered: You will have a greater knowledge in graphical effects then any other resource can provide.
Implementation details: I'm working on a school project in which I have to simulate some queues. At random intervals, clients should be generated, the client selects one queue(i can have multiple queues) to enter, and is added to that queue data structure. Each queue has its own operator which removes clients form the queue it's attached to.
The problem: The client generator is run in a separate thread. The queue graphical representation is that of a ArrayList of JButtons, displayed on a GridLayout panel, with only 1 column. When I try to add a client(a JButton) to the panel, I want to use SwingWorker's publish() to publish a new JButton, to be added to the list. However, after a lot of headaches, and System.out.println's to figure out what's going one, I observed that the System.out.println() in the process() method is called only after the doBackground() method has finished.
Code Here:
//run method of the ClientGenerator thread
public void run()
{
System.out.println("Into thread Generator");
SwingWorker<Void,JButton> worker=new SwingWorker<Void, JButton>()
{
int sleepTime;
#Override
protected Void doInBackground() throws Exception
{
while(checkTime())
{
try
{
sleepTime=minInterval+r.nextInt(maxInterval - minInterval);
System.out.println("Sleeping - "+sleepTime+" milis");
Thread.sleep(sleepTime);
System.out.println("Woke up,"+sleepTime+" milis elapsed");
} catch (InterruptedException e)
{
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
System.out.println("Generating client...");
newClient=new Client(clientMinService,clientMaxService,log);
System.out.println("Locking lock...");
operationsOnTheQueueLock.lock();
selectedQueueOperator=selectQueueOperator();
System.out.println("Adding new client to queue...");
selectedQueueOperator.getQueue().enqueue(newClient);
System.out.println("Publishing new JButton...");
publish(new JButton("C"+selectedQueueOperator.getClientIndicator()));
//}
// else
// {
// queueHolder.add(selectedQueueOperator.getQueueClients().get(0);
// }
System.out.println("Unlocking lock...");
operationsOnTheQueueLock.unlock();
System.out.println("Lock unlocked! Should enter while again and sleep");
}
return null;
}
#Override
public void process(List<JButton> chunks)
{
newClientButton=chunks.get(chunks.size()-1);
System.out.println("Process runs.Jbutton index="+newClientButton.getText());
newClientButton.setFont(new Font("Arial", Font.PLAIN, 10));
newClientButton.setBackground(Color.lightGray);
newClientButton.setVisible(true);
newClientButton.setEnabled(false);
clients=selectedQueueOperator.getQueueClients();
clients.add(newClientButton);
selectedQueueOperator.setQueueClients(clients);
// if(selectedQueueOperator.getQueueClients().size()>0)
// {
queueHolder=selectedQueueOperator.getQueueHolder();
queueHolder.add(clients.get(clients.size()-1));
selectedQueueOperator.setQueueHolder(queueHolder);
}
// return null; //To change body of implemented methods use File | Settings | File Templates.
};
worker.execute();
try {
worker.get();
} catch (InterruptedException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
} catch (ExecutionException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
}
output:
Sleeping - 1260 milis
Woke up,1260 milis elapsed
Generating client...
Locking lock...
Adding new client to queue...
Publishing new JButton... ///here I should see "Process runs.Jbutton index=C0"
Unlocking lock...
Lock unlocked! Should enter while again and sleep
Sleeping - 1901 milis
Woke up,1901 milis elapsed
Generating client...
Locking lock...
Adding new client to queue...
Publishing new JButton...///here I should see "Process runs.Jbutton index=C1
Unlocking lock...
Lock unlocked! Should enter while again and sleep
Process runs.Jbutton index=C0 //instead, Process runs only in the end.
This is just a basic example, for 2 iterations. Clients should be generated only from time to time, so at the beginning I sleep the thread for a certain amount of time. Then I generate the client object, then I want to generate and add the button to my JPanel component, in the process() method.
This last part, obviously isn't happening. Any ideeas why? I'm out of things to try, regarding SwingWorker...
Thanks in advance!
Later edit: "lock" is defined as:
Lock lock = new ReentrantLock();
and is passed as a parameter from the class that managed my ClientsGenerator(this) class, and my class which removes clients from the queue. It's used to synchronize the two, when performing operations over the ArrayList& display.
The whole point of threads is that things are not executed in sequence.
doInBackground() can finish (a while loop iteration) before process() is called.
doInBackground() runs on the swing worker thread
process() runs on the EDT.
process() will run before done() (because it also runs on the EDT).
As noted by the other answer: you should only publish the text, then create the JButton in process().
Note normally you start a SwingWorker from the EDT, in that case you should not call get() on the EDT (which would block it).
Simple example:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.*;
import javax.swing.*;
public class SwingWorkerTest {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
final JPanel panel = new JPanel(new GridLayout(0, 1));
new SwingWorker<Void, String>() {
#Override
protected Void doInBackground() throws Exception {
Random random = new Random();
int count = 1;
while (count < 100) {
publish("Button " + (count++));
Thread.sleep(random.nextInt(1000) + 500);
}
return null;
}
#Override
protected void process(List<String> chunks) {
for (String text : chunks) {
panel.add(new JButton(new AbstractAction(text) {
#Override
public void actionPerformed(ActionEvent e) {
panel.remove((JButton) e.getSource());
panel.revalidate();
panel.repaint();
}
}));
}
panel.revalidate();
panel.repaint();
}
}.execute();
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new JScrollPane(panel));
frame.setPreferredSize(new Dimension(400, 300));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
You should not allocate or access UI components in doInBackground(). All UI interactions should be on Event Dispatch Thread. Do this in done() or process() which are executed on EDT. See Concurrency in Swing for details on Swing's single threaded nature.
Also, there is a dangerous game with operationsOnTheQueueLock locking. You may be locking the thread. Please consider posting all the relevant code as a working sample, ie SSCCE.
See SwingWorker docs, it has a pretty good example how to utilize publish()/process() methods.
When the program starts, a new JFrame is created. Once the user clicks the start button a thread is created and started. Part of this threads execution is to validate the data on the form and then execute with that data. Once the data has been validated the thread calls dispose() on the original frame and then creates a new JFrame that acts as a control panel.
There is also an automatic mode of the program that doesn't display any GUI at all, this mode reads data from a configuration file and then starts the execution thread and runs everything but without the control panel.
I want the program to end once the thread completes, but in GUI mode, only if the user has closed the control panel as well.
Is it possible to make the thread wait for the frame to close. I assuming that the frame is run from it's own Thread? or is that not the case.
Thanks.
The answer you chose is a little awkward. Using Thread.sleep(1000) will check for window state every second. It is not a performance issue, but just bad coding style. And you may have a one second response time.
This code is a little bit better.
private static Object lock = new Object();
private static JFrame frame = new JFrame();
/**
* #param args
*/
public static void main(String[] args) {
frame.setSize(300, 300);
frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
frame.setVisible(true);
Thread t = new Thread() {
public void run() {
synchronized(lock) {
while (frame.isVisible())
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Working now");
}
}
};
t.start();
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent arg0) {
synchronized (lock) {
frame.setVisible(false);
lock.notify();
}
}
});
t.join();
}
You can make reference from your thread to the JFrame. Then set the default close operation of JFrame to HIDE_ON_CLOSE. If the JFrame is closed, you can stop the thread.
Example code:
import java.awt.Dimension;
import javax.swing.JFrame;
public class FrameExample extends JFrame {
public FrameExample() {
setSize(new Dimension(100, 100));
setDefaultCloseOperation(HIDE_ON_CLOSE);
setVisible(true);
}
private static class T implements Runnable {
private FrameExample e;
public T(FrameExample e) {
this.e = e;
}
#Override
public void run() {
while (true) {
if (e.isVisible()) {
// do the validation
System.out.println("validation");
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
break;
}
}
}
}
}
public static void main(String[] args) {
FrameExample frameExample = new FrameExample();
new Thread(new T(frameExample)).start();
}
}
All Swing components, including JFrame, are managed by a single thread, called the Event Dispatch Thread, or EDT. (It's possible to call methods on Swing objects from other threads, but this is usually unsafe, except in a few cases not relevant here.)
You'll probably accomplish what you want here by putting the data validation and execution code in its own object which is otherwise completely unaware of the outside world. Then, call it from one of two other objects: one that manages a GUI, and another that runs in "automatic mode".