Continue once the thread returns true - java

I am having trouble getting my code to 'pause' until the thread is finished. My program is simply a JProgressBar inside a frame. I am using a loop located inside a thread, which loops every 10 milliseconds and adds +1 to the progress bar. The end result will make the bar look animated as its value increases from 0 to 100.
Now, the problem is getting the code to 'wait' until the progress bar hits value 100. As you can see in the code, I would like to have buttons re-enabled only when the progress bar reaches value 100. As of right now, the buttons seem to get re-enabled simultaneously as the progress bar is increasing.
disableButtons();
// loop and animate the progress bar
for (int i = 0; i <= 100; i++) {
Game.setProgBar_loading(i);
// wait a number of milliseconds
try {
Thread.sleep(10);
} catch (InterruptedException e) {}
}
enableButtons();

You could nest Loading class in Program and then access Program's variables from there, and then pretty much copy this part of the code: // <TODO> re-enable all buttons into the end of the loop. Something like this:
class Program {
// assuming you have these buttons:
private JButton button1;
private JButton button2;
private JButton button3;
// snip
public Program() {
// assuming there's initialization code for the above buttons somewhere...
// button1 = new ...
// button2 = new ...
// button3 = new ...
// frame initializing
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setBounds(100, 100, 890, 480);
getContentPane().setLayout(null);
setVisible(true);
// create the loading bar
loadingBar = new JProgressBar();
loadingBar.setBounds(10, 11, 864, 23);
getContentPane().add(loadingBar);
// <TODO> disable all buttons
button1.setEnabled(false);
button2.setEnabled(false);
button3.setEnabled(false);
// animate the loading bar
Loading thread = new Loading();
new Thread(thread).start();
// do not re-enable the buttons here yet.
}
// snip
public class Loading implements Runnable {
#Override
public void run() {
// loop and increment loading bar's value
for (int i = 0; i <= 100; i++) {
Program.setProgressBar(i);
try {
Thread.sleep(10);
} catch (InterruptedException e) {}
}
// re-enable the buttons here instead
button1.setEnabled(true);
button2.setEnabled(true);
button3.setEnabled(true);
}
}
}
EDIT: To generalize your Loading class and reuse it later you could create an interface that contains methods such as beforeExecute() and afterExecute(). Take that interface as an argument to Loading's constructor and save it as a member so you can later call the interface's methods from within run():
public interface LoadingHandler {
public void beforeExecute();
public void afterExecute();
}
public class Loading implements Runnable {
private LoadingHandler handler;
public Loading(LoadingHandler handler) {
this.handler = handler;
}
#Override
public void run() {
// disableButtons();
// use interface's methods instead:
handler.beforeExecute();
// loop and animate the progress bar
for (int i = 0; i <= 100; i++) {
Game.setProgBar_loading(i);
// wait a number of milliseconds
try {
Thread.sleep(10);
} catch (InterruptedException e) {}
}
// enableButtons();
// use interface's method instead:
handler.afterExecute();
}
}
Then, in your GUI's thread you would do something like this:
Loading thread = new Loading(new LoadingHandler() {
#Override
public void beforeExecute() {
disableButtons();
}
#Override
public void afterExecute() {
enableButtons();
}
});
new Thread(thread).start();

Why not just enable the buttons after the loading bar hits the 100%? Either you do it via callback or just put the regarding code in the setProgressBar method
// set the progress bar's value
public static void setProgressBar(int num) {
loadingBar.setValue(num);
if(num >=100){
// <TODO> re-enable all buttons
}
}

I am not sur to understand why it is needed to use a thread to animate a progress bar if no parrallel actions are done meanwhile.
join method from Thread class is blocking until the thread execution terminates.
...
Thread animatedProgressBarThread = new Thread(thread);
animatedProgressBarThread.start();
// next line blocks until thread terminates its execution
animatedProgressBarThread.join();
The run method of the Loading class uses Catch block to ignore InterruptedException. According to documentation, the thread created won't be interruptable. the catch block should set thread's state as interrupted and terminate.

Related

Leave Event Dispatch Thread entry ONLY on key press (Java)

