I have a jbutton which I want, as soon as it's clicked to make an infinite loop of a mouseMove by a robot class. Then, to be stopped when it is clicked again. Problem is in my code when I press it for the first time, it causes the system to freeze and nothing happens when I click it again. I use:
boolean go = false
jb.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
go = !go;
if (go)
jb.setText("Stop!");
else
jb.setText("Start!");
try {
Robot robot = new Robot();
while (go) {
robot.mouseMove(500, 500);
robot.delay(1000);
robot.mouseMove(500, 400);
}
} catch (AWTException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
Swing is a single threaded environment. This means that if anything should block this thread, Swing will be unable to respond to new events, including paint requests and it will appear that you UI has been frozen...because it has...
Take a look at Concurrency in Swing
The simplest solution might be to spawn a new thread and run your loop within it...
Start by writing a Runnable that handels the work you want done...
public class MouseRunner implements Runnable {
#Override
public void run() {
try {
Robot robot = new Robot();
while (go) {
robot.mouseMove(500, 500);
robot.delay(1000);
robot.mouseMove(500, 400);
}
} catch (AWTException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
Then when the user first clicks the button, create a Thread and start it...
jb.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
go = !go;
if (go) {
jb.setText("Stop!");
Thread t = new Thread(new MouseRunner());
t.start();
} else {
jb.setText("Start!");
}
}
});
Beware, your go variable is likely going to need to made volatile
Related
I have a grid Pane which I want to make visible. I want to then pause the program for 2 seconds and make the grid invisible again. For some reason the grid becomes visible after the thread.sleep that I use in my program.
This all happens inside a button click event.
I tried moving around the thread.sleep, putting them in a new method and using multiple sleep but nothing worked.
gameGrid.setVisible(true)
gameGrid.setVisible(false)
Button event:
public void handleButtonGo(ActionEvent Event) throws IOException { //On go button press
boolean validation = true;
try {
gameGrid.setVisible(true);
placeShips();
}catch (Exception e){
labelwarning.setText(e.getMessage()); //on error the program will stop trying to place ships and refresh any ships placed so far.
validation = false;
//gameGrid.getChildren().clear();
//BoardSetup();
try {
Thread.sleep(2000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
gameGrid.setVisible(false);
}
}
The grid is displayed for a millisecond after the thread.sleep.
Use PauseTransition.
public void handleButtonGo(ActionEvent Event) throws IOException { //On go button press
boolean validation = true;
try {
gameGrid.setVisible(true);
placeShips();
}catch (Exception e){
labelwarning.setText(e.getMessage()); //on error the program will stop trying to place ships and refresh any ships placed so far.
validation = false;
//gameGrid.getChildren().clear();
//BoardSetup();
PauseTransition wait = new PauseTransition(Duration.seconds(2));
wait.setOnFinished((e) -> {
/*YOUR METHOD*/
gameGrid.setVisible(false);
});
wait.play();
}
}
I've been trying to create an autoclicker in java using jnativehook.
It works fine, even compiles and runs. My problem is using Thread.sleep to try and add a delay between clicks:
bot.mousePress(InputEvent.BUTTON1_MASK);
Thread.sleep(50);
bot.mouseRelease(InputEvent.BUTTON1_MASK);
If I were to input a delay of 0 it will function fine. But given no delay it will click too fast.
When I add a delay it will click fine but, when I release the trigger key it will keep clicking for a few seconds given the time it's been clicking. A delay of 0 will not do this however.
Full code:
public class App implements NativeKeyListener{
private JPanel panel1;
private JTabbedPane tabbedPane1;
private JButton spoilerButton;
private JSlider slider1;
private JSlider slider2;
//Removed irrelevant code...
static Robot bot;
static {
try {
bot = new Robot();
} catch (Exception e) {
e.printStackTrace();
}
}
static boolean pressed;
public void click() throws InterruptedException {
try {
bot.mousePress(InputEvent.BUTTON1_MASK);
Thread.sleep(50);
bot.mouseRelease(InputEvent.BUTTON1_MASK);
} catch (Exception e) {
e.printStackTrace();
}}
#Override
public void nativeKeyPressed(NativeKeyEvent e) {
if (NativeKeyEvent.getKeyText(e.getKeyCode()) == "Delete") {
pressed = true;
while (pressed){
try {
click();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}}
#Override
public void nativeKeyReleased(NativeKeyEvent e) {
if (NativeKeyEvent.getKeyText(e.getKeyCode())=="Delete"){
pressed=false;
}}
#Override
public void nativeKeyTyped(NativeKeyEvent e) {
}
public static void main(String[] args) {
JFrame frame = new JFrame("Autoclicker");
frame.setContentPane(new App().panel1);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setSize(400,148);
frame.setResizable(false);
try{GlobalScreen.registerNativeHook();
} catch (Exception e){
System.exit(1);}
GlobalScreen.addNativeKeyListener(new App());
}
}
Looks like the method nativeKeyPressed() gets called continuously when you keep pressing the trigger key. This results in several calls to click() method (assuming it's multi threaded) and due to the sleep() between mouse press and release (mouse click is complete when released) this can happen.
Hence you can try two options depending on the root cause:
1. move the sleep() call after mouse released. If the nativeKeyPressed gets called concurrently this won't work.
2. Use a different thread to execute the click() method when trigger is pressed. In this case, you may need to submit a Runnable object to your thread each time with the 'pressed' check and click() call in it. This will ensure it won't run after pressed becomes false.
private void runInBackground() {
new Thread(new Runnable() {
#Override
public void run() {
while (running) {
try {
checkPixel();
} catch (AWTException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
public void stop() {
this.running = false;
}
public void start() {
this.running = true;
}
So I have some code someone provided me to monitor the change in color in the middle of the screen. I want to essentially turn on/off checkPixel() after I press something like F9 but how can I do this without a GUI because I can't seem to find anything that allows this. I believe KeyListeners only work with GUIs?
EDIT: Ok so instead while I'm checking for pixel changes in the thread. Once a pixel change has been detected I want to cause create a "left click action" in checkPixel() then turn off the thread. Any help with this?
I guess you want functionality provided by JNativeHook library.
The library allows for grabbing a key from the backgrond.
I have read and understood how the Robot class in java works. Only thing I would like to ask, is how do I press and release the mouse button inside an if statement. For example I would to make a click only if (and right after) the space button is pressed/released. I would use the code:
try {
Robot robot = new Robot();
if (/*insert my statement here*/) {
try {
robot.mousePress(InputEvent.BUTTON1_MASK);
robot.mouseRelease(InputEvent.BUTTON1_MASK);
} catch (InterruptedException ex) {}
}
} catch (AWTException e) {}
Unfortunately, there isn't a way to directly control hardware (well, in fact there is, but you would have to use JNI/JNA), this means that you can't simply check if a key is pressed.
You can use KeyBindings to bind the space key to an action, when the spacebar is pressed you set a flag to true, when it's released you set that flag to false. In order to use this solution, your application has to be a GUI application, this won't work with console applications.
Action pressedAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
spaceBarPressed = true;
}
};
Action releasedAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
spaceBarPressed = false;
}
};
oneOfYourComponents.getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "pressed");
oneOfYourComponents.getInputMap().put(KeyStroke.getKeyStroke("released SPACE"), "released");
oneOfYourComponents.getActionMap().put("pressed", pressedAction);
oneOfYourComponents.getActionMap().put("released", releasedAction);
Then, use
try {
Robot robot = new Robot();
if (spaceBarPressed) {
try {
robot.mousePress(InputEvent.BUTTON1_MASK);
robot.mouseRelease(InputEvent.BUTTON1_MASK);
} catch (InterruptedException ex) {
//handle the exception here
}
}
} catch (AWTException e) {
//handle the exception here
}
As GGrec wrote, a better way to do it would be to execute your mouse press directly when the keyboard event is fired:
Action pressedAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
try {
robot.mousePress(InputEvent.BUTTON1_MASK);
robot.mouseRelease(InputEvent.BUTTON1_MASK);
} catch (InterruptedException ex) {
//handle the exception here
}
}
};
My suggestion is that you listen for the keyboard event, and when you receive it, you execute your code without the if statement. Add the listener to your canvas, or whatever.
Careful not to recreate the Robot class each time.
new KeyAdapter() {
#Override
public void keyReleased(final KeyEvent e) {
if (e.keyCode == KeyEvent.VK_SPACE)
try {
robot.mousePress(InputEvent.BUTTON1_MASK);
robot.mouseRelease(InputEvent.BUTTON1_MASK);
} catch (InterruptedException ex) {
}
}
}
I have this ActionListener that gets called in the EDT. My plot() function is computationally heavy, it can easily take five seconds. It made the GUI hang as expected. I added the SwingUtilities.invokeLater code and it still hangs. Shouldn't the GUI be responsive now that I am spawning a separate thread for my heave computation?
final ActionListener applyListener = new ActionListener()
{
#CommitingFunction
public void actionPerformed(ActionEvent arg0)
{
/*Don't do plotting in the EDT :)*/
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
plot();
}
});
}
};
Not at all. InvokeLater is not producing a new thread. invokeLater exists to tell Swing explicitly "use the Event Dispatching Thread for this, but not right now". invoke and invokeLater exist to let you do operations that are only safe for the Event Dispatching Thread from other threads- not by doing them on those threads, but by telling the EDT to do them.
Your ActionListener will run very quickly, throwing the Runnable on Swing's event dispatching queue. Then when it gets that far, it will take five seconds to run the plot().
The only workaround is to refactor plot(). Use a SwingWorker (or similar multithreading strategy, but SwingWorker is probably the best for this) to actually move the logic of plot() onto a different thread. That thread cannot safely draw anything because it is not the Swing Event Dispatching Thread, so all of its draw operations need to be performed via invokeLater(). For efficiency reasons, you should try to do all of the drawing operations at once, on one invokeLater(), using results stored from your calculation.
You're doing the opposite of what you think you are. Instead of running your computation thread outside of the EDT, you're explicitly calling it within it!
SwingUtilities.invokeLater() queues up the runnable for invocation at a later time, in the EDT! You want to use SwingWorker instead.
invokeLater add a task to the GUIs work queue. It will be invoked after all other tasks have been performed, however it still uses the GUI thread.
I suggest you look at using an ExecutorService.
As #Adam suggests, any actual drawing it does needs to be done via invokeLater.
You don't show what is inside the plot() function, but you should not put any painting in there. Compute whatever you want in the new thread, and do the painting in the EDT. To do this, it is better to use SwingWorker
Here is what I did for my company's app, this is some pseudo code because of legal reasons, but the jist of it is that if the screen is unresponsive, it will reboot the GUI. Whenever you use SwingUtilities to kick off the EDT, in that same init block, create two watcher threads. One thread will simply perform an action on the EDT thread using Swing utilities. Another thread will monitor the first thread to see if feels the first thread is responsive. The first thread will only acknowledge responsiveness if it can perform a very simple command.
set isEDTCheck to true when running in normal fashion, false in debug mode (otherwise you'll constantly get rebooted.
if (isEDTCheck) {
new Thread("EDTHeartbeat") {
#Override
public void run() {
Runnable thisThingYouDo = new Runnable() {
public void run() {
int x = 0;
}
};
while (true) {
// first thread says we are waiting, aka bad state
edtwait=true;
try {
javax.swing.SwingUtilities.invokeAndWait(thisThingYouDo);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// first thread says we are not waiting, good state
edtwait=false;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
new Thread("EDTValidator") {
#Override
public void run() {
while (true) {
// is first thread in bad state?
if (edtwait) {
try {
Thread.sleep(3000);
// after 3 seconds are we still in bad state? if so, get rid of initial frame, pop up a dialog box in AWT that does no commands
if (edtwait) {
mainFrame.setVisible(false);
new Dialog();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
}
public class Dialog extends Frame {
private static final int WIDTH = 400;
private static final int HEIGHT = 300;
Frame f = null;
public Dialog() {
f = this;
hasSomethingBeenEntered=false;
this.setTitle("APP PROBLEM DETECTED");
this.setSize(WIDTH, HEIGHT);
this.setLocation((int)Toolkit.getDefaultToolkit().getScreenSize().getWidth() - myapp.width, 0);
Panel p1 = new Panel() {
#Override
public void paint(final Graphics g) {
int left = Dialog.WIDTH/2 - 45; // don't use WIDTH shadowed by Panel class
int top = Dialog.HEIGHT/2 - 20; // same as above
g.drawString("APP HAS DETECTED A PROBLEM", left, top);
}
};
this.add("Center", p1);
this.setAlwaysOnTop(true);
TextArea tb = new TextArea("APP HAS DETECTED A MAJOR PROBLEM\nIT WILL NOW RESTART IN 5 SECONDS");
this.add(tb);
this.setVisible(true);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
restartApp();
}
private void restartApp() {
Runtime.getRuntime().exec("cmd /c start cmd.exe /K \"cd C:\\Progra~1\\Common~1 && C:\\Progra~1\\Common~1\\MyAppDir\\myjavaapp.jar\"");
System.exit(0);
}