I'm creating a board game using a GUI and JFrames/JPanels where you can play against the computer. I have a method called showPieces() which updates board GUI by changing the image icons on an array of buttons (which are laid out in a grid format). Once the icons have been updated the revalidate() and repaint() methods to update the GUI.
The showPieces() method has a parameter that needs to be passed to it every time it is called.
The main issue I'm having is I want the human to make a move, update the GUI, wait 1 second, the computer makes it's move and then loop until someone wins.
My basic code is the following:
do{
human.makeMove();
gui.showPieces(data);
try {
Thread.sleep(1000);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
computer.makeMove()
gui.showPieces(data);
}while(playing);
This cause the issue where when the human player makes their move, the GUI will freeze for one second and then after the delay, both moves are made at the same time.
I hope it makes sense, but I'm a novice with Java and may have to look more into threading as I don't understand it well enough.
Thread.sleep() is done on the Event Dispatch Thread which will lock the GUI.
So If you need to wait for a specific amount of time, don't sleep in the event dispatch thread. Instead, use a timer.
int delay = 1000; //milliseconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//...Perform a task...
}
};
new Timer(delay, taskPerformer).start();
As with most all similar Swing questions, you're putting your entire Swing GUI to sleep by calling Thread.sleep(...) on the GUI's event thread (the EDT or Event Dispatch Thread), and when during this period the GUI will not be able to update its images or interact with the user whatsoever. The solution here is not to use Thread.sleep(...) but rather to use a Swing Timer to cause your 1 second delay.
Swing Timer Tutorial.
Related
So, I have an object that extends JPanel and displays dots in a matrix via paintComponent. The dots of the matrix can move, disappear or multiply given certain conditions, and I want to show their evolution over time automatically like so:
for(int i = 0; i < 100; ++i){
matrix = calculateNextMatrix(); //Calculate possible movements, deaths or births of dots
myGraphic.updateMatrix(matrix); //Pass new dots to the JPanel object
myGraphic.repaint(); //Draw new dots
Thread.sleep(100); //Wait 0.1 seconds for next iteration (yes, this should be in a
//try-catch)
}
However, I only get drawn the last iteration after the loop is finished, and all the previous calls to repaint() are basically ignored. If I do the iterations only one at a time (for example, via a manual button press), I have no problem.
Is there any way to get multiple, periodic repaint calls automatically?
I had a simile problem with JComponent in my library and I found a solution with swing timer, I reported the java description of timer
In general, we recommend using Swing timers rather than general-purpose timers for GUI-related tasks because Swing timers all share the same, pre-existing timer thread and the GUI-related task automatically executes on the event-dispatch thread. However, you might use a general-purpose timer if you don't plan on touching the GUI from the timer, or need to perform lengthy processing.
You can use Swing timers in two ways:
To perform a task once, after a delay.
For example, the tool tip manager uses Swing timers to determine when to show a tool tip and when to hide it.
To perform a task repeatedly.
For example, you might perform animation or update a component that displays progress toward a goal.
I think you are in one of this cases.
Without a minimal example reproducible, I can use the my code.
You should create the Swing action listener, like this:
public class UpdateComponentListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
count += 10;
timeLabel.setText(count + "");
//The label call repaint
//in your app you should be call the repaint
//In your cases
/*
matrix = calculateNextMatrix(); //Calculate possible movements, deaths or births of dots
myGraphic.updateMatrix(matrix); //Pass new dots to the JPanel object
myGraphic.repaint();
*/
}
}
The timer constructor get in input the delay and the action listener, so you can build your timer, with this code:
Timer timer = new Timer(1000, new UpdateComponentListener());
timer.start();
You can stop, restart your timer, so you should be set how propriety the timer.
The GUI example:
I wrote the post and after I see the #camickr comment. I post the answer because my work is finished but, the comment answered your question.
I hope to have to build a food example
I'm creating a board game using a GUI and JFrames/JPanels where you can play against the computer. I have a method called showPieces() which updates board GUI by changing the image icons on an array of buttons (which are laid out in a grid format). Once the icons have been updated the revalidate() and repaint() methods to update the GUI.
The showPieces() method has a parameter that needs to be passed to it every time it is called.
The main issue I'm having is I want the human to make a move, update the GUI, wait 1 second, the computer makes it's move and then loop until someone wins.
My basic code is the following:
do{
human.makeMove();
gui.showPieces(data);
try {
Thread.sleep(1000);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
computer.makeMove()
gui.showPieces(data);
}while(playing);
This cause the issue where when the human player makes their move, the GUI will freeze for one second and then after the delay, both moves are made at the same time.
I hope it makes sense, but I'm a novice with Java and may have to look more into threading as I don't understand it well enough.
Thread.sleep() is done on the Event Dispatch Thread which will lock the GUI.
So If you need to wait for a specific amount of time, don't sleep in the event dispatch thread. Instead, use a timer.
int delay = 1000; //milliseconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//...Perform a task...
}
};
new Timer(delay, taskPerformer).start();
As with most all similar Swing questions, you're putting your entire Swing GUI to sleep by calling Thread.sleep(...) on the GUI's event thread (the EDT or Event Dispatch Thread), and when during this period the GUI will not be able to update its images or interact with the user whatsoever. The solution here is not to use Thread.sleep(...) but rather to use a Swing Timer to cause your 1 second delay.
Swing Timer Tutorial.
I'm trying to code a simple game in Java. The basic structure is a single JFrame with different JPanels that I add/remove at different times. At startup, there is a JPanel that's a basic menu (start game, high scores, etc). Once the "Start" button is pressed it switches to a level selector panel with three buttons to select the difficult level of the game. Once any of the three buttons is pressed, it switches to another panel that will displays a three second countdown, then the actual game. All three buttons call the same method, just with a different difficulty value passed in.
I have all the separate pieces working fine, but I'm having troubles with the transition from the level selection panel to the countdown. If I don't use threads the screen freezes on button press and does not switch to the new panel. I've tried messing around with threads, but I don't know that much about them and have only had limited success (I've got it so it will successfully switch some of the time, but not consistently).
In terms of code, in the level selection panel I have something like this listening for button clicks:
private class ButtonClickedListener implements ActionListener {
public void actionPerformed(ActionEvent evt) {
gui.newLevel(1);
}
}
where in place of just gui.newLevel(1) I've messed around with starting new threads and calling the method from them.
The newLevel() method look like:
getContentPane().removeAll();
levelPanel = new LevelPanel(levelNum, this);
add(levelPanel);
validate();
levelPanel.start();
I use very similar code when switching from the start menu JPanel to the level selector panel (again, with an ActionListener on the buttons), which works just fine.
LevelPanel's start() method initializes values for the new JPanel and displays the countdown on screen (currently with the following code, although I messed with putting something like this in the newLevel() method instead) before displaying the actual game:
try {
Thread.sleep(1000);
//update countdown number
validate();
repaint();
Thread.sleep(1000);
//update countdown number
validate();
repaint();
Thread.sleep(1000);
//update countdown number
validate();
repaint();
} catch (Exception e) {
System.out.println(e);
}
//start game
I would really appreciate any help getting this code to work, and I'm pretty sure some sort of threading is the way to go but I'm not quite sure where/how. Any suggestions and/or code samples would be great!
Thanks in advance!
EDIT: I ended up rewriting the countdown using a timer instead of Thread.sleep(), which fixed part of the problem and the rest of it I eventually figured out and was entirely unrelated to GUI stuff, which is why I didn't think to check it in the first place.
never really never use Thread.sleep(1000); during EDT, this code caused freeze on GUI is un_resposible, untill a new event invoke EDT or mouse hover over can alive this container too
1) there are two ways how to dealy any event(s) in the Swing GUI, by implements
Swing Timer
delaying by using Thread.sleep(1000); in the SwingWorker
The layout and painting must be done in EDT. Use SwingUtilities.invokeAndWait to call the validate() and repaint()
You can start some code with a time delay using TimerTask:
Timer timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
invokeLater(); // This starts after [delay] ms
// and - if given - will run every [period] ms.
}
}, delay, period);
You could solve your problem with this, though it won't be a pretty solution.
// edit: (see comments) you should synchronize accesses to the gui properly, else it will give you errors.
I have tried a lot, but can't seem to get it to work.
I was told to use EDT with the following example.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Modify the GUI here
}
});
I have read on this topic a lot and still don't understand. I get what a thread is, but the .invokeLater still makes no sense to me. Honestly if you can explain in detail this it would be a big help!
Goal of Program: To get the randomly generated key that is constantly created every second to update itself afterward in the GUI.
So there is an EDT (Event Dispatch Thread). All actions that appear on your screen are executed by the EDT. There is only one EDT per Swing application.
You are in some arbitrary thread and you want to update the GUI through that thread? Well like I said there is only one EDT for each swing application, so you have to tell that EDT to display the label (or whatever context you want).
The idea here, is you push this Runnable onto a queue that the EDT pulls from. Eventually, your runnable will be processed by the EDT when all other actions before it are completed.
I recommend you get the book Filthy Rich Clients. There's a chapter where they explain Swing's threading model to great detail.
Basically in Swing, any code that modifies the GUI should be executed in the Event Dispatcher Thread. The SwingUtilities class that you are using there provides you with an easy way to post events to the event queue that is then dispatched by the EDT. That's what the invokeLater method does, it takes a new Runnable() as argument which is ultimately executed on the EDT.
From the book:
The invokeLater() implementation takes
care of creating and queuing a special
event that contains the Runnable. This
event is processed on the EDT in the
order it was received, just like any
other event. When its time comes, it
is dispatched by running the
Runnable’s run() method.
This is a pretty common element of all GUI programming. You have one thread that handles drawing the GUI, getting input, and running callbacks. If another thread tries to change the GUI related objects, it will conflict with the GUI thread. Say, for example, it was half way through drawing something and you change the color from a different thread.
All invokeLater does is queue up something for the GUI thread to run. By "later" it's really runs almost instantly but the current thread doesn't have to wait for it. The GUI thread may be doing a draw or waiting for a callback to return which would delay executing the code you gave it.
Needs to be a member so we can change it and still use it from an inner class
protected long secret=0;
... this needs to be in your code somewhere it'll get run...
JFrame f = new JFrame("foo");
new Thread(){
public void run() {
for(;;){
try {
sleep(1000);
} catch Interrupted (Exception ix){
return;
}
// TODO update your secret key here
// please don't use random()
SwingUtilities.invokeLater(new Runnable() {
public void run() {
f.setTitle("secret "+x);
}
});
}
}
}).start();
....
Only ever update Swing from the EDT so that it paints properly.
When you are in the EDT ( running code in an event handler) you can call paintImmediately() if you really must.
If you're all looking to do is update the UI on a known schedule, try something like the following. This assumes that a JFrame is the component you wish to update every 1 second.
private static final int WAIT_LENGTH = 1000; // 1 second
private JFrame frame = new JFrame();
// Thread will update the UI (via updateUI() call) about every 1 second
class UIUpdater extends Thread {
#Override
void run() {
while (true) {
try {
// Update variables here
}
catch (Exception e) {
System.out.println("Error: " + e);
}
finally {
frame.repaint();
Thread.sleep(WAIT_LENGTH);
}
}
}
}
To start this thread:
UIUpdater t = new UIUpdater();
t.start();
I've built a form with Netbeans's visual editor. When I press one of the buttons it should do the following :
set it to disabled
perform a task that takes some time
when the task finishes the button will be enabled again
However, the following happens:
the button remains in a pressed state until the task finishes
when the task finishes, the enabling/disabling of buttons will be very fast (they will happen, but you won't notice them)
This behaviour is not something I want. I tried using repaint on the JButton, on the JFrame and even on the JPanel containing the button, but I can't seem to get it to do what I want. Some hints?
When you do work in a button callback, you are stalling the GUI painting thread until it completes.
What you need to do is spawn a thread to do the long running task, and then have that thread use SwingUtilities.invokeLater() to update the UI when it completes. Not using invokeLater is not thread safe, and is generally bad mojo.
A basic example is:
button.setEnabled(false);
new Thread(new Runnable() {
public void run() {
// Do heavy lifting here
SwingUtilies.invokeLater(new Runnable() {
public void run() {
button.setEnabled(true);
}
});
}
}).start();
When you do things in a button callback, you are essentially stalling the gui painting thread - not just for the button, but for ANY gui painting. (Try covering the interface with another window and then exposing it again - it won't repaint until the task is finished!)
What you need to do is spawn a thread to do the long running task, and then have that thread use SwingUtilities.invokeLater() to do the enabling of the button. invokeLater forces the button enable to happen in the gui painting thread.
You may want to set a busy cursor or otherwise lock the interface while the long-running thread is operating.
The Concurrency in Swing tutorial from Sun is well worth a read. Excellent explanation and background reading, including the event dispatching thread, using worker threads, etc
You need to do the task that takes some time in a different thread.
The reason the button is blocking is because the work is being done in the same thread that draws the button. Once the work is done the button can do the rest of what you tell it to.
If you use a different thread the thread will go do the task while the drawing code can continue drawing the form.