I am trying to build a simple GUI clock with multithreading.My purpose is making two identical exampl clock window.
public class JavaApplication9 {
public static void main(String[] args) {
JFrame clock = new TextClockWindow();
clock.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
clock.setVisible(true);
}//end main}
class TextClockWindow extends JFrame {
private JTextField timeField; // set by timer listener
private JButton listener;
public TextClockWindow() {
// GUI
timeField = new JTextField(6);
timeField.setFont(new Font("sansserif", Font.PLAIN, 48));
JButton button1 = new JButton("Action");
add(button1);
button1.addActionListener((ActionListener) listener);
ActionListener listener=new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){setBackground(Color.red );
}
};
Container content = this.getContentPane();
content.setLayout(new FlowLayout());
content.add(timeField);
this.setTitle("My_simple_clock"); this.pack();
// Create a 1-second timer and action listener for it.
// Specify package because there are two Timer classes
javax.swing.Timer t = new javax.swing.Timer(1000,
new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Calendar now = Calendar.getInstance();
int h = now.get(Calendar.HOUR_OF_DAY);
int m = now.get(Calendar.MINUTE);
int s = now.get(Calendar.SECOND);
timeField.setText("" + h + ":" + m + ":" + s);
}
});
t.start();
}
This is code without multithreading.But wheni trying to use Runnable some error occured.
In method main Non Static variable cannot be referenced in a static context.
My code with multithreading:
public class MyClock{
public static void main(String[] args) {
Runnable r=new Clocks();
Thread n=new Thread(r);
n.start();
}
public class Clocks implements Runnable {
public Clocks() {}
public void run() {JFrame clock = new TextClockWindow();
clock.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
clock.setVisible(true);
}
please help find the reason why it is not work.runnable is writing correctly....
Is there a reason why you declared Clocks class as inner class ?
Move your Clocks class outside MyClock class and remove the public qualifier if you're declaring the class in the same file. It will start working.
Related
I have been trying to create a 'catch me if you can' game: when I start it, it randomly chooses where to allocate a 'click me' button. I am not supposed to be able to click the button, the text should be re-assigned to another button before I am able to do that.
It works for a while but then it throws the following error: "java.awt.AWTEventMulticaster.mouseMoved".
I have been trying to fix the problem with removeListener() method but I don't seem to be able to find a solution. Any comments?
Here's my code:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.lang.*;
public class Game extends JFrame {
//Panels
private JPanel mainPanel = new JPanel();
// Buttons
private JButton[] buttons = new JButton[9];
private JButton theChosenButton = new JButton();
// other
private int random = 0;
public Game() {
this.setTitle("Catch me if you can");
mainPanel.setLayout(new GridLayout(3, 3));
// creates buttons
for(int i = 0; i < 9 ; i++) {
buttons[i] = new JButton();
mainPanel.add(buttons[i]);
}
// Add everything to frame
this.getContentPane().add(mainPanel);
this.setSize(400, 400);
this.setVisible(true);
}
// generates random number between 1 and 9 to be used
public int clickMeGenerator(){
random = (int) Math.floor(Math.random() * 9);
return random;
}
// randomly assigns clickMeGenerator to a button
// add mouseMoved listener to the chosen button
public void assign(){
int randomButton = this.clickMeGenerator();
theChosenButton = buttons[randomButton];
theChosenButton.addMouseMotionListener(new MouseHover());
theChosenButton.setText("Click me");
}
public void removeListener() {
theChosenButton.removeMouseMotionListener(new MouseHover());
//}
}
// inner class
class MouseHover implements MouseMotionListener {
public void mouseMoved(MouseEvent e) {
theChosenButton.setText("");
Game.this.assign();
}
public void mouseDragged(MouseEvent e) {
}
}
} // end of class
Test class:
public class GameTest {
public static void main (String args[]) {
Game myGame = new Game();
myGame.assign();
}
}
Thank you so much for your help!
Just for clarity, the "actual" error is ...
Exception in thread "AWT-EventQueue-0" java.lang.StackOverflowError
at java.desktop/java.awt.AWTEventMulticaster.mouseMoved(AWTEventMulticaster.java:337)
at java.desktop/java.awt.AWTEventMulticaster.mouseMoved(AWTEventMulticaster.java:337)
So looking through the code...
public void assign() {
int randomButton = this.clickMeGenerator();
theChosenButton = buttons[randomButton];
theChosenButton.addMouseMotionListener(new MouseHover());
theChosenButton.setText("Click me");
}
You are repeatedly add a new MouseMotionListener to you buttons, over and over again, and...
public void removeListener() {
theChosenButton.removeMouseMotionListener(new MouseHover());
//}
}
is pointless, as you're trying to remove a new instance of MouseHover from the button, but it will never have been applied in the first place.
The first thing I would do is create an instance of MouseHover as an instance field in Game
private MouseHover mouseHover = new MouseHover();
and use it when calling addMouseMotionListener and removeMouseMotionListener.
I would then, remove the listener from the "currently" active button before adding it to the next one.
Personally, I would do this in the assign method
public void assign() {
int randomButton = this.clickMeGenerator();
if (theChosenButton != null) {
theChosenButton.removeMouseMotionListener(mouseHover);
}
theChosenButton = buttons[randomButton];
theChosenButton.addMouseMotionListener(mouseHover);
theChosenButton.setText("Click me");
}
I would also ensure that assign is called from within the Event Dispatching Thread when the class is first created, as the UI has been realised by the end of the constructor of Game, meaning the first call to assign is outside of the context of the EDT, which is not recommended.
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Game myGame = new Game();
myGame.assign();
}
});
}
I have a class called Gui. This is where I place all my labels and buttons.
It also contains a button.addactionlistener.
When the button is pressed it starts another thread(stopwatch).
This is when stopwatch enters a loop which keeps updating the ms,sec,min in a while loop.
Stopwatch is another class file. Stopwatch contains the ms,sec,min.
How do I update the gui label with the stopwatch ms,sec,min?
public class Gui {
JFrame swFrame = new JFrame("Stopwatch");
Stopwatch sw = new Stopwatch();
Thread t1 = new Thread(sw);
private JPanel p;
private JButton b1;
private JButton b2;
private JButton b3;
private JLabel l1;
private JLabel l2;
public Gui()
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
swFrame.setSize(500,400);
swFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
p = new JPanel();
b1 = new JButton("StartStop");
b2 = new JButton("LapTime");
b3 = new JButton("Reset");
l1 = new JLabel("bla");
l2 = new JLabel("blala");
p.add(b1);
p.add(b2);
p.add(b3);
p.add(l1);
p.add(l2);
swFrame.add(p);
b1.setActionCommand("StartStop");
b2.setActionCommand("LapTime");
b3.setActionCommand("Reset");
b1.addActionListener(new ButtonClickListener());
b2.addActionListener(new ButtonClickListener());
b3.addActionListener(new ButtonClickListener());
}
});
}
private class ButtonClickListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
String command = e.getActionCommand();
if( command.equals( "StartStop" ))
{
if(t1.isAlive())
{
t1.interrupt();
}
else
{
t1.start();
!!!//How to update the jlabel from the moment t1.starts?!!!!
}
}
else if( command.equals( "LapTime" ) )
{
l2.setText("Submit Button clicked.");
}
else if(command.equals("Reset"))
{
}
}
}
class stopwatch
public class Stopwatch implements Runnable
{
private int min;
private int sec;
private long ms;
Timer timerSW = new Timer();
JLabel l1;
public void run()
{
ms = System.currentTimeMillis();
while(!Thread.currentThread().isInterrupted())
{
int seconds = (int) (ms / 1000) % 60 ;
int minutes = (int) ((ms / (1000*60)) % 60);
}
}
I also have a program class which contains a main method. This calls the Gui.
public class Program {
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
Gui gui = new Gui();
gui.swFrame.setVisible(true);
}
});
}
}
How do I update the gui label with the stopwatch ms,sec,min?
Carefully, Swing is not thread safe and you should not modify it's state outside of the context of the Event Dispatching Thread.
One approach would be to use an Observer Pattern, where by your timer triggers updates to which the UI can respond.
A simpler solution might to use a Swing Timer over a Thread, because a Swing Timer is executes its notifications within the context of the EDT
Consider having a look at Concurrency in Swing and How to use Swing Timers for more details
So I'm trying to rotate a line every second using a Timer and a paint method. However, I'm not quite sure whats going on. Here are some of the relevant methods:
public static ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent e) {
Clock cl = new Clock();
seconds++;
cl.repaint();
}
};
public void paint(Graphics g){
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
for(int c = 0; c<10; c++){
g2.setPaint(Color.BLACK);
g2.drawOval(90-c/2,90-c/2,500+c,500+c); //thick outlined circle
}
g2.setPaint(Color.WHITE);
g2.fillOval(90,90,501,501);
g2.setPaint(Color.BLACK);
g2.rotate(Math.toRadians(seconds*6));
g2.drawLine(340,340,340,90);
}
The line remains stationary. However if I add
System.out.println("tick");
to my actionPerformed method, the command line spits out "tick" 3 times a second. Any ideas as to why these things are happening?
Some context:
public static int seconds = 0;
public static int minutes = 0;
public static int hours = 0;
public static Clock cl = new Clock();
private ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("tick");
seconds++;
cl.repaint();
}
};
public static Timer timer = new Timer(1000,taskPerformer);
public static void main(String[] args){
Clock cl = new Clock();
init();
SwingUtilities.invokeLater(new Runnable(){
public void run() {
createAndShowGUI();
}
});
}
public static void init(){
timer.start();
}
public Clock() {
super("Clock");
timer.addActionListener(taskPerformer);
}
You are creating a new clock at every tick:
public void actionPerformed(ActionEvent e) {
Clock cl = new Clock();
...
Instead you should use an existing instance.
// A field in the class:
Clock cl = new Clock();
...
// removed static so that it can access cl
private ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent e) {
seconds++;
cl.repaint();
}
};
You could also make the clock a field in the action listener, if you do not need to access it elsewhere.
Also note that you generally should not be overriding paint(), but should override paintComponent() instead. More about custom painting in swing here.
Edit:
Now that there's more code available, it's possible to say that if you make the clock and action listener static it should work. However, you need to start the timer after the relevant components are ready:
public static void main(String[] args){
// Removed spurious clock here
SwingUtilities.invokeLater(new Runnable(){
public void run() {
createAndShowGUI();
// Start the timer once the components are ready
init();
}
});
}
The above mentioned point about not creating a clock in the action listener still stands.
I have hit another wall. After getting my key input working, I have been racking my brains for hours, i want to create a pause function, so that if the same key is pressed again the timertask stops running (i.e the game is paused)
JPanel component = (JPanel)frame.getContentPane();
component.getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "space");
component.getActionMap().put("space", (new AbstractAction(){
public void actionPerformed(ActionEvent e){
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask(){
public void run(){
grid.stepGame();
}
},250, 250);
}}));
}
The problem is i cant use a global boolean isRunning var and switch it each time the key is pressed because the timerTask method in a nested class (so the boolean isRunning would have to be declared final to be accessed...). Any ideas on how to detect if the key is pressed again or if the game is already running so i can pause/cancel my timerTask.
Many Thanks Sam
Since this is a Swing game, you should be using a javax.swing.Timer or Swing Timer and not a java.util.Timer. By using a Swing Timer, you guarantee that the code being called intermittently is called on the EDT, a key issue for Swing apps, and it also has a stop method that pauses the Timer. You can also give your anonymous AbstractAction class a private boolean field to check if the key is being pressed for the first time or not.
Also, kudos and 1+ for using Key Bindings instead of a KeyListener.
e.g.,
JPanel component = (JPanel) frame.getContentPane();
component.getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "space");
component.getActionMap().put("space", (new AbstractAction() {
private boolean firstPress = true;
private int timerDelay = 250;
private javax.swing.Timer keyTimer = new javax.swing.Timer(timerDelay , new ActionListener() {
// Swing Timer's actionPerformed
public void actionPerformed(ActionEvent e) {
grid.stepGame();
}
});
// key binding AbstractAction's actionPerformed
public void actionPerformed(ActionEvent e) {
if (firstPress) {
keyTimer.start();
} else {
keyTimer.stop();
}
firstPress = !firstPress;
}
}));
Another useful option is to perform a repeating task on key press and stop it on key release, and this can be done easily by getting the keystrokes for on press and on release:
KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, true) // for key release
KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, false) // for key press
For example:
import java.awt.event.*;
import javax.swing.*;
public class SwingTimerEg2 {
private JFrame frame;
private Grid2 grid = new Grid2(this);
private JTextArea textarea = new JTextArea(20, 20);
private int stepCount = 0;
public SwingTimerEg2() {
frame = new JFrame();
textarea.setEditable(false);
frame.add(new JScrollPane(textarea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
setUpKeyBinding();
}
void setUpKeyBinding() {
final int timerDelay = 250;
final Timer keyTimer = new Timer(timerDelay, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
grid.stepGame();
}
});
JPanel component = (JPanel) frame.getContentPane();
final int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
final String spaceDown = "space down";
final String spaceUp = "space up";
component.getInputMap(condition).put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, false), spaceDown);
component.getActionMap().put(spaceDown, (new AbstractAction() {
public void actionPerformed(ActionEvent e) {
keyTimer.start();
}
}));
component.getInputMap(condition).put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, true), spaceUp);
component.getActionMap().put(spaceUp, (new AbstractAction() {
public void actionPerformed(ActionEvent e) {
keyTimer.stop();
}
}));
}
public void doSomething() {
textarea.append(String.format("Zap %d!!!%n", stepCount));
stepCount ++;
}
private static void createAndShowGui() {
new SwingTimerEg2();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class Grid2 {
private SwingTimerEg2 stEg;
public Grid2(SwingTimerEg2 stEg) {
this.stEg = stEg;
}
void stepGame() {
stEg.doSomething();
}
}
Easiest and dirty solution:
final boolean[] isRunning = new boolean[1];
You don't want to do that—but it works assuming proper synchronization around.
What would be better is
final AtomicBoolean isRunning = new AtomicBoolean();
What would be even better is to review the design once again: global state usually means, "global problems"
The final qualifier requirement can easily be avoided -- replace your inner method (which has the final requirement) with a call to a class method.
No you got the wrong idea about WHY you need final for anonymous classes! Final is only needed for local variables (well more exactly any variable that might have a live time shorter than the given object).
Hence a static variable in a class is perfectly fine and will work perfectly!
Edit: example:
public class Main {
interface Test {
void call();
}
public static volatile boolean running = true;
public static void main(String[] args) {
Test t = new Test() {
#Override
public void call() {
System.out.println(Main.running);
}
};
t.call();
running = false;
t.call();
}
}
Keep a reference to the Timer somewhere, say in your game class.
When the game is paused cancel the Timer.
This will cancel any currently scheduled tasks.
Then when the game is unpaused schedule the timer again as you have done above.
public class Game {
private Timer timer;
public void pause() {
if (timer != null) {
timer.pause();
}
}
public void startOrResumeGame() {
if (timer == null) {
timer = new Timer();
} else {
// Just in case the game was already running.
timer.cancel();
}
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
grid.stepGame();
}
}, 250, 250);
}
}
I've got a "status" JLabel in one class (named Welcome) and the timer in another one (named Timer). Right now, the first one displays the word "status" and the second one should be doing the countdown. The way I would like it to be, but don't know how to - display 10, 9, 8, 7 ... 0 (and go to the next window then). My attempts so far:
// class Welcome
setLayout(new BorderLayout());
JPanel area = new JPanel();
JLabel status = new JLabel("status");
area.setBackground(Color.darkGray);
Font font2 = new Font("SansSerif", Font.BOLD, 25);
status.setFont(font2);
status.setForeground(Color.green);
area.add(status, BorderLayout.EAST); // can I put it in the bottom-right corner?
this.add(area);
and the timer:
public class Timer implements Runnable {
// public void runThread() {
// new Thread(this).start();
// }
public void setText(final String text) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
setText(text); // link to status here I guess
}
});
}
public void run() {
for (int i = 10; i > 0; i--) {
// set the label
final String text = "(" + i + ") seconds left";
setText(text);
// // sleep for 1 second
// try {
// Thread.currentThread();
// Thread.sleep(1000);
// } catch (Exception ex) {
// }
}
// go to the next window
UsedBefore window2 = new UsedBefore();
window2.setVisible(true);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
// runThread();
}
} // end class
I agree that you should consider using a "Java" Timer as per Anh Pham, but in actuality, there are several Timer classes available, and for your purposes a Swing Timer not a java.util.Timer as suggested by Anh would suit your purposes best.
As for your problem, it's really nothing more than a simple problem of references. Give the class with the label a public method, say setCountDownLabelText(String text), and then call that method from the class that holds the timer. You'll need to have a reference of the GUI class with the timer JLabel in the other class.
For example:
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Welcome extends JPanel {
private static final String INTRO = "intro";
private static final String USED_BEFORE = "used before";
private CardLayout cardLayout = new CardLayout();
private JLabel countDownLabel = new JLabel("", SwingConstants.CENTER);
public Welcome() {
JPanel introSouthPanel = new JPanel();
introSouthPanel.add(new JLabel("Status:"));
introSouthPanel.add(countDownLabel);
JPanel introPanel = new JPanel();
introPanel.setPreferredSize(new Dimension(400, 300));
introPanel.setLayout(new BorderLayout());
introPanel.add(new JLabel("WELCOME", SwingConstants.CENTER), BorderLayout.CENTER);
introPanel.add(introSouthPanel, BorderLayout.SOUTH);
JPanel usedBeforePanel = new JPanel(new BorderLayout());
usedBeforePanel.setBackground(Color.pink);
usedBeforePanel.add(new JLabel("Used Before", SwingConstants.CENTER));
setLayout(cardLayout);
add(introPanel, INTRO);
add(usedBeforePanel, USED_BEFORE);
new HurdlerTimer(this).start();
}
private static void createAndShowUI() {
JFrame frame = new JFrame("Welcome");
frame.getContentPane().add(new Welcome());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
public void setCountDownLabelText(String text) {
countDownLabel.setText(text);
}
public void showNextPanel() {
cardLayout.next(this);
}
}
class HurdlerTimer {
private static final int TIMER_PERIOD = 1000;
protected static final int MAX_COUNT = 10;
private Welcome welcome; // holds a reference to the Welcome class
private int count;
public HurdlerTimer(Welcome welcome) {
this.welcome = welcome; // initializes the reference to the Welcome class.
String text = "(" + (MAX_COUNT - count) + ") seconds left";
welcome.setCountDownLabelText(text);
}
public void start() {
new Timer(TIMER_PERIOD, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (count < MAX_COUNT) {
count++;
String text = "(" + (MAX_COUNT - count) + ") seconds left";
welcome.setCountDownLabelText(text); // uses the reference to Welcome
} else {
((Timer) e.getSource()).stop();
welcome.showNextPanel();
}
}
}).start();
}
}
Since you're using Swing you should use the javax.swing.Timer, not the java.util.Timer. You can set the timer to fire at 1 second (1000 ms) intervals and have your listener do the updating. Since Swing updates must take place in the event dispatch thread your listener is the perfect place for status.setText.
there's already a Timer class in java: http://www.exampledepot.com/egs/java.util/ScheduleRepeat.html
Why not put the setText method in the welcome class and just do 'status.setText(text)'?
And you might try BorderLayout.SOUTH or .PAGE END or .LINE END to get the timer in the lower right corner