Once again I welcome guest blogger Matt Whitaker, with the long awaited EDT article.
Java Swing’s Event Dispatch Thread (EDT)
or: why does my GUI foul up?
Matlab for the most part is a single threaded environment. That is, all commands are executed sequentially along a single execution thread. The main exception to this are the Handle Graphics (GUI) components whose operations execute on the Java Event Dispatch Thread (EDT). EDT effects are reflected even in mundane Matlab GUI operations.
If we execute the code below we will probably see nothing until the loop completes and the figure appears with the text label showing ‘10000′:
h = figure; txt = uicontrol('Parent',h, 'Style','text', 'String','1'); for n = 1:10000 set(txt,'String',int2str(n)) end %for
By adding a couple of drawnow commands we get the figure and text label to render and then we see the count progress to 10000.
h = figure; txt = uicontrol('Parent',h, 'Style','text', 'String','1'); drawnow; for n = 1:10000 set(txt,'String',int2str(n)); drawnow; end %for
The drawnow function allows the EDT queue to be flushed and the pending graphics operations to be evaluated. This will also happen with pause and several other commands.
If we want to use Swing (or AWT) components in our user interfaces we need to take this multi-threaded environment into account. The Swing toolkit designers decided to make all the Swing components thread un-safe in order to decrease their complexity. As a consequence, all access to Swing components should be done from the event dispatch thread (EDT), to ensure the operations are executed sequentially, at the exact order in which they were dispatched. Any action on a Swing component done on another thread (Matlab’s main processing thread in our case) risks a race-condition or deadlock with the EDT, which could (and often does) result in weird, non-deterministic and non-repetitive behavior – all of which should be avoided in any application which should behave in a precisely deterministic manner.
In Java, the usual pattern to accomplish EDT dispatching is to create a Runnable object, encapsulate the GUI code in the run method of the Runnable object, then pass the Runnable object to the static EventQueue.invokeLater (or EventQueue.invokeAndWait if we need to block operations to get a return value) method.
Runnable runnable = new Runnable() { public void run() { //GUI Code here } } EventQueue.invokeLater(runnable);
There are several functions in Matlab that implement this programming pattern for us: javaObjectEDT, javaMethodEDT, awtinvoke, awtcreate and javacomponent. JavaMethodEDT and javaObjectEDT were introduced in version R2008b (7.7) and are minimally and only partially documented although they have reasonably complete help comments. The other three are semi-documented (meaning they are unsupported but if you edit or type their m-file you’ll see a fairly detailed help section), and although there is some overlap in their functionality they are still available.
javaObjectEDT and javaMethodEDT
javaObjectEDT is the the preferred method since R2008b of creating swing components to be used on the EDT. An object created with javaObjectEDT will have all of its subsequent method calls run on the EDT. This is termed Auto Delegation. Auto-delegation greatly simplifies and increases the readability of code. Note that objects created as a result of method calls may not be implemented on the EDT.
If you have an existing Java object, you can pass it to javaObjectEDT at any time - all its subsequent calls will then onward run on the EDT. Note that this useful functionality is an under-documented javaObjectEDT feature: it is not mentioned in the main help section but only implied from the example.
% Create a button on the EDT btn = javaObjectEDT('javax.swing.JButton'); % this will run on EDT since btn was javaObjectEDT-created btn.setText('Button'); % Create a button NOT on the EDT btn2 = javax.swing.JButton; % Dangerous! call will run on main Matlab thread btn2.setText('Button2'); % modify btn2 so its methods will start running on the EDT javaObjectEDT(btn2); btn2.setText('Button2');
The following example shows the use of javaObjectEDT and javaMethodEDT in a more complex situation using a JTable:
function tableExample hFig = figure; drawnow; %need to get figure rendered %use Yair's createTable to add a javax.swing.JTable %http://www.mathworks.com/matlabcentral/fileexchange/14225-java-based-data-table %wrap ceateTable in javaObjectEDT to put the ensuing method calls on the EDT f = java.awt.Font(java.lang.String('Dialog'),java.awt.Font.PLAIN,14); headers = {'Selected','File','Analysis Routine','Task Status'}; tbl = javaObjectEDT(createTable(hFig,headers,[],false,'Font',f)); %set column 1 to use check boxes and set up a change callback tbl.setCheckBoxEditor(1); jtable = javaObjectEDT(tbl.getTable); %get the underlying Java Table. IMPORTANT: we need to put jtable on the EDT columnModel = javaObjectEDT(jtable.getColumnModel); %now we can now do direct calls safely on jtable selectColumn = javaObjectEDT(columnModel.getColumn(0)); selectColumnCellEditor = selectColumn.getCellEditor; chk = javaMethodEDT('getComponent',selectColumnCellEditor); set(chk,'ItemStateChangedCallback',@chkChange_Callback); %make column three a combo drop down analysisTable = {'Analysis1';'Analysis2';'Analysis3'}; cb = javaObjectEDT('com.mathworks.mwswing.MJComboBox',analysisTable); cb.setEditable(false); cb.setFont(f); set(cb,'ItemStateChangedCallback',@cbChange_Callback); editor = javaObjectEDT('javax.swing.DefaultCellEditor',cb); analysisColumn = javaObjectEDT(columnModel.getColumn(2)); analysisColumn.setCellEditor(editor); %set some column with restrictions selectColumn.setMaxWidth(100); analysisColumn.setPreferredWidth(300); %set the data SELECTED = java.awt.event.ItemEvent.SELECTED; tbl.setData({false,'file1','Analysis2','Analysis2';... true,'file2','Analysis3','Analysis3'}); drawnow; function cbChange_Callback(src,ev) %#ok jRow = jtable.getSelectedRow; stateChange = javaMethodEDT('getStateChange',ev); if stateChange == SELECTED newData = javaMethodEDT('getItem',ev); model = jtable.getModel; javaMethodEDT('setValueAt',model,newData,jRow,3); end %if end %cbChange function chkChange_Callback(src,ev) %#ok chkBox = javaMethodEDT('getItem',ev); if logical(javaMethodEDT('isSelected',chkBox)) beep; %put useful code here else beep; pause(0.1) beep; %put useful code here end %if end %chkChange_Callback end %tableExample
If you are running Matlab R2008a or later, javacomponent uses the javaObjectEDT function to create the returned objects so you do not have to do anything further to these objects to have their calls dispatched on the EDT. Users need to take care that objects added directly to the components created by javacomponent are on the EDT as well as specialized sub-components (e.g. CellRenderers and CellEditors). The overhead of calling javaMethodEDT is fairly small so if in doubt, use it.
javaObjectEDT and its kin first appeared in R2008a, although they only became supported in R2008b. Unfortunately, using them on R2008a sometimes causes hangs and all sorts of other mis-behaviors. This problem was fixed in the R2008b release, when javaObjectEDT became a fully-supported function. The problem with using javaObjectEDT in our application is that if it ever runs on an R2008a platform it might hang! (on Matlab release R2007b and earlier we will get an informative message saying that the javaObjectEDT function does not exist)
For this reason, I am using the following method in my projects:
function result = javaObjEDT(varargin) %Placeholder of Matlab's buggy javaObjectEDT function on R2008a % Programmed by Yair M. Altman: altmany(at)gmail.com % $Revision: 1.2 $ $Date: 2009/01/25 11:31:08 $ try try result = varargin{1}; catch result = []; end v = version; if str2double(v(1:3)) > 7.6 result = builtin('javaObjectEDT',varargin{:}); end catch % never mind end end
Note that javaMethodEDT has the method name as its first input argument, and the object name or reference as its second arg. This is inconsistent with many other Matlab/Java functions, which normally accept the target object as the first argument (compare: invoke, awtinvoke, notify etc.). It also means that we cannot use the familiar obj.javaMethodEDT(methodName) format.
One final note: when javaObjectEDT and javaMethodEDT first appeared in R2008a, they were complemented by the javaObjectMT and javaMethodMT functions, which create and delegate Java objects on the main Matlab computational thread. Their internal documentation says that there are cases when execution must occur on the MT rather than EDT, although I am personally not aware of any such case.
awtcreate and awtinvoke
For users with versions prior to R2008b the user must use the awtcreate function to create objects on the EDT. One huge disadvantage of this older function is that if you have to pass java objects in the parameter list you must use the very cumbersome JNI style notation. For example, for the simple task of setting a button label, one has to use:
btn = awtcreate('javax.swing.JButton'); awtinvoke(btn,'setText(Ljava/lang/String;)','click me')
The other disadvantage is that creating the object using awtcreate does not ensure that its subsequent method calls will be executed on the EDT. The awtinvoke function must be used for each call.
Also, both awtcreate and awtinvoke have some limitations due to bugs in the private parseJavaSignature function (for example, invoking methods which accept a java.lang.Object) which forces one to use the direct call to the method, using the main Matlab thread. This can result in the undesired effects described above. In this situation the best workaround is to call pause(0.01) to allow the event queue to clear.
Versions of javacomponent earlier than R2008a use awtcreate and objects created by these versions must have their subsequent methods called by awtinvoke to be used on the EDT.
A very rare CSSM thread discusses the usage of awtcreate and awtinvoke with some very interesting remarks by MathWorks personnel.
There is an interesting option in awtinvoke that was not carried over into the newer javaMethodEDT. This option allows the user to pass a function handle in the argument list along with its parameters. This option creates an undocumented com.mathworks.jmi.Callback object that has a delayed callback. The delayed callback is dispatched on the EDT so that it will be called once the java method used in awtinvoke is finished. Note that the actual function will still execute on the main Matlab thread the delayed callback will just control when it is called. However this may be useful at times. It is possible to put this functionality into a separate function we can call to delay execution until the event queue is cleared.
%CALLBACKONEDTQUEUE will place a callback on the EDT to asynchronously %run a function. %CALLBBACKONEDTQUEUE(FCN) will run function handle FCN once all previous %methods dispatched to the EDT have completed. %CALLBBACKONEDTQUEUE(FCN,ARG1,ARG2,...) ill run function handle FCN with %arguments ARG1,ARG2...once all previous methods dispatched to the EDT %have completed. %Note that the function is still executing on the main Matlab thread. This %function just delays when it will be called. function callbackOnEDTQueue(varargin) validateattributes(varargin{1},{'function_handle'},{}); callbackObj = handle(com.mathworks.jmi.Callback,'callbackProperties'); set(callbackObj,'delayedCallback',{@cbEval,varargin(:)}); callbackObj.postCallback; function cbEval(src,evt,args) %#ok feval(args{:}); end %cbEval end %callbackOnEDTQueue























