twentylemon
twentylemon

Reputation: 1246

JTextArea text disappears

I'm making a chess program for a project. I'm trying to add a move history box to the side of the board. The move history works fine, and the data is properly sent to the text area, but the text inside the JTextArea disappears while the AI is thinking about his move.

public void aiMove(){
  if (!playing){ return; }
  paintImmediately(0,0,totalX,totalY);
  ai = eve.getMove(chess,wtm,aiOut);   //text disappears here
  chess.makeMove(ai);
  wtm = !wtm;
  humanMove = true;
  writeMove(ai);                   //updates move history, text reappears here
  playing = stillPlaying();
  repaint();
}

private void writeMove(Move move){
  char c = "abcdefgh".charAt(7-move.fromY);
  char h ="abcdefgh".charAt(7-move.toY);
  String s = Character.toString(c)+(move.fromX+1)+" - "+Character.toString(h)+(move.toX+1)+"  ";
  if (!wtm){
    String q = chess.getFullMove()+".  "+s+"  ";
    moves.setText(moves.getText()+q);
  }
  else {
    moves.setText(moves.getText()+s+"\n");
  }
}

Here's a print screen of what's happening. http://s13.postimage.org/mh7hltfk7/JText_Area_disappear.png


SOLVED Thanks to all replies. I changed aiMove() so it creates a thread. Here is what I did.

Attempt #3... swing is still so foreign to me. I didn't want to change writeMove to getMove or I would have to rewrite the human's turn slightly. Since the project is essentially done, I am trying to avoid as much work as possible :) The GUI is entirely optional anyways, I was just doing it for fun, and to try and learn a bit of swing.

public void aiMove(){
  if (!playing){ return; }
  if (!aiThread.isAlive()){
    aiThread = new Thread(){
      public void run(){
        ai = eve.getMove(chess,wtm,aiOut);
        chess.makeMove(ai);
        wtm = !wtm;
        humanMove = true;
        SwingUtilities.invokeLater(new Runnable(){
          public void run(){
            writeMove(ai);
          }
        });
        repaint();
        playing = stillPlaying();
      }
    };
    aiThread.start();
  }
}

It also fixed a problem I had before, in that if I were to hold down the 'a' key (force ai move), it would queue up many forced ai moves. Now that doesn't happen.

Upvotes: 2

Views: 731

Answers (1)

David Kroukamp
David Kroukamp

Reputation: 36423

The problem is your AI thinking is CPU intensive/time consuming, thus it is considered a long running task. You should not do long running tasks on GUI Event Dispatch Thread as this will cause the UI to seem frozen and thus only show updates after the task has finished.

Fortunately there are 2 different approaches you could use:

The SwingWorker subclass can define a method, done, which is automatically invoked on the event dispatch thread when the background task is finished.

SwingWorker implements java.util.concurrent.Future. This interface allows the background task to provide a return value to the other thread. Other methods in this interface allow cancellation of the background task and discovering whether the background task has finished or been cancelled.

The background task can provide intermediate results by invoking SwingWorker.publish, causing SwingWorker.process to be invoked from the event dispatch thread.

The background task can define bound properties. Changes to these properties trigger events, causing event-handling methods to be invoked on the event dispatch thread.

  • Alternatively create separate Thread for AI thinking and wrap setText call in SwingUtilities.invokeLater(...);

    Thread t=new Thread(new Runnable() {
         @Override
         public void run() {
        }
    });
    t.start();
    

UPDATE

After reading MadProgrammers comment (+1 to it) please remember to create/manipulate your GUI/Swing components on EDT via the SwingUtilities.invokeLater(..) block. You can read more on it here.

UPDATE 2:

That edit is defeating the point, the only call on EDT in SwingUtilitites block should be the setText or atleast only code that manipulates a Swing component i.e

public void aiMove(){
  if (!playing){ return; } 
  if (!aiThread.isAlive()){  //originally initialized by constructor
    aiThread = new Thread(){
      public void run(){
            ai = eve.getMove(chess,wtm,aiOut);
            chess.makeMove(ai);
            wtm = !wtm;
            humanMove = true;
        SwingUtilities.invokeLater(new Runnable(){
          public void run(){
            writeMove(ai);
          }
        });
        repaint();
        playing = stillPlaying();
      }
    };
    aiThread.start();
  }
}

Upvotes: 6

Related Questions