J/Link Tutorial

Creating a progress bar

A simple example of a pop-up user interface for a Mathematica program is a progress bar. Here a progress bar is implemented using 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.

Starting Out

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. The program is identical line-for-line 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 the results you want. This is how Mathematica programs are typically written, and J/Link lets you do the same with Java objects and methods.

The following code creates a function ShowProgressBar that prepares and displays a progress bar dialog. The bar will show what percentage of a computation has been completed. You can supply the initial percent completed or use the default value of 0. ShowProgressBar returns the JProgressBar object because the bar needs to be updated later by calling setValue. Because we need to return the bar object, the JavaNew that constructs it is outside the JavaBlock that encloses the rest of the code. If the JavaNew was inside the JavaBlock, the bar object would be released, which would make it invalid for use from Mathematica when JavaBlock ended.

\(ShowProgressBar[
        title_String: "Computation Progress", 
			\ \ \ \ 	caption_String: "Percent complete:", 
			\ \ \ \ 	percent_Integer:  
          0
			\ \ \ 	\ \ ]\  := 
	
      Module[{frame, \ panel, \ label, \ 
          bar}, 
		InstallJava[]; 
		bar\  = \ 
          JavaNew["javax.swing.JProgressBar"]; 
		JavaBlock[
			frame\  = \ JavaNew["javax.swing.JFrame", \ title]; 
			frame@
            setSize[300, \ 110]; 
			frame@
            setResizable[False]; 
			frame@
            setLocation[400, \ 400]; 
			panel\  = \ 
            JavaNew["javax.swing.JPanel"]; 
			panel@
            setLayout[Null]; 
			frame@\(getContentPane[]@
              add[panel]\); 
			label\  = \ 
            JavaNew["javax.swing.JLabel", \ caption]; 
			label@
            setBounds[20, \ 10, \ 260, \ 20]; 
			panel@
            add[label]; 
			bar@
            setBounds[20, \ 40, \ 260, \ 30]; 
			bar@
            setMinimum[0]; 
			bar@setMaximum[100]; 
			bar@
            setValue[percent]; 
			panel@add[bar]; 
			JavaShow[
            frame]; 
			bar
		]
	]\)

You 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 you want to avoid leaking object references, you must call ReleaseObject on the bar object because it is the only object reference that escaped the JavaBlock in ShowProgressBar. You need to call dispose on the JFrame object you created in ShowProgressBar The reference to the object was not saved. The SwingUtilities class has a handy method, windowForComponent, that will retrieve this frame, given the bar object.

\(DestroyProgressBar[bar_?JavaObjectQ]\  := 
	
      JavaBlock[
		LoadClass["javax.swing.SwingUtilities"]; 
		SwingUtilities`windowForComponent[bar]@dispose[]; 
		ReleaseObject[
          bar]
	]\)

The bar dialog box has a close box in it, so a user can dismiss it prematurely if desired. However, you would still need to release the bar object. DestroyProgressBar and the bar's setValue method are safe to call whether or not the user closes the dialog box.

The following code uses the progress bar in a computation and releases the bar object with DestroyProgressBar. The call to ShowProgressBar displays the bar dialog box and returns a reference to the bar object. Then, while the computation is running, the setValue method is called periodically to update the bar’s appearance. When the computation is done, DestroyProgressBar is called.

bar  =  ShowProgressBar[];
n  =  0;
\(\(While[n\  <= \ 5, 
	bar@setValue[n/5\ *\ 100]; 
	
        Pause[1]; \  (*\ 
          This\ simulates\ the\ time - 
            consuming\ \(\(computation\)\(.\)\)\ *) 
	\(n++\)
];\)\)

DestroyProgressBar[bar];

An easy way to test whether your code leaks object references is to call PeekObjects[] before and after the above computation. If the list of objects gets longer, then you have forgotten to use ReleaseObject 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 LoadClass ahead of time to explicitly load the classes that appear in JavaNew statements.

The dialog box appears on the screen with its upper left corner at the coordinates (400, 400). Centering the box on the screen is left as an exercise for the reader. (Hint: the java.awt.Toolkit class has a getScreenSize method.)

Finally, because the progress bar uses the Swing classes, you can play with the look-and-feel options that Swing provides. Specifically, you can change the theme at run time. The progress bar window is not very complicated, so it changes very little in going from one look-and-feel theme to another. The effect is much more dramatic for more complex windows.

First, create a new progress bar window.

bar  =  ShowProgressBar[];

Now load some classes from which you need to call static methods.

LoadClass["javax.swing.UIManager"];
LoadClass["javax.swing.SwingUtilities"];

Change the appearance to the metal theme. It helps to be able to see the window when doing this.

\(JavaBlock[
	
      UIManager`setLookAndFeel["javax.swing.plaf.metal.MetalLookAndFeel"]
	frame\  = \ SwingUtilities`windowForComponent[bar]; 
	
      SwingUtilities`updateComponentTreeUI[frame]
]\)

Clean up.

DestroyProgressBar[bar]

Final Code

The following example combines all the above elements for J/Link for creating a progress bar.

Needs["JLink`"]

ShowProgressBar[title_String:"Computation Progress", caption_String:"Percent complete:", percent_Integer:0] :=
	Module[{frame, panel, label, bar},
		InstallJava[];
		bar = JavaNew["javax.swing.JProgressBar"];
		JavaBlock[
			frame = JavaNew["javax.swing.JFrame", title];
			frame@setSize[300, 110];
			frame@setResizable[False];
			frame@setLocation[400, 400];
			panel = JavaNew["javax.swing.JPanel"];
			panel@setLayout[Null];
			frame@getContentPane[]@add[panel];
			label = JavaNew["javax.swing.JLabel", caption];
			label@setBounds[20, 10, 260, 20];
			panel@add[label];
			bar@setBounds[20, 40, 260, 30];
			bar@setMinimum[0];
			bar@setMaximum[100];
			bar@setValue[percent];
			panel@add[bar];
			JavaShow[frame];
			bar
		]
	]

DestroyProgressBar[bar_?JavaObjectQ] :=
	JavaBlock[
		LoadClass["javax.swing.SwingUtilities"];
		SwingUtilities`windowForComponent[bar]@dispose[];
		ReleaseObject[bar]
	]


ProgressBarDemo[] :=
	Module[{bar, n},
		bar = ShowProgressBar[];
		n = 0;
		While[n <= 5,
			bar@setValue[n/5 * 100];
			Pause[1]; (* This simulates the time-consuming computation. *)
			n++
		];
		DestroyProgressBar[bar];
	]

Example

You must call InstallJava prior to running this example.

InstallJava[];

ProgressBarDemo[]