I understand that it is important to use the Event Dispatch Thread for any changes to the interface in Java. However, I have no idea how I can manipulate these events to stop/continue/start. I want to refrain from moving on to the next line of main() (after the ones which put the Runnable in the EventQueue) until a certain key is pressed.
I put together an example for clarity. What I'd like to do here is spawn the JFrame, allow the user to move the box around with the arrow keys and then press Enter to cease the box-shifting operations, and ONLY then make the calculation at the end of main() and cause the answer to appear. I should be able to get 400, 500, 600, etc. As it is, the calculation is made immediately after the JFrame appears, so the answer is always 300.
I carved out a spot for whatever action should be bound to Enter; it's underneath the declarations for the actions bound to the arrow keys.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class EndTheShifter extends JFrame
{
private Color ourRectColor = new Color(28,222,144);
private int ourRectWidth = 50;
private int ourRectHeight = 50;
protected static Point ourRecLocation = new Point(100,100);
// Rectangle object can paint itself
public class Rectangle
{
protected void paint(Graphics2D g2d)
{
g2d.setColor(ourRectColor);
g2d.fillRect(ourRecLocation.x, ourRecLocation.y, ourRectWidth, ourRectHeight);
}
} // Rectangle class
// OurRectangle can create a Rectangle and call paint() on it
public class OurRectangle extends JPanel
{
private Rectangle capableRectangle;
public OurRectangle()
{
capableRectangle = new Rectangle();
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g.create();
capableRectangle.paint(g2d);
g2d.dispose();
}
} // OurRectangle class
KeyStroke pressRight = KeyStroke.getKeyStroke("RIGHT");
KeyStroke pressLeft = KeyStroke.getKeyStroke("LEFT");
KeyStroke pressUp = KeyStroke.getKeyStroke("UP");
KeyStroke pressDown = KeyStroke.getKeyStroke("DOWN");
KeyStroke pressEnter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0);
OurRectangle recToWorkWith = new OurRectangle();
// Create InputMap and ActionMap
InputMap inputMap = recToWorkWith.getInputMap(JPanel.WHEN_IN_FOCUSED_WINDOW);
ActionMap actionMap = recToWorkWith.getActionMap();
// Mapping Shortcut
protected void setTheAction(KeyStroke a, String b, Action c)
{
inputMap.put(a,b);
actionMap.put(b,c);
}
// Constructor!!!
public EndTheShifter()
{
add(recToWorkWith);
Action rightAction = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
if(ourRecLocation.x != 600)
ourRecLocation.x += 50;
else
ourRecLocation.x = 100;
recToWorkWith.repaint();
}
};
Action leftAction = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
if(ourRecLocation.x != 100)
ourRecLocation.x -= 50;
else
ourRecLocation.x = 600;
recToWorkWith.repaint();
}
};
Action downAction = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
if(ourRecLocation.y != 600)
ourRecLocation.y += 50;
else
ourRecLocation.y = 100;
recToWorkWith.repaint();
}
};
Action upAction = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
if(ourRecLocation.y != 100)
ourRecLocation.y -= 50;
else
ourRecLocation.y = 600;
recToWorkWith.repaint();
}
};
/*
Action enterAction = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
}
}
setTheAction(pressEnter,"enterAction",enterAction);
*/
setTheAction(pressRight,"rightAction",rightAction);
setTheAction(pressLeft,"leftAction",leftAction);
setTheAction(pressDown,"downAction",downAction);
setTheAction(pressUp,"upAction",upAction);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(800,800);
setVisible(true);
}
// Main kicks things off by putting all of the above
// in the Event Dispatch thread
// On an enter press, I want the last line of main() to run
public static void main(String[] argv)
{
EventQueue.invokeLater(
new Runnable()
{
#Override
public void run()
{
new EndTheShifter();
}
});
// What I want to trigger only on Enter
System.out.println(ourRecLocation.x + 2*ourRecLocation.y);
}
} // EndTheShifter, our outermost class
and ONLY then make the calculation at the end of main()
That is not the way a GUI works.
The main() method is only used to display the frame.
Once the frame is visible the EDT is started and the frame sits there waiting for user events to be generated.
Your application code then responds to these user events.
I understand that it is important to use the Event Dispatch Thread for any changes to the interface in Java.
All code invoked in a listener does execute on the EDT. So the code in your Action does execute on the EDT. You don't need to do anything special.
What I want to trigger only on Enter
Then that logic should be contained in the Enter Action.
I would like to support what camickr said; there is likely a better way to achieve what you are trying to do. That said, if you really want to make your main method wait until the enter key is pressed, here's how:
First, at the top of your file, define an object to use as a synchronization lock like so:
public static final Object LOCK = new Object();
Then, in your main method, before your println statement, put the following code:
synchronized (LOCK) {
LOCK.wait();
}
What this does is it waits until the LOCK object's monitor lock is not being used by any thread (very simplified explanation, read more here), and then it makes the current thread (in this case, the thread that started your main method) wait indefinitely.
Next, add a throws declaration to the method header on your main method:
public static void main(String[] argv) throws InterruptedException
This tells the compiler that your code could throw an InterruptedException, which would happen if your thread was interrupted while it was waiting.
Finally, anywhere in your EndTheShifter constructor, put the following code:
synchronized (LOCK) {
LOCK.notify();
}
This again waits until the LOCK object's monitor lock becomes available, and it then "notifies" all threads waiting on the LOCK object that they may continue. In this case, it will make our main thread continue and execute the println.

