views:

162

answers:

3

Hi,

I have a problem following from my previous problem. I also have the code SwingUtillities.invokeAndWait somewhere else in the code base, but when I remove this the gui does not refresh. If I dont remove it the error I get is:

Exception in thread "AWT-EventQueue-0" java.lang.Error: Cannot call invokeAndWait from the event dispatcher thread
 at java.awt.EventQueue.invokeAndWait(Unknown Source)
 at javax.swing.SwingUtilities.invokeAndWait(Unknown Source)
 at game.player.humanplayer.model.HumanPlayer.act(HumanPlayer.java:69)

The code in HumanPlayer.act is:

public Action act(final Action[] availiableActions) {
  try {

   SwingUtilities.invokeAndWait(new Runnable() {
    @Override
    public void run() {
     gui.update(availiableActions);
    }
   });
  }
  catch (InterruptedException e) {
   e.printStackTrace();
  } catch (InvocationTargetException e) {
   e.printStackTrace();
  }

  synchronized(performedAction){
   while(!hasPerformedAction()){
    try {
     performedAction.wait();
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   }
   setPerformedAction(false); 
  }

  return getActionPerfomed();
 }

Image of threads when in debug as screen doesn't paint: alt text

Text version of stack:

ui.startup.LoginScreen at localhost:51050
 -> Deamon Thread [AWT-Windows] (Running)
 -> Thread [AWT-Shutdown] (Running)
 -> Thread [AWT-EventQueue-0] (Running)
 -> Thread [DestroyJavaVM] (Running)
A: 

invokeAndWait is meant to be called from the non-GUI thread - it sends a Eunnable object to the GUI thread where it will be executed.

There's no point in sending a runnable object from the GUI-thread to itself. It has the same effect as calling run() on the Runnable object directly.

Itay
Yes is there another way to execute that command and wait for completion before the rest of the method is executed?
Aly
I think that's already how it works - the event thread is a single thread so it will complete the gui.update call before moving on.
Amber Shah
but the gui isn't refreshing
Aly
+1  A: 

Based on the comments, it appears you are not repainting the frame once the actions are completed. If you do not, then the screen will only be updated at what seems to be random times (when another window moves in front, perhaps).

Inside gui.update, I suggest you make the last line:

myFrame.repaint();

(more or less, depending on your circumstances).


Edit: As it turns out, the actual problem is this loop:

synchronized(performedAction){
    while(!hasPerformedAction()){
        try {
            performedAction.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    setPerformedAction(false);
}

Since there is only one application thread (which happens to be the EDT), the result of hasPerformedAction() can never change (assuming it's a simple getter). There is no other thread to change the value. Since this infinite loop is on the EDT, the GUI can never be repainted; hence it locks up.

Michael Myers
I have tried this with no luck, the gui doesn't even initially paint; the screen stays white and the X in the top right hand corner doesn't kill the process
Aly
@Aly: Have you ever gotten anything to paint on the screen in this project (and then changed things later)?
Michael Myers
yes I previously was creating a new GameInitializer from another thread (not EDT) and the gui was functioning fine. But now I have tried to make another JFrame which creates the GameInitializer which now breaks things as previously calling `invokeAndWait` was fine (as the initial thread was not EDT)
Aly
@Aly: Can you run it in a debugger and break it while the screen is white? The stacks of each thread might prove interesting.
Michael Myers
adding image of thread stack in debug mode to question
Aly
+1  A: 

The answer was instead of making the call

new GameInitializer(userName, player, Constants.BLIND_STRUCTURE_FILES.get(blindStructure), handState);

from the EDT, make it execute on a new (non EDT) thread so that later when invokeAndWait is called it functions as correctly as the thread running that command is not the EDT. The amended code is as follows:

Thread t = new Thread(new Runnable() {
    @Override
    public void run() {
       new GameInitializer(userName, player, Constants.BLIND_STRUCTURE_FILES.get(blindStructure), handState);       
    }

   });
t.start();
Aly