1.3.5 A Progress Bar
A simple example of a pop-up user interface for a Mathematica program is a progress bar. This is an example of a "non-interactive" user interface, as defined in Section 1.2.7, because it does not need to call back to Mathematica or return a result to Mathematica. The implementation uses the Swing user interface classes, because Swing has a built-in class for progress bars. (You cannot run this example unless you have Swing installed. It comes as a standard part of Java 1.2 or later, but you can get it separately for Java 1.1.x. Most Java development tools that are still at version 1.1.x come with Swing.) The complete code for this example is also provided in the file ProgressBar.nb in the JLink/Examples/Part1 directory.
The code is commented to point out the general structure. There are several classes and methods used in this code that may be unfamiliar to you. Just keep in mind that this is completely standard Java code translated into Mathematica using the J/Link conventions. It is line-for-line identical to a Java program that does the same thing.
This code is presented as a complete program, but this does not suggest that it should be developed that way. The interactive nature of J/Link lets you tinker with Java objects a line at a time, experimenting until you get things just how you want them. Of course, this is how Mathematica programs are typically written, and J/Link lets you do the same with Java objects and methods.
We create a function ShowProgressBar that prepares and displays a progress bar dialog. The bar will be used to show percentage completion of a computation. You can supply the initial percent completed or use the default value of zero. ShowProgressBar returns the JProgressBar object because the bar needs to be updated later by calling setValue(). Note that because we return the bar object from the JavaBlock, it is not released like all other new Java objects created within this JavaBlock. This is a new behavior of JavaBlock in J/Link 2.0. If what is returned from a JavaBlock is precisely a single Java object (and not, for example, a list of objects), then this object is not released. JavaBlock is discussed in Section 1.2.3.
We also need a function to close the progress dialog and clean up after it. Only two things need to be done. First, the dispose() method must be called on the top-level frame window that contains the bar. Second, if we want to avoid leaking object references, we need to call ReleaseJavaObject on the bar object because it is the only object reference that escaped the JavaBlock in ShowProgressBar. We need to call dispose() on the JFrame object we created in ShowProgressBar, but we did not save a reference to it. The SwingUtilities class has a handy method windowForComponent() that will retrieve this frame, given the bar object.
The bar dialog has a close box in it, so a user can dismiss it prematurely if desired. This would take care of disposing the dialog, but we would still need to release the bar object. DestroyProgressBar (and the bar's setValue() method) is safe to call whether or not the user closed the dialog.
Here is how we would use the progress bar in a computation. The call to ShowProgressBar displays the bar dialog and returns a reference to the bar object. Then, while the computation is running, we periodically call the setValue() method to update the bar's appearance. When the computation is done, we call DestroyProgressBar.
An easy way to test whether your code leaks object references is to call PeekObjects before and after the computation. If the list of objects gets longer, then you have forgotten to use ReleaseJavaObject or improperly used JavaBlock.
It can take several seconds to load all the Swing classes used in this example. This means that the first time ShowProgressBar is called, there will be a significant delay. You could avoid this delay by using LoadJavaClass ahead of time to explicitly load the classes that appear in JavaNew statements.
The dialog appears onscreen with its upper left at the coordinates (400, 400). It is left as an exercise to the reader to make it centered on the screen. (Hint: the java.awt.Toolkit class has a getScreenSize() method).
Finally, because the progress bar uses the Swing classes, we can play with the look-and-feel options that Swing provides. Specifically, we can change the theme at runtime. The progress bar window is not very complicated, so it changes very little in going from one look-and-feel theme to another, but this demonstrates how to do it. The effect is much more dramatic for more complex windows.
First, create a new progress bar window:
Now load some classes from which we need to call static methods:
The default look and feel is the "metal" theme. You can change it to the native style look for your platform as follows (it helps to be able to see the window when doing this):