JButton ActionListener not starting TimerTask - java

I am making a bot for a game that I play, and have made a GUI so the user of the bot has some control over when the bot is active or not. To do this, I made a start/stop button that would set a variable to true. Each attack is a different class, and they extend the TimerTask class. All they have as an implementation of the run() method that TimerTask has, and they just do
Robot robot = new Robot();
robot.keyPress(KeyEvent.VK_1); //the ".VK_1" part will be different per class
robot.keyRelease(KeyEvent.VK_1);
Ideally, the user would press the button, and TimerTasks would run, simulating keypresses that would be attacks in the game. However, the text on the button does not change, and the TimerTasks never run. Does anyone have a solution for this? My code is below. Thanks in advance!
Main-Class:
package AQWGrindBot;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Timer;
public class Main {
private static boolean playing = false;
static Timer timer = new Timer();
public static void main(String[] args) {
int speed = 1000;
final boolean[] startVar = {false};
JFrame frame = new JFrame("AQW Attack Bot");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400,100);
frame.setResizable(false);
frame.setExtendedState(Frame.MAXIMIZED_BOTH);
JButton start = new JButton("START");
start.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (start.isSelected()) {
playing = !playing;
if (start.getText().equals("START")) {
start.setText("STOP");
toggleBot(true);
} else {
start.setText("START");
toggleBot(false);
}
}
}
});
frame.setLayout(new FlowLayout());
frame.add(start);
frame.setVisible(true);
}
private static void toggleBot(boolean check) {
if (check) {
timer.schedule(new AQWBotATK1(), 0, 1000);
timer.schedule(new AQWBotATK3(), 0, 3000);
timer.schedule(new AQWBotATK4(), 0, 17000);
} else {
timer.cancel();
}
}
}
P.S I am on macOS Mojave. I do not know if this is important, but there are apparently some JFrame features that do not work properly on Mac. Please tell me if I am trying to implement one of them. Also, I am using java.awt and the Robot object to simulate key presses.

You have two main problems, the first is here:
if (start.isSelected()) {
Since start is a JButton, isSelected() will never be true, and if you want this type of functionality, you should be using a JToggleButton or a JCheckBox or JRadioButton (the latter two which extend from JToggleButton) and which change the selected state on press.
So,
JToggleButton start = new JToggleButton("START");
Another option is to create your own boolean field to toggle and to test on button press, but then you don't get a visual feed back from the button as to its state. You could also I suppose use the state of the button's text, obtained via getText()
Your other problem is your use of a java.util.Timer and java.util.TimerTask as this creates code that is potentially not Swing thread-safe. It's almost always better to use javax.swing.Timer or a "Swing Timer" when creating timers for Swing GUI's since the code within the timer's ActionListener is guaranteed to be called on the Swing event thread.

Related

How can I successfully display a "splash" screen (JFrame/JPanel) that gives the user a message before closing after a certain delay?

I have tried thread.sleep which ends up lagging the appearance of the JLabel and then immediately closing the program, and I have tried implementing the solutions I've found to "similar" questions that involved Timers.
Is there any straightforward way to do this within this actionPerformed to display this JFrame for a few seconds, and then System.exit(0)?
My partner and I (this is for a school project we have kind of gone off the rails with) have both been "googling" excessively and cannot understand the variable context solutions we have found. We are both working with 3 weeks of Java programming experience.
Thank you in advance, I apologize for my lack of expertise in applying the possible solutions from analogous questions.
private class ExitButton implements ActionListener {
public void actionPerformed(ActionEvent e) {
JFrame exitMenu = new JFrame();
JPanel message = new JPanel();
JLabel punk = new JLabel("You can keep all that corporate BS");
punk.setFont(new Font("Comic Sans MS", Font.ITALIC, 16));
exitMenu.setSize(300, 150);
exitMenu.setLocationRelativeTo(null);
message.add(punk);
message.setVisible(true);
exitMenu.add(message);
exitMenu.setVisible(true);
// some kind of timer/delay for like... 3-5 seconds
System.exit(0);
}
I tried using:
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class TimedExit {
Timer timer = new Timer();
TimerTask exitApp = new TimerTask() {
public void run() {
System.exit(0);
}
};
public TimedExit() {
timer.schedule(exitApp, new Date(System.currentTimeMillis()+5*1000));
}
}
... from another question/answer. But it tells me I need to make a method (which this does?) after I call TimedExit().
# Swing Timer, again, I am having a hard time implementing what I am finding online which is tailored to general cases (and end up using a confusing amount of jargon that assumes the user understands the relevance of the placeholders they represent - however, I do not understand). As lazy as this may make me, I was hoping someone could tell me some code that would fit within this particular section of my code and would simply allow this JFrame display the asinine sentence and then System.exit(0).
If it truly is something too complicated to answer or do concisely, I apologize for bringing it up, and thank all who have attempted to assist me.
int delay = 3000; //milliseconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
System.exit(0);
}
};
new javax.swing.Timer(delay, taskPerformer).start();
The above code submitted by Jayfray solved my problem!
Try something like this:
int delay = 3000; //milliseconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
System.exit(0);
}
};
new Timer(delay, taskPerformer).start();
after your call to exitMenu.setVisible(true);

