Some doubts regarding Java GUI and flow of control in Java - java

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

Related

How to interrupt p.waitFor()?

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

Not getting the output as expected in Swing

i know multithreading a bit but not in vast and i think the problem is of multithreading. I am calling a method to set label's text by invoking a new thread and leaving it blank after a specified time. I am getting the desired output every time but not only the place which i am going to show you by my piece of code. I am expecting that message should be set and disappeared after the specified time and the window should be minimized after that time. But what actually happening is when it is going to the other thread main thread execution starts and goes for sleep for 5 sec and the message is not appearing and after 5 sec window is getting minimized without showing the message which i am setting on the label.
(Main thread)
Validation.setMessageOnLabel("Username and password has been copied", jLabel15,1.5F);
try {
Thread.sleep(5000);
} catch (InterruptedException ex) {
Logger.getLogger(PasswordManager.class.getName()).log(Level.SEVERE, null, ex);
}
setState(ICONIFIED);
validation.java (setMessageOnLabel())
static public void setMessageOnLabel(final String msg, final JLabel label, final float time)
{
new Thread(new Runnable() {
#Override
public void run() {
label.setText(msg);
try {
Thread.sleep((long) (time*1000));
} catch (InterruptedException ex) {
Logger.getLogger(PasswordManager.class.getName()).log(Level.SEVERE, null, ex);
}
label.setText("");
}
}).start();
}
Since you're calling setState() directly, I assume the first code snippet is part of a JFrame. In that case you're most probably sending the event dispatch thread to sleep for 5 seconds and thus prevent screen updates during that time.
Put the sleep into another thread or use a swing worker instead and call setState() on the EDT in the worker's callback method, since setState() is not labelled as thread-safe and calling it on a thread other than the EDT might result in unexpected behavior.
From the linked tutorial:
Some Swing component methods are labelled "thread safe" in the API specification; these can be safely invoked from any thread. All other Swing component methods must be invoked from the event dispatch thread. Programs that ignore this rule may function correctly most of the time, but are subject to unpredictable errors that are difficult to reproduce.
Don't use Thread.sleep(5000);, that block EDT.
For that purposes you can use swing Timer, examine next example:
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.Timer;
public class TestFrame extends JFrame {
private JLabel lbl;
public TestFrame() {
init();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
private void init() {
lbl = new JLabel(" ");
JButton setText = new JButton("setText");
setText.addActionListener(getActionListener());
add(lbl);
add(setText,BorderLayout.SOUTH);
}
private ActionListener getActionListener() {
return new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
lbl.setText("wait...");
Timer t = new Timer(5000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
lbl.setText("");
setState(JFrame.ICONIFIED);
}
});
t.setRepeats(false);
t.start();
}
};
}
public static void main(String args[]) {
new TestFrame();
}
}
When dealing with Swing components you shuld not use threads like that. Launch your own SwingWorker instead.
public class MySwingWorker extends SwingWorker<Object, Object> {
#Override
public Object doInBackground() {
//your code here
//dont forget to repaint changed component or validate parent of it,
//if your text dont shows up.
return null;
}
}
you can also execute your own runnable via SwingUtilites
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//again your code here...
}
});

Swing - Thread.sleep() stop JTextField.setText() working [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
using sleep() for a single thread
I'm having issues with JTextField.setText() when using Thread.sleep(). This is for a basic calculator I'm making. When the input in the input field is not of the correct format I want "INPUT ERROR" to appear in the output field for 5 seconds and then for it to be cleared. The setText() method did work when I just set the text once to "INPUT ERROR" and by printing out the text in between I found it does work with both that and the setText("") one after the other. The problem arises when I put the Thread.sleep() between them.
Here's a SSCCE version of the code:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.regex.Pattern;
import javax.swing.*;
public class Calc {
static Calc calc = new Calc();
public static void main(String args[]) {
GUI gui = calc.new GUI();
}
public class GUI implements ActionListener {
private JButton equals;
private JTextField inputField, outputField;
public GUI() {
createFrame();
}
public void createFrame() {
JFrame baseFrame = new JFrame("Calculator");
baseFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel contentPane = new JPanel();
BoxLayout layout = new BoxLayout(contentPane, BoxLayout.Y_AXIS);
contentPane.setLayout(layout);
baseFrame.setContentPane(contentPane);
baseFrame.setSize(320, 100);
equals = new JButton("=");
equals.addActionListener(this);
inputField = new JTextField(16);
inputField.setHorizontalAlignment(JTextField.TRAILING);
outputField = new JTextField(16);
outputField.setHorizontalAlignment(JTextField.TRAILING);
outputField.setEditable(false);
contentPane.add(inputField);
contentPane.add(outputField);
contentPane.add(equals);
contentPane.getRootPane().setDefaultButton(equals);
baseFrame.setResizable(false);
baseFrame.setLocation(100, 100);
baseFrame.setVisible(true);
}
/**
* When an action event takes place, the source is identified and the
* appropriate action is taken.
*/
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == equals) {
inputField.setText(inputField.getText().replaceAll("\\s", ""));
String text = inputField.getText();
System.out.println(text);
Pattern equationPattern = Pattern.compile("[\\d(][\\d-+*/()]+[)\\d]");
boolean match = equationPattern.matcher(text).matches();
System.out.println(match);
if (match) {
// Another class calculates
} else {
try {
outputField.setText("INPUT ERROR"); // This doesn't appear
Thread.sleep(5000);
outputField.setText("");
} catch (InterruptedException e1) {
}
}
}
}
}
}
I'm not actually using a nested class but I wanted it to be able to be contained in one class for you. Sorry about how the GUI looks but again this was to cut down the code. The the important section (if (e.getSource() == equals)) remains unchanged from my code. The simplest way to give an incorrect input is to use letters.
When you use Thread.sleep() you're doing it on the main thread. This freezes the gui for five seconds then it updates the outputField. When that happens, it uses the last set text which is blank.
It's much better to use Swing Timers and here's an example that does what you're trying to accomplish:
if (match) {
// Another class calculates
} else {
outputField.setText("INPUT ERROR");
ActionListener listener = new ActionListener(){
public void actionPerformed(ActionEvent event){
outputField.setText("");
}
};
Timer timer = new Timer(5000, listener);
timer.setRepeats(false);
timer.start();
}
As Philip Whitehouse states in his answer, you are blocking the swing Event Dispatch Thread with the Thread.sleep(...) call.
Given that you've taken the time to set up an ActionListener already, it would probably be easiest to use a javax.swing.Timer to control clearing the text. To do this, you could add a field to your GUI class:
private Timer clearTimer = new Timer(5000, this);
In the constructor for GUI, turn off the repeats feature, as you really only need a one-shot:
public GUI() {
clearTimer.setRepeats(false);
createFrame();
}
Then, actionPerformed can be modified to use this to start the timer/clear the field:
public void actionPerformed(ActionEvent e) {
if (e.getSource() == equals) {
inputField.setText(inputField.getText().replaceAll("\\s", ""));
String text = inputField.getText();
System.out.println(text);
Pattern equationPattern = Pattern.compile("[\\d(][\\d-+*/()]+[)\\d]");
boolean match = equationPattern.matcher(text).matches();
System.out.println(match);
if (match) {
// Another class calculates
} else {
clearTimer.restart();
outputField.setText("INPUT ERROR"); // This doesn't appear
}
} else if (e.getSource() == clearTimer) {
outputField.setText("");
}
}
You're doing a Thread.sleep() in the Swing main thread. This is NOT good practice. You need to use a SwingWorker thread at best.
What's happening is that it's running the first line, hitting Thread.sleep().
This prevents the (main) EDT thread from doing any of the repaints (as well as preventing the next line executing).
You should use a javax.swing.Timer to setup the delayed reaction and not put sleep() calls in the main thread.