Changing owner for ReentrantLock

So I wrote a little program which will move circles around and when they collide they will move opposite direction, however when I'm trying to delay the execution so they won't move around stupidly fast I get java.lang.IllegalMonitorStateException
Lock in canvasRender.java, creating an instance:
ReentrantLock renderLock = new ReentrantLock();
Method which will pause execution for a moment, so circles won't move around super fast.
publlic void delay(){
renderLock.unlock();
try { Thread.sleep(10); } catch (Exception e) {} ;
renderLock.lock();
}
then from another class where I create a window and add actionListener
public static void main(String[] args){
//Buttons and other elements
// ...
JButton start = new JButton("Start!");
createAndShowGUI();
}
In createAndShowGUI():
static void createAndShowGUI(){
//adding elements to panels
start.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
start(); //this will set gameIsRunning variable to true and create models
while (gameIsRunning) {
//update(); //which has delay(); at the end of frame drawing
//but even if just put delay()
delay(); //still says exception
start.setEnabled(false); //while game is running button is unavailable
}
start.setEnabled(true);
}
});
}
In this case my lock is owned by the Thread main, but at the time when I click button 'Start!' current is Thread AWT-EventQueue-0, and so the program crashes. How to fix this issue? (or where am I silly?)
The problem is that you're calling renderLock.unlock() from AWT-EventQueue-0 after renderLock.lock() was called by main. The thread AWT-EventQueue-0 isn't allowed to call unlock() it, since it's not the thread that called lock() it in the first place.
You could probably simplify things by dropping ReentrantLock and just using synchronized.
I don't know the design of the rest of your program, but it seems to me that the contents of the while loop belong in a separate thread. You generally don't want to loop in a UI listener method (such as actionPerformed() in ActionListener) because it will freeze up the GUI.
One thing you could do is add an Object to synchronize on:
private static final Object LOCK = new Object()
Then move the game-updating logic to its own thread — something like this:
private static class GameThread extends Thread {
public GameThread() {
super("GameThread");
}
public void run() {
synchronized (LOCK) {
start();
while (gameIsRunning) {
update();
try {
// Try to sleep for 10 millis:
LOCK.wait(10);
} catch (InterruptedException ignored) { }
}
}
// Re-enable the button:
javax.swing.SwingUtilities.invokeLater(() -> start.setEnabled(true));
}
}
And you can change your ActionListener to simply disable the button and start a GameThread:
start.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
start.setEnabled(false);
synchronized(LOCK) {
if(!gameIsRunning) {
new GameThread().start();
}
}
}
});
Any other code that checks or modifies the state of the game should also be enclosed within a synchronized (LOCK) block. And if update() modifies the GUI as well as the game state, then it probably needs to do so with SwingUtilities.invokeLater().
It might also make things more clear to rename start() to setupGame() and JButton start to JButton startButton.

How to pause program execution without blocking a Timer (Java Swing)?

I have a Timer that shows an animation on the screen when a game event takes place. What I want to do is have the main execution pause until this animation finishes, but everything I try seems to block the Timer from running. I've tried using Thread.sleep() and calling wait() and notify() on a lock object, but with the same result. The Timer listener's actionPerformed() is never called.
This code sets up the timer:
protected void showMovingEffect(int steps, Direction dir, AnimatedImageSet imgs) {
effectsPane.runAnimation(imgs, dir, steps);
waiting = true;
while (waiting) {
synchronized(animationWaitLock) {
try {
animationWaitLock.wait();
} catch (InterruptedException e) {}
}
}
}
And the Timer and listener inside the EffectsPane object:
private void runAnimation(AnimatedImageSet ais, Direction dir, int iters) {
System.out.println("run");
imgs = ais;
top = 0;
left = 0;
topStep = dir.getRowIncrement() * 10;
leftStep = dir.getColIncrement() * 10;
iterations = iters;
index = 0;
active = true;
timer.start();
}
public void actionPerformed(ActionEvent e) {
System.out.println(index);
top += topStep;
left += leftStep;
index++;
currentImage = imgs.getImage(index);
repaint();
if (index == iterations) {
active = false;
timer.stop();
synchronized(animationWaitLock) {
animationWaitLock.notify();
}
waiting = false;
}
}
The System.out.println call at the start of the actionPerformed() is never called, so either the wait() call is also pausing the Timer, or something is blocking it. If I comment out the sleep/wait lines in showMovingEffect(), the animation runs, but the program does not pause.
One approach would be to display a modal dialog while the animation proceeds. As discussed here, user interaction will be foreclosed, but background GUI updates in response to the javax.swing.Timer will continue. You can allow the user to dismiss the dialog at any time, as shown here, but you may want to abandon the animation at that point.
no input from the user is needed.
You can block user interaction without displaying a dialog by entering a SecondaryLoop after the animation starts.
SecondaryLoop loop = Toolkit.getDefaultToolkit()
.getSystemEventQueue().createSecondaryLoop();
timer.start();
loop.enter();
When the animation concludes, exit() the SecondaryLoop:
public void actionPerformed(ActionEvent e) {
…
if (index == iterations) {
timer.stop();
loop.exit ();
…
}
}
You can try this:
timer.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// your code
}
});