How To Make a Method Using Swing, Sleep Using a Timer

I've recently made a small puzzle game that deals with clicking certain areas. I also made a solver which activates the necessary areas to win. The problem that I am facing is that I would like to pause each time it activates an area to create a sort of "solving animation". My problem is represented here
package experiment;
import java.awt.Dimension;
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.JPanel;
public class ExperimentHere extends JFrame implements ActionListener
{
private static final long serialVersionUID = 1L;
private JButton changeLabelButton;
private JPanel mainPanel;
private JLabel labelToChange;
public ExperimentHere() {
changeLabelButton = new JButton("Set the label");
changeLabelButton.addActionListener(this);
mainPanel = new JPanel();
labelToChange = new JLabel();
labelToChange.setText("This needs to be changed");
mainPanel.add(labelToChange);
mainPanel.add(changeLabelButton);
this.add(mainPanel);
setTitle("Timer Program");
setContentPane(mainPanel);
setPreferredSize(new Dimension(1000, 1000));
pack();
}
public void actionPerformed(ActionEvent e) {
if (e.getSource().equals(changeLabelButton)){
changeLabel();
}
}
public void changeLabel(){
for (int i = 0; i<5; i++){
labelToChange.setText(""+i);
// Pause for 200ms here
}
}
public static void main(String[] args){
ExperimentHere test = new ExperimentHere();
test.setVisible(true);
}
}
I have tried using Timers, but I'm not sure how to format it properly so that it only pauses each time the loop inside of changeLabel() is incremented, because the second paramter in Timer() asks for an ActionListener.
I've also tried using Thread.sleep() but it just freezes my program and then instantly solves it.
Ideally the changeLabel method would increment by 1, set the label to the new String, wait for 200ms, and then increment again.
I have tried using Timers, but I'm not sure how to format it properly so that it only pauses each time the loop inside of changeLabel() is incremented
When you use a Timer you don't use a loop. The point of a Timer is that you start the Timer and it keeps executing until you stop the Timer.
You also don't make methods, you make an Action to invoke whenever the Timer fires.
So you need an instance variable in your class that keeps track of the number of times the Timer has fired (lets call it "timerCounter"). Then you need to create an Action to invoke every time the Timer is fired.
So you create a couple of instance variables:
int timerCounter = 0;
Action action;
Then in the constructor of your class you create an Action something like:
action = new AbstractAction()
{
#Override
public void actionPerformed(ActionEvent e)
{
labelToChange.setText("" + timerCounter);
timerCounter++;
if (timerCounter > 5)
{
Timer timer = (Timer)e.getSource();
timer.stop();
}
}
}
So now in the ActionListenerof your button you can do something like:
timerCounter = 0;
Timer timer = new Timer(200, action);
timer.start();

java sending the frame to front of screen

Is there a way to send the java frame in front of every other opened program. I know you can use
JFrame.setAlwaysOnTop(true);
but that just keeps it in front allways. I want it to only happen when a certain function is called. For instance, When I press a button on a frame, it will wait using Thread.sleep(10000) for ten seconds, but the I want it to just the frame to the front in case you clicked out of the window for a second. Any suggestions?
Take a look at Window#toFront
You may also want to take a look at
WindowListener
Swing Timer
Be careful of using Thread.sleep in a GUI environment, if used incorrectly, this will cause you window to stop updating (painting)
This is surprisingly fiddly.
The exact behavior might also depend on the operating system. But at least on Windows, a call to frame.toFront() will not necessarily bring the window to the front. Instead, it will cause the corresponding entry in the task bar to blink for a few seconds. I tried something like
f.setAlwaysOnTop(true);
f.setAlwaysOnTop(false);
which basically works, but after the window was brought to the front, it is not "active", and none of my attempts to make it active worked (e.g. requesting the focus or so).
The only solution that I found now to (reliably) work (on Windows, at least) was
if (!f.isActive())
{
f.setState(JFrame.ICONIFIED);
f.setState(JFrame.NORMAL);
}
But wonder wheter there is a more elegant solution.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class FrameToTopTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
final JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("Bring me to top after 3 seconds");
button.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
triggerBringToFront(f, 3000);
}
});
f.getContentPane().add(button);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static void triggerBringToFront(final JFrame f, final int delayMS)
{
Timer timer = new Timer(delayMS, new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
// This will only cause the task bar entry
// for this frame to blink
//f.toFront();
// This will bring the window to the front,
// but not make it the "active" one
//f.setAlwaysOnTop(true);
//f.setAlwaysOnTop(false);
if (!f.isActive())
{
f.setState(JFrame.ICONIFIED);
f.setState(JFrame.NORMAL);
}
}
});
timer.setRepeats(false);
timer.start();
}
}

