1
public static void moveTo(Coordinate destination) {

    changeState(State.NAVIGATION);
    controlPnl.addRemote(Remote.createRemote(remoteType.NAVIGATION));

    dmc.moveTo(destination);

    changeState(State.IMMEDIATE);
    controlPnl.addRemote(Remote.createRemote(remoteType.IMMEDIATE));
}

In this code, the addRemote method updates the controlPnl GUI with new buttons. The dmc.moveTo method has up to two Thread.sleep calls in it, and I think that they are being called before the controlPnl GUI is being updated. I've commented out the two method calls after dmc.moveTo which change the GUI back to what it was before the call, and the controlPnl doesn't finish updating until moveTo finishes executing. What I need is for the GUI to finish updating before the moveTo method starts executing and puts the Thread to sleep. Is there any way that I could accomplish this in Java 6?

In case it matters, the moveTo method moves a LEGO Mindstorm robot to a specified point on a path defined by the user. The GUI that is being updated provides Swing components (JButtons and JRadioButtons) for the user to control the robot with while it's navigating. The addRemote method changes the set of Swing components for the user to use, and the moveTo method sends commands to the robot to actually execute the movement (by telling its motors to move, sleeping for the correct amount of time, then telling its motors to stop moving). I'm using a state machine pattern, and this method is part of the controller which handles events from the UIs.

Nelson O
  • 263
  • 2
  • 8

2 Answers2

0

You have a single GUI thread. Don't use it to call other things; if you do, those things have to complete before anything else is going to happen in your GUI.

At the very least you would want to start a new thread to perform your dmc.moveTo(destination). More than likely this isn't the only place you're doing this, and probably want an Executor set up to perform these tasks.

Without knowing more about your code (especially since you're using a static method) I can't comment on how you would want to set up the Executor but the simplest example of using a Thread would be:

 public static void moveTo(final Coordinate destination) {

    changeState(State.NAVIGATION);
    controlPnl.addRemote(Remote.createRemote(remoteType.NAVIGATION));

    new Thread(new Runnable() {
                   public void run() {
                       dmc.moveTo(destination);
                       changeState(State.IMMEDIATE);
                       controlPnl.addRemote(Remote.createRemote(remoteType.IMMEDIATE));
                   }
               }).start();
}

This creates a new Thread that executes your (anonymous) Runnable which performs your moveTo(). Note this is far less efficient than having an Executor that is ready to run your task; it has to create a new Thread every time. However, if that's not an issue in terms of the performance you need then it's perfectly fine. Also note that because I'm referencing destination directly inside the anonymous inner class, it has to be declared final when passed into your method.

Brian Roach
  • 76,169
  • 12
  • 136
  • 161
  • I implemented it like you did, but I also called join on the thread after starting it. I also wrapped my Swing calls with `SwingUtilities.invokeLater` and now the GUI flashes after the moveTo thread is finished executing, but the GUI still remains unupdated until after I the moveTo method finishes its execution, which causes it to update to the NAVIGATION view, and immediately back to the IMMEDIATE view. Thank you for helping me, and I would add more details about my code if I didn't have somewhere to be at the moment. – Nelson O Feb 12 '13 at 17:54
  • @user2065144 - don't call `join()` on the thread. That's going to make your GUI thread wait until it completes which ... takes you right back to your original problem. Once `run()` completes the `Thread` will finish and be garbage collected because there's nothing holding a reference to it. . – Brian Roach Feb 12 '13 at 17:56
  • Ok, thank you. I thought that join stopped the execution of the current thread until the thread that you called join on finished, so I was calling it to keep me from moving onto the code to switch back to the IMMEDIATE stuff, but it's working now that I removed join and put the `change state` and `add remote` methods into the run method. Thank you very much for your help. – Nelson O Feb 12 '13 at 19:30
  • Gotcha - I wasn't sure that you were also trying to delay the last two calls as well; I went ahead and moved those in my answer for anyone reading it in the future. Glad I could help! – Brian Roach Feb 12 '13 at 19:32
0

Since your moveTo takes a long time you should not execute it on the main event handling thread. Instead, have moveTo update the GUI and start the actual movement in a separate thread. Once the movement is complete, use SwingUtilities.invokeLater to do the second set of GUI updates.

private static ExecutorService executor = Executors.newCachedThreadPool();

public static void moveTo(final Coordinate destination) {

    changeState(State.NAVIGATION);
    controlPnl.addRemote(Remote.createRemote(remoteType.NAVIGATION));

    executor.execute(new Runnable() {
      public void run() {
        dmc.moveTo(destination);
        SwingUtilities.invokeLater(new Runnable() {
          public void run() {
            changeState(State.IMMEDIATE);
            controlPnl.addRemote(Remote.createRemote(remoteType.IMMEDIATE));
          }
        });
      }
    });
}

This way moveTo does the initial set of GUI updates and then returns immediately, freeing the event loop to keep the GUI responsive, but the second changeState is delayed until the dmc.moveTo is complete.

(it may make more sense to factor this stuff out into separate methods rather than using the Runnable-in-a-Runnable anonymous classes)

Ian Roberts
  • 120,891
  • 16
  • 170
  • 183
  • Thank you, I pretty much ended up doing this, but I had the `invokeLater` methods being called within the addRemote method now. – Nelson O Feb 12 '13 at 19:34