Cant stop a while loop with a button - java

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.

Related

Some doubts regarding Java GUI and flow of control in 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);
}
}

I am trying to use javax.swing.timer . The code in actionperformed method is not running. May be the timer is not starting. What is the fault?

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
public class TimeController {
public static void main(String arg[]){
TimeController m = new TimeController();
System.out.println("starting");
m.start();
}
Timer timer = new Timer (1000, new ActionListener(){
public void actionPerformed(ActionEvent e) {
System.out.println("Running");
}
});
public void start(){
timer.start();
}
}
I think there is no fault at all.
The Timer starts its task in the background
Your application terminates before the first execution of the Timers method takes place.
For testing you could add
try {
Thread.sleep(5000);
} catch(Exception e){
// Add exception handling
}
right after `m.start();
For some reason the Timer needs to fire at least once to prevent the program from termination. After it fires once, it does hold the program running, even after the main method terminates.
Probably the bug got unnoticed as this is a rather unusual work flow. Delay the termination of the main thread as proposed by #StefanFreitag.

Execute Events in an Infinite Loop

public void actionPerformed(ActionEvent event)
{
// TODO Auto-generated method stub
JButton src = (JButton) event.getSource(); //get which button is clicked
if(src.equals(GO)) //if GO button is clicked
{
try {
runHack();
} catch (AWTException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(src.equals(STOP)) //if STOP button is clicked
{
//do nothing
FeedBack.setText(null);
FeedBack.setText("Stopped");
}
}
I have a program where when you click on a button GO, it will execute a method called runHack();
private void runHack() throws AWTException
{
FeedBack.setText(null);
FeedBack.setText("Running(This doesn't print out)");
while(true)//infinite loop
{
FeedBack.setText("This doesn't print out");
}
}
runHack() is method that runs an infinite loop. When I click on the GO button, the program freezes while executing the runHack() method. The String "Running" doesn't displayed on the JLabel FeedBack.
My question is how do you make events still available when the program is in the infinite loop? I want it so that when I press on the STOP button, the program exits out of the infinite loop. Also, I want the JLabel FeedBack to work inside the loop.
you need run this infine toop inside of a new thread .this is how to do using timer .swing timer runs in separate thread.set delay to zero.so it's act as a while(true) loop.
in your code you are blocking EDT because of long lasting task(infinite loop) .the changes you made to textfield not get update because EDT is blocked.
you need to swing timer not java.util.Timer
import import javax.swing.Timer;
declare timer
Timer t;//global declaration;
initialize //
t=new Timer(0, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
FeedBack.setText("This doesn't print out");
}
});
when button click//
JButton src = (JButton) event.getSource(); //get which button is clicked
if(src.equals(GO)) //if GO button is clicked
{
try {
t.start();
} catch (AWTException e) {
e.printStackTrace();
}
}
if(src.equals(STOP)) //if STOP button is clicked
{
//do nothing
t.stop();
FeedBack.setText(null);
FeedBack.setText("Stopped");
}
updating...
a complete example
import java.awt.AWTException;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.Timer;
public class example extends JFrame implements ActionListener{
Timer t;
private final JTextField FeedBack;
private JButton go;
private JButton stop;
int i=0;
public example() {
FeedBack=new JTextField("initial text");
go=new JButton("go");
stop=new JButton("stop");
go.addActionListener(this);
stop.addActionListener(this);
this.setLayout(new GridLayout(1, 3));
this.add(go);
this.add(stop);
this.add(FeedBack);
this.setVisible(true);
t = new Timer(0, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
FeedBack.setText(i+"");
i++;
}
});
}
public void actionPerformed(ActionEvent event) {
JButton src = (JButton) event.getSource();
System.out.println(src);
if (src==go) //if GO button is clicked
{
t.start();
}
if (src==stop) //if STOP button is clicked
{
//stop timer
t.stop();
//FeedBack.setText(null);
FeedBack.setText("Stopped");
}
}
public static void main(String[] args) {
example f = new example();
}
}
output>>
Short answer:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run(){
runHack();
}
}).start();
Just take in account that there are better ways to spawn new Threads like using an Executor. Like someone commented, Threads is a broad topic, you should read the documentation.
[EDIT]
Like #Hovercraft Full Of Eels said is not Thread safe calling in a new Thread modifications of the UI.

Why is this java applet code not picking up every MouseClicked event?

I'm following this [slightly old tutorial for 2d java games here][1].
I have a basic applet that runs in a thread with a mouselistener.
On left button click I can shoot up to 10 claypigeons (balls) from the bottom of the window. On right button click I "shoot" and if I hit a pigeon it is removed from the screen.
I've noticed however that sometimes right clicks are not getting picked up. This is not necessarily when there is a lot going on on the screen, although never at the beginning before it all kicks off. At worst it can take 3 or 4 clicks before one is registered.
I'm guessing that I'm doing something obviously wrong in my code, but I'm not sure what. My first thought was the for loops that loop through every object every frame to recalculate their position or check if they have been "shot"? Could they been "blocking" the mouselistener?!
Any tips on how to debug this are welcome!
*********EDIT***********
Ok I've taken the very good advice given below and reduced the code to the smallest nutshell that reproduces the bug. I think I had it in my head that it was all the for loops and the complexity that were causing the problems which is why I included so much in my first code.
So as it happens I can reproduce this bug with almost no code, but at the same time it the bug is much milder. With the code below I just have the basic applet and the mouselistener and on right click a count increments in console and prints its value to screen. This works fine most of the time, but every now and again, a right click is "lost" and not registered.
With the full class, I could sometimes get sequences of 3 or 4 or more right clicks not being registered, so the bug was much more obvious. Anyway code is below, only one class this time:
Main class code:
package javacooperation;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.net.*;
public class ClayPigeonGame extends Applet implements Runnable, MouseListener {
//counters and flags for debugging
private boolean flag = true;
private int click_count = 0;
private boolean game_running;
public void init(){
//set boolean for while loop
game_running=true;
addMouseListener(this);
}
public void start() {
//threading this applet.. why?
Thread th = new Thread(this);
//does this call the run method in the class?
th.start();
}
public void stop(){
game_running=false;
}
public void destroy() { }
public void run(){
while(game_running) {
//updatePigeonPosition();
repaint();
try{
//stop thread for 20 milliseconds
Thread.sleep(20);
} catch (InterruptedException ex){ }
}
}
public void paint(Graphics g){ }
public void mouseClicked(MouseEvent e) {
switch (e.getButton()){
case MouseEvent.BUTTON1:
case MouseEvent.BUTTON2:
break;
case MouseEvent.BUTTON3:
click_count ++;
System.out.println("Right click count: " + click_count);
break;
default:
break;
}
}
//all the mouse listening events required because we implemented MouseListener
public void mouseReleased(MouseEvent e) { }
public void mouseEntered(MouseEvent e) { }
public void mousePressed(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
}
It's possible I guess that your code is interfering with the mouse clicked detection timing as a click is actually both a press and a release with the correct bounds etc. Have you tried not using the clicked call back and instead use the pressed (move the code to mousePressed instead) ?

How to create a delay in Swing

I made a blackjack game, and I want the AI player to pause between taking cards. I tried simply using Thread.sleep(x), but that makes it freeze until the AI player is done taking all of his cards. I know that Swing is not thread safe, so I looked at Timers, but I could not understand how I could use one for this. Here is my current code:
while (JB.total < 21) {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
System.out.println("Oh noes!");
}
switch (getJBTable(JB.total, JB.aces > 0)) {
case 0:
JB.hit();
break;
case 1:
break done;
case 2:
JB.hit();
JB.bet *= 2;
break done;
}
}
BTW, the hit(); method updates the GUI.
so I looked at Timers, but I could not understand how I could use one for this
The Timer is the solution, since as you say you are updating the GUI which should be done on the EDT.
I'm not sure what your concern is. You deal a card and start the Timer. When the Timer fires you decide to take another card or hold. When you hold your stop the Timer.
Well, the following code shows a JFrame with a JTextArea and a JButton. When the buttons is clicked, the Timer send the event repeatedly (with a second delay between them) to the actionListener related to the button which appends a line with the current time.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Calendar;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.Timer;
public class TimerTest extends JFrame implements ActionListener{
private static final long serialVersionUID = 7416567620110237028L;
JTextArea area;
Timer timer;
int count; // Counts the number of sendings done by the timer
boolean running; // Indicates if the timer is started (true) or stopped (false)
public TimerTest() {
super("Test");
setBounds(30,30,500,500);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(null);
area = new JTextArea();
area.setBounds(0, 0, 500, 400);
add(area);
JButton button = new JButton("Click Me!");
button.addActionListener(this);
button.setBounds(200, 400, 100, 40);
add(button);
// Initialization of the timer. 1 second delay and this class as ActionListener
timer = new Timer(1000, this);
timer.setRepeats(true); // Send events until someone stops it
count = 0; // in the beginning, 0 events sended by timer
running = false;
System.out.println(timer.isRepeats());
setVisible(true); // Shows the frame
}
public void actionPerformed(ActionEvent e) {
if (! running) {
timer.start();
running = true;
}
// Writing the current time and increasing the cont times
area.append(Calendar.getInstance().getTime().toString()+"\n");
count++;
if (count == 10) {
timer.stop();
count = 0;
running = false;
}
}
public static void main(String[] args) {
// Executing the frame with its Timer
new TimerTest();
}
}
Well, this code is a sample of how to use javax.swig.Timer objects. In relation with the particular case of the question. The if statement to stop the timer must change, and, obviously, the actions of the actionPerformed. The following fragment is a skeleton of the solution actionPerformed:
public void actionPerformed(ActionEvent e) {
if (e.getComponent() == myDealerComponent()) {
// I do this if statement because the actionPerformed can treat more components
if (! running) {
timer.start();
runnig = true;
}
// Hit a card if it must be hitted
switch (getJBTable(JB.total, JB.aces > 0)) {
case 0:
JB.hit();
break;
case 1:
break done;
case 2:
JB.hit();
JB.bet *= 2;
break done;
}
if (JB.total >= 21) { // In this case we don't need count the number of times, only check the JB.total 21 reached
timer.stop()
running = false;
}
}
}
IMHO this resolves the problem, now #user920769 must think where put the actionListener and the starting/stopping conditions...
#kleopatra: Thanks for show me the existence of this timer class, I don't know nothing about it and it's amazing, make possible a lot of tasked things into a swing application :)
Well, a quick explanation about Timers.
First of all, you need a java.util.Timer variable in your class and another class in your project which extends from java.util.TimerTask (let's call it Tasker).
The initialization of the Timer variable is so easy:
Timer timer = new Timer();
Now the Tasker class:
public class Tasker extends TimerTask {
#Override
public void run() {
actionToDo(); // For example take cards
}
// More functions if they are needed
}
Finally, the installation of the timer with its related Tasker:
long delay = 0L;
long period = pauseTime;
timer.schedule(new Tasker(),delay,period);
The schedule function indicates the following:
Fisrt param: Action to do each period milliseconds (Executes the run function of a TimerTask class or its extension)
Second param: When the timer must start. In this case, it starts when the schedule function is called. The following example indicates a starting 1 second after call the schedule function: timer.schedule(new Tasker(),1000,period);
Third param: milliseconds between one call of Tasker.run() function and the following call.
I hope you understand this microtutorial :). If you have any problem, ask for more detailed information!
Kind regards!
I think that in this tutorial is clear how to use Timers in order to achieve what you want, without having to deal with Threads.

Categories

Resources