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.
Related
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.
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();
I am trying to make a program that displays 3 MessageDialog boxes at a time. I thought that if you put JOPtionPane.showMessageDialog in an actionListner class for a swing timer, it would show a new MessageDialog box every second.
So here is the code that I came up with:
package pracatice;
import java.awt.event.*;
import javax.swing.*;
public class practice extends JFrame
{
public static int num = 0;
public static TimerClass tc = new TimerClass();
public static Timer timer = new Timer(1000, tc);
public JPanel panel = new JPanel();
public JButton btn = new JButton("press");
public practice()
{
setSize(100,100);
setTitle("Test");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setPanel();
setVisible(true);
}
public void setPanel()
{
btn.addActionListener(new listener());
panel.add(btn);
add(panel);
}
public class listener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
num = 0;
System.out.println("starting timer");
timer.start();
}
}
public static class TimerClass implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
System.out.println("Adding 1 to num");
num++;
JOptionPane.showMessageDialog(null,"Test");
if(num == 3)
{
System.out.println("stopping the timer");
timer.stop();
}
}
}
public static void main(String[] args)
{
practice p = new practice();
System.out.println("created an instance of practice");
}
}
It works, but not the way I want it to. Instead of showing a new box every second it shows a new one 1 second after you press ok on the previous one.
So when I press "press" it waits 1 second and spawns the box. When I press "ok", it waits 1 second and spawns another one and so on. Any idea how to make the 3 boxes spawn 1 after another?
When using the showX methods of JOptionPane you are creating modal (blocking and one at a time) dialogues as stated by the documentation.
You can Use the JOptionPane directly by creating it manually instead of using the showX methods.
create a new one manually and set it to not be modal:
optionPane = new JOptionPane(...);
dialog = optionPane.createDialog(null, "the title");
dialog.setModal(false);
dialog.show();
The methods to create the dialogs (JOptionPane.show...) do not return until the user has somehow closed the dialogs. Given Swing is single threaded, no other Swing process can happen until this occurs. If you wish to have three dialogs open at once, use a non-modal dialog.
Please help, I'm trying to use javax.swing.Timer in my program. I have looked at lots of examples, but it still doesn't make sense to me. I am writing a program that has the user guessing the price. What I can't seem to figure out is how to have a timer that counts down from 30 seconds after the "new Game" button is clicked. If the user has not guessed the correct answer, then I want the game to display "You lose", but I also want the timer to stop if they get the correct answer in under 30 seconds and display that time. I believe I'm suppose to use
timer = new Timer(Speed, this);
timer.start();
timer.end();
but, I'm not sure what else I need for the timer or where to place these within my code. Any help would be much appreciated. Below is the code for my program...
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.Random;
public class ClockGame extends JFrame {
//Declare fields for GUI components
private JTextField guessField;
private JButton newGameButton;
private JLabel messageLabel;
private JLabel guessLabel;
private ImageIcon clockImage;
private int countTotal;
private Random rand;
private JLabel title;
private int number;
private Timer timer;
public ClockGame() {
//Build GUI
super ("Clock Game");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Set layout
this.setLayout(new BorderLayout());
//Create the main panel
JPanel mainPanel = new JPanel();
//Create components and place them in the panel
rand = new Random();
guessLabel = new JLabel("Guess: ");
guessField = new JTextField(20);
messageLabel = new JLabel(" Click New Game to Begin");
clockImage = new ImageIcon("clock.jpg");
newGameButton = new JButton("New Game");
title = new JLabel("The Clock Game", clockImage, SwingConstants.CENTER);
//Set font for clockGameLabel
title.setFont(new Font("Calibri", Font.BOLD, 24));
//Set messageLabel Color
messageLabel.setOpaque(true);
messageLabel.setBackground(Color.yellow);
newGameButton.addActionListener(new ButtonListener());
guessField.addActionListener(new AnswerListener());
//Add components to the panel
mainPanel.add(guessLabel);
mainPanel.add(guessField);
mainPanel.add(newGameButton);
this.add(title, BorderLayout.NORTH);
this.add(messageLabel, BorderLayout.SOUTH);
//Add the panel to this JFrame
this.add(mainPanel, BorderLayout.CENTER);
//Sizes this JFrame so that it is just big enough to hold the components
this.setSize(340,225);
//Make the JFrame visible on the screen
this.setVisible(true);
}
private class AnswerListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
//Code to check to see if answer is correct
int sum = number;
int answer = Integer.parseInt(guessField.getText());
Color purple = new Color(153, 153, 253);
countTotal++;
if (sum < answer)
{
messageLabel.setText("Too High");
messageLabel.setBackground(Color.red);
}
else if (sum > answer)
{
messageLabel.setText("Too Low");
messageLabel.setBackground(purple);
}
else
{
messageLabel.setText("Correct! It took you " + countTotal + " tries, in " +
timer + " seconds");
messageLabel.setBackground(Color.yellow);
}
}
}
private class ButtonListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
number = rand.nextInt(1001);
messageLabel.setText(" The price is between $1 and $1000, begin.");
messageLabel.setBackground(Color.green);
countTotal = 0;
}
}
public static void main(String[] args) {
ClockGame frame = new ClockGame();
}
}
timer.end();
There is no end() method. First you need to read the API for the appropriate method to use.
counts down from 30 seconds after the "new Game" button is clicked.
So in the ActionListener you add to the button you need to start the Timer and schedule it to fire every second. When the Timer fires you decrement your count by 1.
I also want the timer to stop if they get the correct answer
So when they get the correct answer you stop the Timer. So in your code where you update the text of the label you stop the Timer.
If the user has not guessed the correct answer, then I want the game to display "You lose",
So when the timer count reaches 0, you 1) stop the timer and 2) display the message.
In the constructor of you class you would actually create the Timer, so that the above methods in your class have a reference to the Timer so it can be started and stopped as required.
What you really need to do is forget about your game and learn how to use a Timer. So you create a frame with a label and two buttons. The label will display the initial count of 30. Then you have a "Start" button that decrements the label by 1 each time the Timer fires. Then you have a "Stop" button that stops the Timer so the count is not decremented.
Once you understand the basic concept of starting and stopping the Timer, then you add the code to your real program.
Well to begin... you would need a JLabel that is assigned to print "You lose" and another one that prints the Time that it took the player to answer the question. Add these to your Frame however you want.
JLabel outcome = new JLabel(); //setText to win or lose.
JLabel countdown = new JLabel(); //setTime as Timer counts down.
After you have instantiated these Labels. The Timer needs to be instantiated.
Timer timer = new Timer(1000, new ActionListener() { //Change parameters to your needs.
int count = 30;
public void actionPerformed(ActionEvent e) {
count--;
if(count == 0) //They lose
{
countdown.setText("CountDown: " + count);
outcome.setText("You Lose");
timer.stop(); //ends the countdown.
}
else if(userGotAnswer) // You need to create a boolean value that changes when a user gets the answer right.
{
countdown.setText("CountDown: " + count);
outcome.setText("You win");
timer.stop(); //ends the countdown
}
else
{
countdown.setText("CountDown: " + count); //default
}
}
});
Then call
timer.start();
when you want the timer to start.
I've made a program that sets a button's setEnable from time to time. The Thread.sleep() is in another class. Here's the code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Try extends JFrame implements ActionListener{
JButton n1 = new JButton("1");
JButton n2 = new JButton("2");
JButton n3 = new JButton("3");
JButton show = new JButton("Show");
{
show.addActionListener(this);
n1.setEnabled(false);
n2.setEnabled(false);
n3.setEnabled(false);
}
public Try(){
super("Try");
setVisible(true);
setSize(500, 200);
setLayout(new GridLayout(1, 4));
add(n1);
add(n2);
add(n3);
add(show);
}
public void actionPerformed(ActionEvent a) {
Object clicked = a.getSource();
if(show == clicked){
new EasyLevel1().start();
}
}
class EasyLevel1 extends Thread {
public void run() {
try {
n1.setEnabled(true);
Thread.sleep(1000);
n1.setEnabled(false);
n2.setEnabled(true);
Thread.sleep(1000);
n2.setEnabled(false);
n3.setEnabled(true);
Thread.sleep(1000);
n3.setEnabled(false);
} catch (InterruptedException e){
}
}
}
public static void main(String[] args){
Try frame = new Try();
frame.setVisible(true);
}
}
However, when I put it on my actionListener within the class:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Try extends JFrame implements ActionListener{
JButton n1 = new JButton("1");
JButton n2 = new JButton("2");
JButton n3 = new JButton("3");
JButton show = new JButton("Show");
{
show.addActionListener(this);
n1.setEnabled(false);
n2.setEnabled(false);
n3.setEnabled(false);
}
public Try(){
super("Try");
setVisible(true);
setSize(500, 200);
setLayout(new GridLayout(1, 4));
add(n1);
add(n2);
add(n3);
add(show);
}
public void actionPerformed(ActionEvent a) {
Object clicked = a.getSource();
if(show == clicked){
try {n1.setEnabled(true);
Thread.sleep(1000);
n1.setEnabled(false);
n2.setEnabled(true);
Thread.sleep(1000);
n2.setEnabled(false);
n3.setEnabled(true);
Thread.sleep(1000);
n3.setEnabled(false);
} catch (InterruptedException e){}
}
}
public static void main(String[] args){
Try frame = new Try();
frame.setVisible(true);
}
}
It freezes my whole program, based from that example I've understood that a thread sleep should be ran in another thread to stop the current class from freezing. But I expected the new thread.sleep with still freeze its operations like it will still do the code above but the buttons will be responsive since its in another thread. But surprisingly it did what I wanted it to do, It didn't instantly set everything to disabled like the first program.
Thread.sleep() makes the current thread pause. You are running it in an actionPerformed, that is in a Swing Event. All Swing operations are done in a single thread, the EDT. When you pause it with a Thread.sleep(), Swing can not handle any other event because you haven't returned from the actionPerformed listener. Therefore, the GUI freezes (not the complete application, just the GUI).
In general, it is bad practice to do long-running actions in a Swing event because of this. For what you are trying to do, the good alternative is to use Swing timers.
What is happening is that in the 2nd example Thread.sleep is blocking the EDT so no further UI updates occur. In contrast, in the first example you are calling sleep in a separare Thread so no "freezing" occurs. For tasks like this the use of Swing Timers is preferred.
Thread.sleep will cause the thread that executes the call to sleep for the specified time (or until the thread is interrupted). When you call it in the actionPerformed method, it causes the UI thread to sleep. That's why your program is locking up.
You should start a separate thread that will step through the various calls you want to make while sleeping in between. Alternatively (and much better, in my opinion) you could use Swing timers to do what you want.