In my previous posts I have shown how Matlab’s semi-documented uitree and uitreenode functions can be used to display hierarchical (tree) control in Matlab GUI. Today I conclude this mini-series by answering a reader’s request to show how checkboxes, radio buttons and other similar controls can be attached to tree nodes.
There are actually several ways this can be done:
Matlab icon control
The simplest is to create two icons (checked/unchecked) and switch the node’s icon whenever it is selected (use mtree’s NodeSelectedCallback or jtree’s MouseClickedCallback callbacks) – a sample implementation was posted by Gwendolyn Fischer a couple of years ago, based on even earlier posts by John Anderson, Brad Phelan and me. Here it is, with minor fixes:
function uitree_demo % function based on treeExperiment6 by John Anderson % see http://www.mathworks.com/matlabcentral/newsreader/view_thread/104957#269485 % % The mousePressedCallback part is inspired by Yair Altman % % derived from Brad Phelan's tree demo % create a tree model based on UITreeNodes and insert into uitree. % add and remove nodes from the treeModel and update the display import javax.swing.* import javax.swing.tree.*; % figure window f = figure('Units', 'normalized'); b1 = uicontrol( 'string','add Node', ... 'units' , 'normalized', ... 'position', [0 0.5 0.5 0.5], ... 'callback', @b1_cb); b2 = uicontrol( 'string','remove Node', ... 'units' , 'normalized', ... 'position', [0.5 0.5 0.5 0.5], ... 'callback', @b2_cb); %[I,map] = imread([matlab_work_path, '/checkedIcon.gif']); [I,map] = checkedIcon; javaImage_checked = im2java(I,map); %[I,map] = imread([matlab_work_path, '/uncheckedIcon.gif']); [I,map] = uncheckedIcon; javaImage_unchecked = im2java(I,map); % javaImage_checked/unchecked are assumed to have the same width iconWidth = javaImage_unchecked.getWidth; % create top node rootNode = uitreenode('v0','root', 'File List', [], 0); % [matlab_work_path, '/fileListIcon.gif'],0); % create two children with checkboxes cNode = uitreenode('v0','unselected', 'File A', [], 0); % as icon is embedded here we set the icon via java, otherwise one could % use the uitreenode syntax uitreenode(value, string, icon, isLeaf) with % icon being a qualified pathname to an image to be used. cNode.setIcon(javaImage_unchecked); rootNode.add(cNode); cNode = uitreenode('v0','unselected', 'File B', [], 0); cNode.setIcon(javaImage_unchecked); rootNode.add(cNode); % set treeModel treeModel = DefaultTreeModel( rootNode ); % create the tree tree = uitree('v0'); tree.setModel( treeModel ); % we often rely on the underlying java tree jtree = handle(tree.getTree,'CallbackProperties'); % some layout drawnow; set(tree, 'Units', 'normalized', 'position', [0 0 1 0.5]); set(tree, 'NodeSelectedCallback', @selected_cb ); % make root the initially selected node tree.setSelectedNode( rootNode ); % MousePressedCallback is not supported by the uitree, but by jtree set(jtree, 'MousePressedCallback', @mousePressedCallback); % Set the mouse-press callback function mousePressedCallback(hTree, eventData) %,additionalVar) % if eventData.isMetaDown % right-click is like a Meta-button % if eventData.getClickCount==2 % how to detect double clicks % Get the clicked node clickX = eventData.getX; clickY = eventData.getY; treePath = jtree.getPathForLocation(clickX, clickY); % check if a node was clicked if ~isempty(treePath) % check if the checkbox was clicked if clickX <= (jtree.getPathBounds(treePath).x+iconWidth) node = treePath.getLastPathComponent; nodeValue = node.getValue; % as the value field is the selected/unselected flag, % we can also use it to only act on nodes with these values switch nodeValue case 'selected' node.setValue('unselected'); node.setIcon(javaImage_unchecked); jtree.treeDidChange(); case 'unselected' node.setValue('selected'); node.setIcon(javaImage_checked); jtree.treeDidChange(); end end end end % function mousePressedCallback function selected_cb( tree, ev ) nodes = tree.getSelectedNodes; node = nodes(1); path = node2path(node); end function path = node2path(node) path = node.getPath; for i=1:length(path); p{i} = char(path(i).getName); end if length(p) > 1 path = fullfile(p{:}); else path = p{1}; end end % add node function b1_cb( h, env ) nodes = tree.getSelectedNodes; node = nodes(1); parent = node; childNode = uitreenode('v0','dummy', 'Child Node', [], 0); treeModel.insertNodeInto(childNode,parent,parent.getChildCount()); % expand to show added child tree.setSelectedNode( childNode ); % insure additional nodes are added to parent tree.setSelectedNode( parent ); end % remove node function b2_cb( h, env ) nodes = tree.getSelectedNodes; node = nodes(1); if ~node.isRoot nP = node.getPreviousSibling; nN = node.getNextSibling; if ~isempty( nN ) tree.setSelectedNode( nN ); elseif ~isempty( nP ) tree.setSelectedNode( nP ); else tree.setSelectedNode( node.getParent ); end treeModel.removeNodeFromParent( node ); end end end % of main function treeExperiment6 function [I,map] = checkedIcon() I = uint8(... [1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0; 2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,1; 2,2,2,2,2,2,2,2,2,2,2,2,0,2,3,1; 2,2,1,1,1,1,1,1,1,1,1,0,2,2,3,1; 2,2,1,1,1,1,1,1,1,1,0,1,2,2,3,1; 2,2,1,1,1,1,1,1,1,0,1,1,2,2,3,1; 2,2,1,1,1,1,1,1,0,0,1,1,2,2,3,1; 2,2,1,0,0,1,1,0,0,1,1,1,2,2,3,1; 2,2,1,1,0,0,0,0,1,1,1,1,2,2,3,1; 2,2,1,1,0,0,0,0,1,1,1,1,2,2,3,1; 2,2,1,1,1,0,0,1,1,1,1,1,2,2,3,1; 2,2,1,1,1,0,1,1,1,1,1,1,2,2,3,1; 2,2,1,1,1,1,1,1,1,1,1,1,2,2,3,1; 2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,1; 2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,1; 1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1]); map = [0.023529,0.4902,0; 1,1,1; 0,0,0; 0.50196,0.50196,0.50196; 0.50196,0.50196,0.50196; 0,0,0; 0,0,0; 0,0,0]; end function [I,map] = uncheckedIcon() I = uint8(... [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1; 2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1; 2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,1; 2,2,1,1,1,1,1,1,1,1,1,1,2,2,3,1; 2,2,1,1,1,1,1,1,1,1,1,1,2,2,3,1; 2,2,1,1,1,1,1,1,1,1,1,1,2,2,3,1; 2,2,1,1,1,1,1,1,1,1,1,1,2,2,3,1; 2,2,1,1,1,1,1,1,1,1,1,1,2,2,3,1; 2,2,1,1,1,1,1,1,1,1,1,1,2,2,3,1; 2,2,1,1,1,1,1,1,1,1,1,1,2,2,3,1; 2,2,1,1,1,1,1,1,1,1,1,1,2,2,3,1; 2,2,1,1,1,1,1,1,1,1,1,1,2,2,3,1; 2,2,1,1,1,1,1,1,1,1,1,1,2,2,3,1; 2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,1; 2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,1; 1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1]); map = ... [0.023529,0.4902,0; 1,1,1; 0,0,0; 0.50196,0.50196,0.50196; 0.50196,0.50196,0.50196; 0,0,0; 0,0,0; 0,0,0]; end