Cant stop a while loop with a button

Im trying to write a program which moves the mouse every 3 minutes (to stop a screen saver coming on) but I want to be able to stop and start it at will. As you can see below I have create the buttons and method but when you click run it steps into the while loop and because its in effect an infinite loop it won't look and see if you have clicked the end button.
I have tried system.exit(0) on click for the end button, having the end button pass in false to the method run() and as you can see from the code ive tried an if statement in the while loop to see if it will take notice of me!
Any help would be greatly appreciated!
code:
import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Robot;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test
{
boolean loop;
static boolean exit;
public static void main(String[] args) throws AWTException
{
System.out.println("before");
makeButtons();
System.out.println("after");
}
public static void makeButtons()
{
JFrame jfrMain = new JFrame ("Mouse Robot");
JPanel jplMain = new JPanel();
final JButton run = new JButton("Run");
final JButton end = new JButton("End");
run.setEnabled(true);
end.setEnabled(true);
run.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
//run.setEnabled(false);
//end.setEnabled(true);
try {
run(true);
} catch (AWTException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
end.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
exit = true;
}
});
jplMain.setLayout(new FlowLayout());
jplMain.add(run);
jplMain.add(end);
jfrMain.getContentPane().add(jplMain, BorderLayout.CENTER);
jfrMain.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jfrMain.pack();
jfrMain.setVisible(true);
}
public static void run(boolean loop) throws AWTException
{
Robot r2d2 = new Robot();
while(loop)
{
System.out.println("1");
Point mousePoint = MouseInfo.getPointerInfo().getLocation();
mousePoint.translate(0, 1);
r2d2.mouseMove(mousePoint.x, mousePoint.y);
r2d2.delay(60000);
//r2d2.delay(60000);
//r2d2.delay(60000);
System.out.println("2");
mousePoint = MouseInfo.getPointerInfo().getLocation();
mousePoint.translate(0, -1);
r2d2.mouseMove(mousePoint.x, mousePoint.y);
r2d2.delay(60000);
//r2d2.delay(60000);
//r2d2.delay(60000);
System.out.println("looping");
if (exit = true)
{
break;
}
}
}
}
Well first correct the condition for exit i.e., make it exit == true as mentioned in the first answer.
Second I don't think even this is going to fix your problem because you are making an infinite loop in the actionPerformed which gets called by EDT (Even Dispatch Thread) and this will halt the event processing altogether. So instead start a new Thread inside the actionPerformed method that moves the mouse. Keep a reference to that thread so that you can stop/interrupt the thread or you can also set the exit condition to stop the thread.
Let me know if you need a code example for this.
Try
if (exit == true)
{
break;
}
if (exit == true)
{
break;
}
I have some doubt about that working out, though I have yet to try it. Wouldn't Java run while without ever listening to mouse, which will end up as infinite loop?
I think it'll have to be some equivalent of javascript function setInterval() to move the mouse and clearInterval() once the button has been clicked.
first of all make it exit == true;
instead of using exit = true; you can use loop = false.
Still it will not stop while loop as soon as you click on end button.
To stop it immediately after button clicked you must use two different threads.
1. Handling your events.
2. another one running the while loop.
In event handling thread you have to maintain a object of while loop thread, by which you can set appropriate value of loop or exit variable to stop the loop.

How do I make a thread wait for JFrame to close in Java?

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".

Categories

Resources