| CIT
594 Threading Suggestions for Interpreter Spring 2006, David Matuszek |
The GUI has Start, Pause/Resume, and Stop buttons. This is fairly typical for a program that does animation under the users control. For these buttons to work properly, they must be in a Thread that is separate from the Thread that performs the animation.
Note that while it is possible to pause, resume, and stop a Thread directly from another Thread, these methods are deprecated, because they may leave objects in an invalid state. Instead, the correct approach is to set flags that the other Thread can check, and pause or stop itself.
There are two ways to create and execute a Thread. Here's the slightly simpler way:
extends Thread.public void run() in that class.start().Since a class may only extend one other class, and the above requires that you extend Thread, there is a second and more flexible way to create and execute a Thread:
implements Runnable.public void run() in that class.new Thread(myObject).start().Since my Interpreter does not extend any other class (loosely speaking; by default, of course, it extends Object), I could have used either approach. For no particular reason, I chose the second.
I only created one Interpreter object, which I reuse. It's also possible to create a new Interpreter each time you execute a Logo program, if for some reason that's more convenient.
When the Start button is clicked, here's what I do:
if (parser.program()) { Treetree = parser.getParseTree(); interpreter.setParseTree(tree); Thread thread = new Thread(interpreter); thread.start(); }
You need to catch two kinds of exceptions: Parse exceptions, and Interpreter exceptions. Your error messages should be as helpful as you can reasonably make them.
You could have two different buttons, Pause and Resume. In a GUI, you should always disable (gray out) any controls that are not currently applicable, so at least one of these buttons should always be disabled. I found it easier just to have one button, and relabel it.
Either way, clicking this button should just send a message to the interpreter.
interpreter.setPaused(pause);
The main loop of the interpreter needs to check for this. Ideally this would
be done with wait and notify, but I was lazy and did
it this way:
while (paused) { try { Thread.sleep(50); } catch (Exception e) { } }
In LogoGui: interpreter.setTerminated(true);
In the Interpreter's main loop: if (terminated) return;
When the Interpreter finishes execution, the Thread dies, and cannot be resurrected. Hence, the only way to run the program again (or another program) is to have the Start button create a new Thread.
As the program executes, you want it to pause between operations that cause
a visible change on the screen (and only those operations). Do
this with a Thread.sleep(delay); message. This could be done in
the interpreter, on a case-by-case basis, but I found it slightly easier to
do in the Turtle, each time it tells the TurtleCanvas to repaint().