Infinite loop inside paint() method in an Applet does not let me interact with the buttons displayed

What I am trying to do is an Appled which throws 2 threads, each running a counter which increases itself via an infinite loop
I then use a while(true) in the Applet's paint() method, which continuously paints the counters, the problem is that I have also 2 buttons, each intended to stop each thread, but the infinite loop in the paint() method doesn't let me neither click none of them nor close the Applet's window nor anything
Here a screenshot followed by the code
btw I'm certain the problem is the paint() loop as if I disable the loop I can interact with the buttons, but the counters are obviously not updated, and weird thing is that I put the mouse cursor over the buttons to show it took the form like when you want to resize a windows but the imprpant didn't capture it :/
http://i.imgur.com/PJnDI4u.png
public class MainApplet extends Applet implements ActionListener {
private static final long serialVersionUID = -2500043816999861110L;
private Font fuente;
private Button bUno, bDos;
private HiloContador hUno, hDos;
public void init() {
setBackground(Color.LIGHT_GRAY);
fuente = new Font("Verdana",Font.BOLD,26);
bUno = new Button("Parar");
bUno.addActionListener(this);
bDos = new Button("Parar");
bDos.addActionListener(this);
bUno.setSize(40,20);
add(bUno);
bDos.setSize(40,20);
add(bDos);
hUno = new HiloContador(20);
hUno.start();
hDos = new HiloContador(40);
hDos.start();
}
#SuppressWarnings({ "deprecation", "static-access" })
public void actionPerformed(ActionEvent e) {
if(e.getSource().equals(bUno)){
hUno.parar();
bUno.setLabel("1 parado");
}else if (e.getSource().equals(bDos)){
hDos.parar();
bDos.setLabel("2 parado");
}
}
public void paint(Graphics g) {
while (true){
g.clearRect(1,1,getSize().width,getSize().height); //dibuja la ventana
g.setFont(fuente);
g.drawString(hUno.getContador()+"",40,60);
g.drawString(hDos.getContador()+"",100,60);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
in case it helps anyone, solved deleting the infinite loop and adding this method
Timer timer = new Timer();
timer.schedule( new TimerTask() {
public void run() {
repaint();}
}, 0, 1000);

Rectangle colouring logic

I have one three rectangles in my canvas. I wanted to change the colours of three rectangles
in a slow manner one by one.
For example: When starting the application, user should be able to see three rectangles with the same colour (blue).
After 2 secons that rectangles colour should change to red.
Again after 2 secons the next rectangles colour should get changed.
The last one is also done the same way, that means after 2 seconds of the 2nd rectangle.
I wrote in my own way. But it is not working. All the rectanlges are changed together. I want one by one.
Could anyone give me the logic.
final Runnable timer = new Runnable() {
public void run() {
//list of rectangles size =3; each contain Rectangle.
for(int i = 0 ; i < rectangleList.size();i++){
if(rectangleListt.get(i).getBackgroundColor().equals(ColorConstants.blue)){
try {
rectangleList.get(i).setBackgroundColor(ColorConstants.yellow);
Thread.sleep(1500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//rectSubFigureList.get(i).setBorder(null);
}/*else{
rectSubFigureList.get(i).setBackgroundColor(ColorConstants.blue);
}*/
}
You're likely calling Thread.sleep inside of Swing's event thread or EDT (for event dispatch thread), and this will cause the thread itself to sleep. Since this thread is responsible for all of Swing's graphics and user interactions, this will in effect put your entire application to sleep, and is not what you want to have happen. Instead, read up on and use a Swing Timer for this.
References:
Swing Timer tutorial
Swing Event Dispatch Thread and Swingworker tutorial
To expand on Hidde's code, you could do:
// the timer:
Timer t = new Timer(2000, new ActionListener() {
private int changed = 0; // better to keep this private and in the class
#Override
public void actionPerformed(ActionEvent e) {
if (changed < rectangleList.size()) {
rectangleList.setBackgroundColor(someColor);
} else {
((Timer) e.getSource()).stop();
}
changed++;
}
});
t.start();
You can set a Timer:
// declaration:
static int changed = 0;
// the timer:
Timer t = new Timer(2000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// Change the colour here:
if (changed == 0) {
// change the first one
} else if (changed == 1) {
// change the second one
} else if (changed == 2) {
// change the last one
} else {
((Timer) e.getSource()).stop();
}
changed ++;
}
});
t.start();

Categories