Java running main method of other class, when JButton is pressed

I am trying to develop a JFrame which has two buttons that would let me to call the main method of other classes. The first try was to put it directly into the actionPerformed of each button, this will cause the JFrame of the other class to open but showing only the title of it and not showing any contents of the JPanel additionally freezing the program (can't even press close button, have to go into task manager or eclipse to kill it). The second try was adding a method call in actionPerformed, and adding the method will this time call the main method of other class however the same result (freeze of program).
For testing purposes I have placed the call to main method of other class, straight in this class main method which has proven to me that the frame of other class has successfully appeared, including all its JPanel contents, functionality etc.
I know I could make some kind of infinite loop in my main method to wait until a boolean is set to true, but then I though there must be some less-expensive way to get it working. So here I am asking this question to you guys.
Here is the code of the 2nd try;
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.JPanel;
public class Chat {
public static void main (String[] args) {
JFrame window = new JFrame("Chat Selection");
//Set the default operation when user closes the window (frame)
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Set the size of the window
window.setSize(600, 400);
//Do not allow resizing of the window
window.setResizable(false);
//Set the position of the window to be in middle of the screen when program is started
window.setLocationRelativeTo(null);
//Call the setUpWindow method for setting up all the components needed in the window
window = setUpWindow(window);
//Set the window to be visible
window.setVisible(true);
}
private static JFrame setUpWindow(JFrame window) {
//Create an instance of the JPanel object
JPanel panel = new JPanel();
//Set the panel's layout manager to null
panel.setLayout(null);
//Set the bounds of the window
panel.setBounds(0, 0, 600, 400);
JButton client = new JButton("Run Client");
JButton server = new JButton("Run Server");
JLabel author = new JLabel("By xxx");
client.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//run client main
runClient();
}
});
server.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//run server main
}
});
panel.add(client);
client.setBounds(10,20,250,200);
panel.add(server);
server.setBounds(270,20,250,200);
panel.add(author);
author.setBounds(230, 350, 200, 25);
window.add(panel);
return window;
}
private static void runClient() {
String[] args1={"10"};
ClientMain.main(args1);
}
}
Only one main method is allowed per application. Honestly I am not sure what you are trying to do or think is supposed to happen when you call main on other classes. When you call main on other classes all you are doing is calling a method that happens to be called main and passing args to it. Your freezing is probably because you are not using Swing correctly:
http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
The problem you're having is that Java Swing is single threaded. When you're running the main function of the other class, however you do it, the GUI won't be able to keep running until it returns. Try spawning off a new thread that calls the second main method.
private static void runClient() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
String[] args1={"10"};
ClientMain.main(args1);
}
});
}
EDIT: Updated, as per #Radiodef's suggestion. Missed at the top when you said this second class had to display things on the GUI. Definitely want to go with the invokeLater then.

How do I create buttons that reset and pause a timer?

How do I make the two buttons that are displayed reset / pause the timer? The timer works but I want to change the code for the buttons so that they will change the timer instead of outputting to the console. Thank you.
CODE:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class createWindow extends JFrame implements ActionListener
{
public static void main(String[] args)
{
new createWindow();
}//end main
createWindow()
{
super("Frame");
setSize(400,70);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new FlowLayout());
show();
final JLabel time = new JLabel("Timer");
JButton reset = new JButton("Reset timer");
JButton pause = new JButton("Pause timer");
reset.setActionCommand("resetClicked");
pause.setActionCommand("pauseClicked");
reset.addActionListener(this);
pause.addActionListener(this);
add(pause);
add(time);
add(reset);
long start = System.currentTimeMillis();
while (true)
{
long timer = System.currentTimeMillis() - start;
final int seconds = (int) (timer / 1000);
String display = Integer.toString(seconds);
time.setText(display);
}//end while loop
}//end constructor
#Override
public void actionPerformed(ActionEvent e)
{
String buttonClicked = e.getActionCommand();
if(buttonClicked.equals("resetClicked"))
{
System.out.println("The reset button was clicked"); //Change to reset timer
}
else if(buttonClicked.equals("pauseClicked"))
{
System.out.println("The pause button was clicked"); //Change to pause timer
}
}//end listener
}
Don't use an infinite while loop. This blocks the EDT. Instead use a Swing Timer. This will give you control to start and stop the Timer.
Stopwatch Example
Side Notes:
Don't use JFrame.show as that method is deprecated. Use JFrame.setVisible instead. Also make this call when all components have been added to the frame.
The functionality for the JButtons is sufficiently different to warrant using separate ActionListener instances for each button.
The preferred approach is to use a JFrame instance directly rather then extending it.
Class names in Java begin with uppercase so createWindow would become CreateWindow.

Categories

Resources