uitree with custom checkbox icons
Custom Java classes
An alternative is to create a custom tree Java class and/or a custom TreeCellRenderer/TreeCellEditor. Specifically, to change the icons of each node you have to implement your own java component which derives from DefaultMutableTreeNode.
Some online resources to get you started:
- Sun’s official JTree customization tutorial
- Santhosh Kumar’s custom tree icons, tree-node checkboxes, and selective tree-node checkboxes
- Java2s’s multiple JTree customization snippets
Built-in classes
Another option is to use Matlab’s built-in classes, either com.mathworks.mwswing.checkboxtree.CheckBoxTree or com.jidesoft.swing.CheckBoxTree:
import com.mathworks.mwswing.checkboxtree.* jRoot = DefaultCheckBoxNode('Root'); l1a = DefaultCheckBoxNode('Letters'); jRoot.add(l1a); l1b = DefaultCheckBoxNode('Numbers'); jRoot.add(l1b); l2a = DefaultCheckBoxNode('A'); l1a.add(l2a); l2b = DefaultCheckBoxNode('b'); l1a.add(l2b); l2c = DefaultCheckBoxNode('<html><b>α'); l1a.add(l2c); l2d = DefaultCheckBoxNode('<html><i>β'); l1a.add(l2d); l2e = DefaultCheckBoxNode('3.1415'); l1b.add(l2e); % Present the standard MJTree: jTree = com.mathworks.mwswing.MJTree(jRoot); jScrollPane = com.mathworks.mwswing.MJScrollPane(jTree); [jComp,hc] = javacomponent(jScrollPane,[10,10,120,110],gcf); % Now present the CheckBoxTree: jCheckBoxTree = CheckBoxTree(jTree.getModel); jScrollPane = com.mathworks.mwswing.MJScrollPane(jCheckBoxTree); [jComp,hc] = javacomponent(jScrollPane,[150,10,120,110],gcf);

a regular MJTree (left) and a CheckBoxTree (right)
Note: Matlab’s CheckBoxTree does not have a separate data model. Instead, it relies on the base MJTree’s model, which is a DefaultTreeModel by default. JIDE’s CheckBoxTree does have its own model.
This concludes my uitree mini-series. If you have any special customizations, please post a comment below.




























