I have trouble trying to click on a JButton and constantly updating the Conway's Game of Life. So what I have is firstly to give the rules from the Game of Life and simulate and calculate the position for the counters. Then update the frame by setting the background colour to the JButton, and then delay and repeat. But the problem is when I press the start button, it gets stuck due to the fact I was trying to use while loop.
There is a separate package called the AI_Processor which is just the simulation and calculation which is all done correctly, just the updating got some problems.
Code Parts:
public void updateFrame() {
AI.AI_Movement_Update();
addColour();
}
public void addColour() {
for (int i = 0; i < fieldHeight; i++) {
for (int j = 0; j < fieldWidth; j++) {
if (AI.getPosB(j, i) == true) {
testMolecules[i][j].setBackground(Color.green);
} else {
testMolecules[i][j].setBackground(Color.black);
}
}
}
}
Timer tm = new Timer(1000,this);
if (ae.getSource() == start) {
while(true) {
updateFrame();
tm.start();
}
}
You state:
But the problem is when I press the start button, it got stucked due to the fact i was trying to use while loop.
Then get rid of the while (true) loop since all this does is tie up the Swing event thread rendering your GUI useless. You have a Swing Timer, and you could call the model's update method in the timer's ActionListener so that it is called with each tick of the timer, and then you would not need the while loop. Other options include keeping the while (true) loop, but calling it in a background thread, but if you do this, take care to update your GUI on the Swing event thread only.
...Sorry for the formatting though...
I have formatted your code for you, but for future reference you will want to read the help section of this site regarding how to format questions and containing code. Also have a look here.
Other random thoughts:
Regarding Timer tm = new Timer(1000,this);, I try to avoid having my GUI classes implement listener interfaces as it forces the class to do too much, breaking the Single Responsibility Principle. Better to use either a separate listener class, a Control class that assigns listeners, or an anonymous inner class.
For more information on Swing threading issues, please see Lesson: Concurrency in Swing
For more on anonymous inner classes, again, get rid of the while (true) bit and instead try something like:
// note that TIMER_DELAY is a constant, and needs to be smaller than 1000, perhaps 20?
Timer tm = new Timer(TIMER_DELAY, new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
updateFrame();
}
});
// the start call below can only be called inside of a method or a constructor
tm.start();
EDIT:
sorry, previous solution was bad :-(
EDIT:
you could use anonymous inner class for this
see http://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html
see http://docs.oracle.com/javase/7/docs/api/javax/swing/Timer.html
If you use Timer, then you should pass an instance of ActionListener
Timer creates a new Thread, so while is not neccessary...
Not tested:
public void updateFrame(){
AI.AI_Movement_Update();
addColour();
}
public void addColour() {
for (int i = 0; i < fieldHeight; i++) {
for (int j = 0; j < fieldWidth; j++) {
if (AI.getPosB(j, i) == true) {
testMolecules[i][j].setBackground(Color.green);
} else {
testMolecules[i][j].setBackground(Color.black);
}
}
}
}
if(ae.getSource() == start)
new Timer(1000,new ActionListener(){
updateFrame();
}).start();
Related
I am using a Thread to do some calculations related to the app that need to be done simultaneously but this Thread causes the FPS to drop (logically) and I wanted to know how to resolve the issue as the Thread is not doing any heavy calculations at all. Here is the code where I implement the Thread and the Thread itself.
incrementMass = new IncrementMass();
incrementMass.start();
// I added some extra functionality to the InputProcessor but I assume that is irrelevant
if(InputProcessor.isButtonUp() && InputProcessor.getButtonLetGo() == RIGHT && !isJustPressed) {
isJustPressed = true;
try {
incrementMass.join();
} catch(InterruptedException e) {
e.printStackTrace();
}
planets.add(new Planet(10, m, mouseOnWorld2.x, mouseOnWorld2.y));
} else if(Gdx.input.isButtonJustPressed(RIGHT)) {
isJustPressed = false;
incrementMass.restart();
}
The Thread:
/**
* Thread to increment the mass in a while loop.
*/
public class IncrementMass extends Thread {
/**
* Increments the mass when started.
*/
#Override
public void run() {
super.run();
while(Gdx.input.isButtonPressed(Input.Buttons.RIGHT)) {
MainScreen.m += 100;
System.out.println(MainScreen.m);
}
}
/**
* Resets the mass so the whole thing starts over (for the creation of a new planet)
*/
public void restart() {
MainScreen.m = 100000;
}
}
All this is called in the render() function of my Screen by the way.
I have one idea as to what is causing this: Every frame I create a new Thread which is not optimal but everything else I tried failed to actually perform my calculations correctly. It definitely solves the FPS problem to have the initiation of the Thread and the ´start()´ function in the constructor of my Screen but that for some reason messes with the incrementing of the mass and makes it a fixed value: the value I reset it to in ´restart()´
I've been trying to solve this but I'm baffled so here I am.
As said in the comment, there was no function for isButtonJustUp() which made it not be able to run sequentially. Therefore I made a Thread so that it was sequential which is not a good implementation of Threads. I've come up with a solution:
if(Gdx.input.isButtonPressed(RIGHT)) {
m += 100;
} else if(InputProcessor.isButtonJustUp() && InputProcessor.getButtonLetGo() == RIGHT) {
planets.add(new Planet(10, m, mouseOnWorld2.x, mouseOnWorld2.y));
m=0;
}
I haven't made isButtonJustUp() yet but it is the best way rather than implementing an unnecessary Thread.
Context
I have an activity where a user clicks hit or miss for three turns and then it is the computers turn, to click hit or miss.
The problem
The problem is when it is the computers turn the program hangs the recycler view and makes multiple clicking sounds before eventually displaying the a result. You can see this here:
What I've tried
Through some troubleshooting I seen that I should preform the computers task in a background thread and any UI changes should be preformed in the runOnUiThread. So I have this (this is the onClickEvent):
AsyncTask.execute(new Runnable() {
#Override
public void run() {
playerHit(whosThrowing);
runOnUiThread(new Runnable() {
#Override
public void run() {
setAdapterToDisplayStats(whosThrowing);
tv_numberToHit.setText(numToHit.get(whosThrowing));
if(amountThrownThisRound == 3 && (!opponent.equals("SINGLE"))) {
nextPlayerTurn(whosThrowing);
}
}
});
}
});
break;
case R.id.btn_miss:
AsyncTask.execute(new Runnable() {
#Override
public void run() {
playerMissed(whosThrowing);
runOnUiThread(new Runnable() {
#Override
public void run() {
setAdapterToDisplayStats(whosThrowing);
//Player threw their three darts. Change player.
if(amountThrownThisRound == 3 && (!opponent.equals("SINGLE"))) {
nextPlayerTurn(whosThrowing);
}
}
});
}
});
break;
However this didn't solve the problem and the GIF above actually shows the result of this code. I designed this first for a Human v Human scenario and then started implementing a Human V Computer scenario. I thought this would have been straight forward get a result for the computer either hit or miss and perfomClick() on the relevant button.
As you can see in the above code block, the majority of the methods are in the runOnUiThread this is because these methods contain at least one bit of code that changes the UI but parts that doesn't mess with the UI. I can't imagine I have to go through and wrap each line of code in a run in background or run in UI thread, surely? Just when it is the computers turn.
The setAdapterToDisplayStats(whosThrowing); calls onBindViewHolder in a separate class which updates the recylerview. Does having just setAdapterToDisplayStats(whosThrowing) ensure that anything after it even if it is in a separate class runsOnTheUi thread?
Code that may be having an effect
Computers throw
if(opponent.equals("COMPUTER") && confirmedPlayers.get(0).equals("A.I") && Integer.parseInt(amountThrown.get(0)) % 3 == 0){
btn_hit.setEnabled(false);
btn_miss.setEnabled(false);
for (int i = 0; i < 3; i++) {
takeCompThrow();
}
btn_hit.setEnabled(true);
btn_miss.setEnabled(true);
}
return whosThrowing;
Computers performClick
The preformClick was what I had originally wrapped in a runnable block, thinking anything I called after it would be done in the background thread. Although this is where I first ran into the problem and moved it to the onClick() as I read the UI should be done separately but still no joy.
private void takeCompThrow() {
boolean hitOrMiss = computerThrow.computerHitOrMiss(compLevel);
if(hitOrMiss) {
btn_hit.performClick();
} else {
btn_miss.performClick();
Here's how it preforms Human V Human in case that's important:
The main question
Where am I going wrong?
In my opinion the problem is related with this code block, because you are trying to update recyclerview for multiple times in a really short period.
for (int i = 0; i < 3; i++) {
takeCompThrow();
}
Since you are the owner of the calculation logic, you can directly play turns and update the result directly without performing clicks. In this regard, there is no need to use any async task, unless there is a huge calculation cost.
My first suggestion, you can do sth like this:
if this is computer turn
for 1..3
calculate result for the computer turn
update recyclerview
There is a second option, but I'd go definitely with the above . Even though it will be a quick solution but won't be the best. You just need to put delay between computer clicks in order to let Recyclerview to calculate and redraw elements. So it will be like with the existing code :
for (int i = 0; i < 3; i++) {
takeCompThrow();
someDelay();
}
I have this code sample
public static class BlinkMe extends Thread {
int counter = 0;
protected boolean stop = true;
public void run() {
while (stop) {
counter++;
if (counter % 2 == 0) {
jLabel4.setVisible(true);
jLabel7.setVisible(true);
jLabel8.setVisible(true);
counter = 0;
} else {
jLabel4.setVisible(false);
jLabel7.setVisible(false);
jLabel8.setVisible(false);
if (jButton4.isEnabled() == false) {
stop = false;
jLabel4.setVisible(true);
jLabel7.setVisible(true);
jLabel8.setVisible(true);
if (jButton2.isEnabled() == false) {
stop = true;
jButton2.setEnabled(false);
}
}
}
}
}
}
I need to stop this Thread when I press my Stop Button...
Here's the code I'm using for the Button's function but it is not working. ***The Thread is not working at ll*
Here is the Button's function
private void jButton4ActionPerformed(java.awt.event.ActionEvent evt) {
BlinkMe b=new BlinkMe();
b.stop(); //here I have even used b.interrupt(); but doesn't stop the
}
There are many, many things wrong in this code.
you're accessing Swing components from a background thread. That's forbidden. Only the event dispatch thread is allowed to access Swing components
You're trying to stop() a thread, although this method is deprecaed and should never, ever be used, as the documentation explains
Instead of stopping the actual thread, you create a new instance of that thread class, and call stop() on that new instance.
You "blink" without any delay between the blink.
Your thread uses a stop variable, but this variable is never modified anywhere. Even if it was, it's not volatile, so you have a big chance of not seeing the modification, and thus not stopping the thread.
Read the Swing tutorial abount concurrency. And use a Swing Timer, which is designed to do that kind of thing, safely.
You are creating a new thread in actionPerformed and trying to stop the same, which was not started so far. Try calling stop in actual thread.
The initial value of your stop is "true". This means that when the thread starts, the run method executes but will not execute the while block because the condition will result to false right away.
First, you need to change your while loop into like this:
while(!stop) { /* the rest of your code */ }
Next, you need to create a method in your BlinkMe thread that would allow other objects in your program that would make it stop. The method would look something like this:
public void stopBlinking() {
stop = true;
}
Calling the above method will stop the infinite loop in the run method.
I don't think you will see a blinking effect when you run your program. It is because the loop executes very fast. I suggest you put a Thread.sleep(1000) somewhere in the loop so that there is time to reflect the blink effect visually.
I am using eclipse if it would make any difference. I am trying to update a label 10 times at the press of a button, and I want it to wait between updates. I am trying to use thread.sleep in a for loop, but it does not update the label until the for loop reaches an end.
The code is close to. It also has much more code in it to specify what to change the label to.
for (int i = 0; i < 10; i++) {
try{
thread.sleep(250);
}catch(InterruptedException ie) {
return;
}
panel.repaint();
}
Thanks, it really helped!
For the label to be updated, the main GUI event loop has to get its turn. But I'm guessing your code is running in the main thread, so the redrawing can't occur until your code is completely finished.
What you need to do is put your sleeping loop into a separate thread.
For this task, the SwingWorker class might be useful.
Swing has a single thread (commonly called the Swing thread) and all button presses, redraws, processing, updates, etc happen in that thread.
This means that if you block that thread (such as for example by sleeping in a loop) then it cannot redraw the screen until you finish.
You need to farm out the work to another thread such as by using SwingWorker or user a Timer to schedule the updates. Swing has a Timer class you can use that is designed for exactly this case, just tell it to call you back every 250ms and make the change in that callback.
May be i am not getting your exact problem otherwise below is the solution:
for (int i = 0; i < 10; i++) {
try{
panel.repaint();
thread.sleep(250);
// Or here if you want to wait for 250ms before first update
}catch(InterruptedException ie) {
return;
}
}
Thoguh SwingWorker is better option. Move above logic to SwingWorker thread. Sample code is below:
class Task extends SwingWorker<Void, Void> {
#Override
public Void doInBackground() {
for (int i = 0; i < 10; i++) {
try{
panel.repaint();
thread.sleep(250);
// Or here if you want to wait for 250ms before first update
}catch(InterruptedException ie) {
}
}
return null;
}
/*
* Executed in event dispatching thread
*/
#Override
public void done() {
// Do something if you want at the end of all updates like turn off the wait cursor
}
}
I am trying to use progress bar in my swing code. It works well but i am enable to reset to zero after the execution is finished. Here is my code logic to get user inputs and call respective methods.
final JProgressBar progressBar = new JProgressBar();
btnRun = new JButton("Run");
btnRun.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
btnRun.setEnabled(false);
if (textStartDate.getText().length() == 0 ||textEndDate.getText().length() == 0 ||textField.getText().length() == 0
|| textField_1.getText().length() == 0) {
JOptionPane.showMessageDialog(frame,"Please Enter all fields");
}
// else if(minDate.compareTo(maxDate)>0 ){
// JOptionPane.showMessageDialog(frame,"Starting Date should be lesser then end Date");
// }
else{
ArrayList<String> ss = list.listFiles(fSource,
textStartDate.getText(), textEndDate.getText());
for (String string : ss) {
i++;
progressBar.setMaximum(i);
System.out.println(i);
progressBar.setValue(i);
System.out.println(textField.getText().replace('\\', '/'));
list.writeToFolder(ftarget, string);
}
btnRun.setEnabled(true);
}
}
});
Set values to 0 like next:
progressBar.setValue(0);
progressBar.setMaximum(0);
progressBar.setString("");
The main problem you're having is your running a loop within the context of the Event Dispatching Thread, which will prevent it from process, amongst other things, paint requests.
This means the the progress bar won't actually update to until you exit the actionPerformed method.
There are a number of possible solutions, but the easiest would be to use a SwingWorker, which will allow you to run the loop in a background thread, but has the ability to provide both progress updates as well as re-sync updates back to the EDT.
Take a look at Concurrency in Swing for more details
For example...
java swingworker thread to update main Gui
Progress Bar Java
JProgressBar isn't progressing
I would, also, focus on maintaining the maximum value as a static value, for example...
progressBar.setValue(0);
progressBar.setMaximum(100);
//... Within the SwingWorker...
ArrayList<String> ss = list.listFiles(fSource,
textStartDate.getText(), textEndDate.getText());
for (String string : ss) {
i++;
int progress = (int)(((float)i / (float)ss.size()) * 100f);
setProgress(progress);
//...
}
For example. It will make the progress bar actually progress, otherwise it will always appear to be 100% (because i is both the value and the maximum).
This will automatically re-seed the progress bar the next time you create a new instance of the SwingWorker and execute it...