I haven’t written on listboxes in a long while, and since I’ve recently posted on related controls (editbox, combo-box), I thought of following up with an article on customizing Matlab listbox layout. By the end of today’s article, you should be able to customize the layout of items within your listbox, such as the following:
For the following hacks, we need to gain access to the listbox control’s underlying Java component, which is a MathWorks class that extends the standard JList. We can get this component’s reference using the findjobj utility:
>> hListbox = uicontrol('Style','List', 'String',{'item #1','item #2'}); >> jScrollPane = java(findjobj(hListbox)) jScrollPane = com.mathworks.hg.peer.utils.UIScrollPane[...] >> jListbox = jScrollPane.getViewport.getView jListbox = com.mathworks.hg.peer.ListboxPeer$UicontrolList[...] |
Like multi-line editboxes, listboxes are actually composed of a container (a com.mathworks.hg.peer.utils.UIScrollPane
object) that includes three children, as expected from any JScrollPane: a javax.swing.JViewport
that contains the ListboxPeer$UicontrolList
component, and horizontal/vertical scrollbars. I explained how to customize the scrollbars in an article back in early 2010.
Today we are not interested in the scroll-pane or scollbars, but rather the jListbox
component that takes up the view-port’s contents. This component includes many useful properties that we can access and modify, including several that control the layout of the list items:
LayoutOrientation sets the layout of listbox items within the viewport. Possible values include:
- The default value (
jListbox.VERTICAL
=0) indicates the regular top-to-bottom arrangement jListbox.VERTICAL_WRAP
=1 sets a horizontal item layout, wrapping to a new row as necessary for the maximum number of rows determined by the VisibleRowCount property (default=8)jListbox.HORIZONTAL_WRAP
=2 sets a vertical item layout, wrapping to a new column at row number VisibleRowCount
For example:
jListbox.setLayoutOrientation(jListbox.HORIZONTAL_WRAP); jListbox.setVisibleRowCount(3); set(jListbox, 'LayoutOrientation',2, 'VisibleRowCount',3); % equivalent alternative |
LayoutOrientation = VERTICAL = 0VisibleRowCount is irrelevant |
LayoutOrientation = VERTICAL_WRAP = 1VisibleRowCount = 3 |
LayoutOrientation = HORIZONTAL_WRAP = 2VisibleRowCount = 3 |
FixedCellHeight and FixedCellWidth hold the listbox’s cells height (default=13 pixels) and width (default=-1). A value of -1 means that the actual size is determined by the default platform-dependent CellRenderer size:
FixedCellHeight = -1 FixedCellWidth = -1 |
FixedCellHeight = 10 FixedCellWidth = 30 |
FixedCellHeight = 16 FixedCellWidth = 50 |
We can use these properties to display a selection matrix of icons. For example, let’s display the icons in Matlab standard icons folder:
% Prepare the list of ImageIcon objects iconsFolder = fullfile(matlabroot,'toolbox/matlab/icons'); imgs = dir(fullfile(iconsFolder,'*.gif')); for iconIdx = 1 : length(imgs) iconFilename = fullfile(iconsFolder,imgs(iconIdx).name); iconFilename = strrep(iconFilename, '\', '/'); htmlStr{iconIdx} = ['<html><img src="file:/' iconFilename '"/>']; % no need for </html> end % Display in a standard one-column listbox hListbox = uicontrol('style','list', 'string',htmlStr, 'position',[10,10,160,90]); % Modify the listbox layout to display 18x18-pixel cells jScrollPane = findjobj(hListbox); jListbox = jScrollPane.getViewport.getView; jListbox.setLayoutOrientation(jListbox.HORIZONTAL_WRAP) jListbox.setVisibleRowCount(4) jListbox.setFixedCellWidth(18) % icon width=16 + 2px margin jListbox.setFixedCellHeight(18) % icon height=16 + 2px margin jListbox.repaint % refresh the display |
Other interesting things that we can do with listboxes (among others):
- Customize the scrollbars, as noted above
- Display HTML-formatted list items:
uicontrol('Style','list', 'Position',[10,10,70,70], 'String', ... {'<html><font color="red">Hello</font></html>', 'world', ... '<html><font style="font-family:impact;color:green"><i>What a', ... % note: </i></font></html> are not needed '<html><font color="blue" face="Comic Sans MS">nice day!</font>'}); % note: </html> is not needed
- Setting dynamic tooltips and right-click context-menus:
Note that while the code above used the underlying Java component, absolutely no knowledge of Java is required to understand it or use it. In fact, the entire code above is pure Matlab, simply setting the component’s properties and calling its methods, and using its inherent support of HTML strings.
Much more advanced customizations are possible at the Java level, especially using a dedicated CellRenderer. Interested readers can find more information about these and other possible customizations in my report on “Advanced Customizations of Matlab Uicontrols“. This 90-page PDF report can be purchased here ($29, please allow 24 hours for delivery by email). The report explains how to customize Matlab’s uicontrols in ways that are simply not possible using documented Matlab properties. This includes treatment of push buttons, toggle buttons, radio buttons, checkboxes, editboxes, listboxes, popup menus (aka combo-boxes/drop-downs), sliders, labels, and tooltips. Much of the information in the report is also available in hard-copy format in chapter 6 of my Matlab-Java programming book.
If you’d like me to personally add similar magic to your GUI, email me to see if I can help.
Now tell the truth – doesn’t Matlab’s standard listbox look kinda boring after all this? 🙂
Yair,
Excellent stuff
I don’t know if you’ve seen the FEX utility which I created which uses some of the items I learn’t on your site about formating and customising listboxes?
The utility uses pure matlab code with an API to make multi column (formatable) listboxes.
Regards
@Robert – I haven’t seen it until now, but it looks impressive…
Hi Yair,
thanks for this powerful tips and tricks, so far i was not a big fan of Matlab mainly because of very limited GUI functions, but now i start to believe in it.
I ordered your book, meanwhile i have a few questions if you don’t mind me to ask:
– is that possible to have a listbox with Popup menu and submenu to give options on Font/ color…
– i can not find how to change list row color with Javax.
rgds,
Michael
@Michael –
1) you can set the UIContextMenu property of the listbox with whatever functionality that you wish – see the documentation.
2) you can use HTML formatting for this – see here.
Hi Yair,
I’m already using javax to customize the ListBox and other uicontrol …..is there any way to add popup menu and submenu with javax ? same for change listbox row color.
rgds,
Michael
Of course you can do all that in Java (I don’t know why you call it javax), it’s called a JPopupMenu. But this is a Matlab blog, not a Java blog. So if you insist in using Java for this then I suggest that you check one of the numerous Java resources, either online or offline (books). You can start here.
I have an issue here. I also use a Listbox with HTML code in it, and my figure resizes the listbox whenever the figure size is changed. Now it seems that the value set with ‘setFixedCellHeight’ is reset after resizing. Do you have an idea why?
And something else: On a colleagues PC the HTML text is cut-off at the bottom. Now I found out that he is using a larger Windows font size. Do you have an idea how to detect the needed list item height depending on the used default font?
Best regards,
Sebastian
The component is reset/repainted by Matlab when it determines that a component resize is required. You can trap this in the containing panel’s SizeChangedFcn or possibly the Java component’s ComponentResizedCallback callbacks, where you can reapply all the customizations.
Regarding the font size, you can use the Java
FontMetrics
class to get the necessary information (details)Oh all right. I thought I did something wrong and the settings should be consistent after repaints.
Also the
FontMetrics
class works like a charm. Thanks a lot for your quick help!Hi Yair,
I am having a similar issue with Sebastian, for my GUI, I need to add/remove elements to the listbox interactively, causing the cell height to be reverted. I believe this is because MATLAB doesn’t have an addElement method for UIControls, meaning that the listbox needs to be redrawn everytime listbox.String is updated, resetting to the default MATLAB values for cell height and width. I can call ‘setFixedCellHeight’ for every callback that changes the listbox, but this causes flickering and you can see the cells change size everytime it is updated, do you have any suggestions for avoiding this issue?
Thanks
Hi Yair,
I am having a similar issue to Sebastian, where cell height is reset when listbox.String is updated. I believe MATLAB redraws the listbox whenever the String field is updated, as it doesn’t provide a ‘addElement’ method. I can manually use ‘setFixedCellHeight’ in callbacks following an update of the elements of the listbox, but the resize down to default and the resize back to the fixed height is clearly visible as a flicker, I am wondering if you have any suggestions in fixing this problem?
I tried using the addElement method of the DefaultListModel used in the listbox, but the ListModel is tied to updates in listbox.String field, rather than the other way around.
Many thanks,
Alex
There is no simple solution. I think that if you call setFixedCellHeight immediately after updating the String property, without an intervening drawnow or repaint, then there should be no flicker. The flicker indicates that one or both of drawnow or repaint are being called between these two actions.
Hi Yair,
Thanks for your response. Annoyingly, and somewhat interestingly, a String update immediately followed by a setFixedCellHeight means that the listbox does NOT update its height at all, if you call getFixedCellHeight the value is still the default MATLAB value. I am not sure why this is.
One way to I found reduce to the flicker is adding a pause(0.01) before setFixedCellHeight call, which does allow the cell height to be set, but it isn’t much of a solution.
Alex – the reason is probably that when updating the String property, the Matlab engine places a call to setFixedCellHeight (or some other method that eventually sets the cell height) on the Event Dispatch (EDT queue), and this gets executed in a separate thread asynchronously, a few millisecs later. So if you directly call setFixedCellHeight after setting String, your call is being overridden by the EDT call a few millisecs later and only then the listbox gets repainted, so in the end you see no visible change.
When you add a pause(0.01) before setFixedCellHeight, the EDT call gets executed before your direct call, and so this does produce the requested effect, but due to the intervening listbox repaint you will necessarily see a flicker.