views:

187

answers:

2

Hi!

I have a recursive method that changes the value of a variable in every recursion, then it shows that value on the JPanel, and then I would like to pause (here is my problem) until I click (it pauses in every new recurse). Then when I click this method continues to do the next recursion.

The following code is just the structure of how my real program looks like and how I tried to implement that. I have tried many ways to do that thing using threads and executors but I failed.

I have created 2 classes, the PanelToBeClicked class which is a JPanel and has the recursive method, and a PanelMouseListener class which collects the clicks.

The following code is totally naked from threads and executors. I would be very grateful if someone could add some lines of code here to demonstrate the correct approach, or give me some clues of how I can implement it.

Here is the code:

import java.awt.*;
import javax.swing.*;

public class PanelToBeClicked extends JPanel {
    int counter;

    public PanelToBeClicked() {
  super();
  setPreferredSize(new Dimension(100,100));
  addMouseListener(new PanelMouseListener(this));
}

@Override
protected void paintComponent(Graphics g){
 super.paintComponent(g);
 g.drawString(""  + counter, 10, 10);
}

public void recursiveMethod(){
 counter++;
 repaint();     

 /*
  * Pause/wait until the panel is clicked so you proceed the recursions
  * MISSING CODE HERE. I tried thread.sleep 
  * but I had a monitor exception 
 */

 if (counter <10)recursiveMethod();
}

public static void main(String[] args) {
 final JFrame frame = new JFrame("How can i do that?");
 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 frame.add(new PanelToBeClicked());

 SwingUtilities.invokeLater(new Runnable() {
  @Override
  public void run() {
   frame.pack();
   frame.setVisible(true);
  }
 });
}
}

The listener:

public class PanelMouseListener extends MouseAdapter {
   private PanelToBeClicked panelToBeClicked;

    public PanelMouseListener(PanelToBeClicked panelToBeClicked) {
         this.panelToBeClicked = panelToBeClicked;
    }

    @Override
    public void mouseClicked(MouseEvent e) {

 /*
  *  start the method PanelToBeClicked.recursiveMethod()... 
 */
 panelToBeClicked.recursiveMethod();

 /*
  *  if the method is already running  and it is not paused do nothing. 
  *  ** MISSING CODE
  *  if the method is running and is  paused then  make the method to continue the recursions 
  *   **MISSING CODE  */

    }

}
A: 

Don't forget that your Swing Panel will execute in a separate thread.

You need to wait on a monitor object (e.g. an object shared between the 2 threads), then the click on the panel will send a notification to that object. The recursive method needs to wait for the monitor to be notified by the click action on the panel.

In pseudo-code (exceptions etc. omitted)

Object monitor = new Object();

void recursiveMethod() {
   // do stuff and then wait...
   synchronized (monitor) {
      monitor.wait();
   }
}

void doClick() {
   synchronized (monitor) {
      monitor.notify();
   }
}

See the Java Guarded Objects tutorial for more info.

Brian Agnew
Thnx for you anseer... i have an issue with setting a common object monitor .. i always get a illigalMonitorException....Also what fo you mean by invoke the panel? does it have to do with java reflection?My thinkng was to be able so use the thread.sleep() method in the recursiceMethod (but i get a monitor exception as expecetd... :( ) and then use the thread.notify() at the mouseClicked of the listner class
Pitelk
correnctin .. not htread.sleep().. but thread.wait();
Pitelk
Edited to show pseudo code. Note that to wait/notify you need to synchronise on the objects
Brian Agnew
Thanks for the link and the answer.I have tried what you said and it almost.. worked.I used monitor.wait() in the recursive method and the monitor.notify9) ath the mouseClicked method.. but the programe Halted...not even the close button of the JFrame worked. I though that this musthave happen becuase the main thread was suspended at the monitor.wait() call.... So i putted the recursiveMethod in a new Thread and it worked perfecty... The thing is that in every recurse a new thread is created.. and i have read that is not the correct approach ..So..do you know how to make that code cleaner ?
Pitelk
I would do a thread dump (Ctrl+break) and see what's hanging. The thread dump will tell you what's waiting for what
Brian Agnew
I did a thread.CurrentThread() just before the monitor.wait() and here is the resultThread[AWT-EventQueue-0,6,main]
Pitelk
A: 

Just wanted to give an example for the Java 5+ lock+condition way:

package gui;

import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.GroupLayout.Alignment;

public class RecursiveContinue extends JFrame {
    private static final long serialVersionUID = 7149607943058112216L;
    JLabel value;
    JButton next;
    volatile SwingWorker<Void, Void> worker;
    Lock lock = new ReentrantLock();
    Condition cond = lock.newCondition();
    boolean continueFlag;
    public RecursiveContinue() {
        super("Recursive Continue Example");
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        value = new JLabel("Recursion depth: None");
        next = new JButton("Next");
        next.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                doNextClick();
            }
        });
        Container c = getContentPane();
        GroupLayout gl = new GroupLayout(c);
        c.setLayout(gl);
        gl.setAutoCreateContainerGaps(true);
        gl.setAutoCreateGaps(true);

        gl.setHorizontalGroup(
            gl.createSequentialGroup()
            .addComponent(value)
            .addComponent(next)
        );
        gl.setVerticalGroup(
            gl.createParallelGroup(Alignment.BASELINE)
            .addComponent(value)
            .addComponent(next)
        );

        pack();
        setLocationRelativeTo(null);
    }

    void doNextClick() {
        if (worker == null) {
            worker = new SwingWorker<Void, Void>() {
                @Override
                protected Void doInBackground() throws Exception {
                    doRecursiveAction(0);
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            value.setText("Recursive level: Done");
                        }
                    });
                    worker = null;
                    return null;
                }
            };
            worker.execute();
        } else {
            signal();
        }

    }
    void signal() {
        lock.lock();
        try {
            continueFlag = true;
            cond.signalAll();
        } finally {
            lock.unlock();
        }
    }
    void await() {
        lock.lock();
        try {
            while (!continueFlag) {
                cond.await();
            }
            continueFlag = false;
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }
    void doRecursiveAction(final int depth) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                value.setText("Recursive level: " + depth);
            }
        });
        await();
        if (depth < 10) {
            doRecursiveAction(depth + 1);
        }
    }
    /**
     * @param args
     */
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new RecursiveContinue().setVisible(true);
            }
        });
    }

}
kd304
Thnx for this example.. i am into it right now
Pitelk