0

I am looking to create an autoclicker with a JFrame GUI in Java with the Robot class. I was wondering if this is the best way to create the GUI in different threads so that it would remain responsive even while the autoclicker/robot is performing actions?

I read in the Oracle documentation that Swing objects have their own Event Dispatcher Thread, so maybe this isn't necessary at all? If so, what is it? Thanks in advance!

package Engine;
import javax.swing.*;
import GUI.UI;

public class Engine {

    private Thread UIthread, clickerThread;
    public UI ui;
    public AutoClicker autoClicker;

    public Engine(int width, int height) {

        ui = new UI(width, height);
        ui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        ui.setVisible(true);

        UIthread = new Thread(ui);
        UIthread.start();

        //autoclicker is where the Robot class 
        autoClicker = new AutoClicker();
        clickerThread = new Thread(autoClicker);
        clickerThread.start();
    }



}

Here's the UI(and its event handler) and AutoClicker classes respectively in case it helps:

package GUI;

import javax.swing.*;

public class UI extends JFrame implements Runnable{

    public int width, height;
    JButton start, stop;


    public UI(int width, int height) {
        this.width = width;
        this.height = height;
        this.setLayout(null);
        this.setSize(width, height);
    }

    public void init() {
        start = new JButton();
        start.setText("start");
        start.setBounds(100, 100, 100, 30);
        add(start);

        stop = new JButton();
        stop.setText("stop");
        stop.setBounds(100, 140, 100, 30);
        add(stop);

        EventHandler eHandler = new EventHandler(this);
        start.addActionListener(eHandler);
        stop.addActionListener(eHandler);
    }

    @Override
    public void run() {
        init();

    }
}

EventHandler class

    package GUI;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;

    public class EventHandler implements ActionListener{

        private UI ui;

        public EventHandler(UI ui) {
            this.ui = ui;
        }

        public void actionPerformed(ActionEvent actionEvent) {
            if(actionEvent.getSource().equals(ui.start)) {
                //start autoclicking
                System.out.println("start");
            }else if(actionEvent.getSource().equals(ui.stop)) {
                //stop autoclicking 
                System.out.println("stop");
            }

        }

    }

The autoclicker class:

package Engine;

import java.awt.AWTException;
import java.awt.Robot;

public class AutoClicker implements Runnable{

    private Robot robot;

    public void run() {

        try {
            robot = new Robot();
            //do mouse clicks and stuff 

        } catch (AWTException e) {
            e.printStackTrace();
        }


    }

}
Jeff
  • 610
  • 4
  • 12
  • `this.setLayout(null);` 1) Java GUIs have to work on different OS', screen size, screen resolution etc. using different PLAFs in different locales. As such, they are not conducive to pixel perfect layout. Instead use layout managers, or [combinations of them](http://stackoverflow.com/a/5630271/418556) along with layout padding and borders for [white space](http://stackoverflow.com/a/17874718/418556). 2) For better help sooner, post a [MCVE] or [Short, Self Contained, Correct Example](http://www.sscce.org/). – Andrew Thompson Aug 18 '17 at 18:04
  • `I was wondering if this is the best way to create the GUI in different threads so that it would remain responsive even while the autoclicker/robot is performing actions?` - how do you expect this to work? If the user is busy doing something on the frame and the Robot does something then what happens when the two function are done at the same time. As a user, I would get very frustrated moving the mouse to a component only to have the robot move it to a different component. – camickr Aug 18 '17 at 21:35
  • The point about designing responsive GUI's is to have code the is executed in listener code be efficient. The listener code won't know if the user or the robot clicked the button. So the Robot executing in a separate Thread is not really the issue. Any long running listener code needs to execute in a separate Thread. Read the section from the Swing tutorial on [Concurrency](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html) for more information. – camickr Aug 18 '17 at 21:38
  • @camickr Thanks for the comment! This isn't really for consumer use nor for a school grade, it's just for my personal use so I can tolerate some sort of bodging by letting the robot sleep for a set period of time, allowing me a window of opportunity to press the stop button. My main question was really just "how" to multi-thread a GUI and another object since I'm a really novice and youtube self taught programmer. – Jeff Aug 19 '17 at 14:36
  • However, if it is possible for the listener to distinguish between the robot clicking and the actual user clicker (for example having some variable update the listener when the robot invokes the mouse click method) I would love to hear it! – Jeff Aug 19 '17 at 14:36

1 Answers1

1

The Event Dispatching Thread you are talking about is indeed the thread you should be using with javax.swing to create a responsive GUI. When you are going to display a window, you should schedule a job for the EDT via the javax.swing.SwingUtilites class. For example:

import javax.swing.*;

public class HelloWorldSwing {
    private static void createAndShowGUI() {
        // Create and set up the window.
        JFrame frame = new JFrame("HelloWorldSwing");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // Add the ubiquitous "Hello World" label.
        JLabel label = new JLabel("Hello World");
        frame.getContentPane().add(label);

        // Display the window.
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        // Schedule a job for the event-dispatching thread:
        // creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
}

(code taken directly from HelloWorldSwing.java)

The important part here is in the main method: it ensures that other code is free to use the main thread and the GUI doesn't get unresponsive when, say, you're downloading a large file (then again, that should be in its own thread too, depending on the situation).

So now, if we took the code you supplied and put in a Main class:

import Engine.*;

public class Main {
    public static void main(String[] args){
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new Engine(640, 400);
            }
        });
    }
}

With all this, the GUI now runs in a separate thread, but in the Engine class two new threads are created, resulting in the EDT having two sub-threads, in a way. You should eliminate the thread for the UI; all it really does is add extra components to the original window in a separate thread, which is kind of overkill when you think about it.

Now, about the autoclicker. You should implement this so that when the 'start' button is pressed, a new thread is spawned with a Runnable that just loops as long as some boolean is true, clicking as fast as you want. When the 'stop' button is pressed (my guess is with the robot still active...), set that boolean to false. This should all be within the EventHandler.actionPerformed method.