Last week I presented a detailed explanation of the uiinspect utility, which displays a GUI listing all the properties, callbacks and internal methods of an inspected object. Something like Matlab’s inspect tool on steroids. I explained that uiinspect uses com.mathworks.mlwidgets.inspector.PropertyView
, which in turn uses JIDE’s PropertyTable. PropertyView
automatically extracts the inspected object’s properties and displays them using a corresponding renderer in the PropertyTable
.
We often need to present a non-object Matlab construct. For example, it is very common to store an application’s configuration properties in a struct (using Matlab objects is often impractical and unwarranted) — it would be very useful to present this configuration dynamically, without having to programmatically scan all struct fields and build a dedicated GUI table. Unfortunately, PropertyView
cannot automatically retrospect Matlab structs, which is the reason that the built-in inspect function fails for such objects. In the general case, we may wish to present a PropertyTable
using row-specific renderers and editors, when we have such an un-inspectable object (struct, cell array etc.), or even for a dynamically-created table of properties that is not stored in any single object.
Another limitation of PropertyView
is that it immediately assigns updated property values to the inspected object, without allowing any sanity checks to be conducted on the new values, and without enabling the user to cancel property changes.
Last but not least, PropertyView
does not enable users to easily modify the way that certain properties are presented. Boolean properties (true/false) always use a checkbox cell-renderer/editor, while we may wish to display a string (on/off) or combo-box instead.
In short, PropertyView
is great unless you want to customize stuff, or do something slightly out-of-the-ordinary.
Enter propertiesGUI, the subject of today’s article.
The propertiesGUI utility
propertiesGUI, which can be downloaded from the Matlab File Exchange, presents a generic solution to this problem. propertiesGUI is based on Levente Hunyadi’s articles here back in 2010, and on his “Property grid” utility on the File Exchange.
propertiesGUI expects the following syntax:
[hPropsPane, parameters] = propertiesGUI(hParent, parameters) |
where:
hParent
(input) is an optional handle of a parent GUI container (figure/uipanel/uitab) in which the properties table will appear. If missing or empty or 0, the table will be shown in a new modal dialog window; otherwise it will be embedded in the parent container.parameters
(input) is an optional struct or object with data fields. The fields are processed separately to determine their corresponding cell renderer and editor. Ifparameters
is not specified, then the globaltest_data
will be used. Iftest_data
is also empty, then a demo of several different data types will be used.hPropsPane
(output) is the handle of the generated properties panel widget, which can be customized to display field descriptions, toolbar, etc.parameters
(output) is the resulting (possibly-updated) parameters struct. Naturally, this is only relevant in case of a modal dialog.
When passing the properties in an input parameters
struct, propertiesGUI automatically inspects each struct field and assigns a corresponding cell-editor with no description and a field label that reflects the field name. The properties are automatically set as modifiable (editable) and assigned a default callback function (propUpdatedCallback sub-function).
The global test_data
variable is updated internally when the <OK> button is clicked. It is meant to enable easy data passing between the properties GUI and other application component. Using global vars is generally discouraged as bad programming, but it simplifies GUI component interaction and has performance advantages. In this specific case I decided that the benefits of simplicity outweigh the pedagogical aspects. After all, propertiesGUI is meant to illustrate how to customize/use a properties pane, not to teach GUI programming best practices. In your real-world application you should consider using other information-sharing mechanisms (getappdata or guidata, for example).
Usage demo
For an illustration of propertiesGUI, simply run it without any parameters to display its demo (see screenshot above):
propertiesGUI; |
This creates (in the demoParameters sub-function) the following demo parameters
struct:
floating_point_property: 3.14159265358979 signed_integer_property: 12 unsigned_integer_property: 12 flag_property: 1 file_property: '' folder_property: 'C:\Yair' text_property: 'Sample text' fixed_choice_property: {'Yes' 'No' 'Maybe'} editable_choice_property: {'Yes' 'No' 'Maybe' ''} date_property: [1x1 java.util.Date] another_date_property: 734895.957829132 time_property: '22:59:16' password_property: '*****' IP_address_property: '10.20.30.40' my_category: [1x1 struct] color_property: [0.4 0.5 0.6] another_color_property: [1x1 java.awt.Color] |
A template for customization
propertiesGUI is meant to be used either as stand-alone, or as a template for customization. For example, we can attach a unique description to each property that will be shown in an internal sub-panel: see the customizePropertyPane and preparePropsList sub-functions.
We can have specific control over each property’s description, label, editability, cell-editor and callback function. See the preparePropsList sub-functions for some examples. Additional cell-editors/renderers can be added in the newProperty sub-function.
Specific control over the acceptable property values can be achieved by entering custom code into the checkProp sub-function. For example, checkProp already contains skeleton code to highlight missing mandatory field values in yellow, and non-editable properties in gray (see highlighted lines; the necessary modifications to make it work should be self-evident):
function isOk = checkProp(prop) isOk = true; oldWarn = warning('off','MATLAB:hg:JavaSetHGProperty'); warning off MATLAB:hg:PossibleDeprecatedJavaSetHGProperty propName = get(prop, 'UserData'); renderer = com.jidesoft.grid.CellRendererManager.getRenderer(get(prop,'Type'), get(prop,'EditorContext')); warning(oldWarn); mandatoryFields = {}; % TODO - add the mandatory field-names here if any(strcmpi(propName, mandatoryFields)) && isempty(get(prop,'Value')) propColor = java.awt.Color.yellow; isOk = false; elseif ~prop.isEditable %propColor = java.awt.Color.gray; propColor = renderer.getBackground(); else propColor = java.awt.Color.white; end renderer.setBackground(propColor); end % checkProp |
So if we wish to check the validity of the input or user updates, we simply need to enter the relevant checks into checkProp.
Additional control over the appearance and functionality can be achieved via the hPropsPane
reference that is returned by propertiesGUI.
As an example for such customizations, here is a variant of the properties pane, embedded within a uitab panel. Note the unique description of the properties, and the absence of the OK/Cancel buttons:
TODO
Even as a template for customization, propertiesGUI is still far from perfect. Some potential items on my TODO list for this utility include:
- Fix the renderer/editor for numeric and cell arrays
- Enable more control over appearance and functionality via input parameters (for example: display or hide the OK/Cancel buttons)
- Add additional built-in cell editors/renderers: time, slider, point, rectangle (=position), font, …
Do you find propertiesGUI useful in your work? If so then please share your experience in a comment below. Suggestions for improvement of the utility will also be gladly accepted.
Thanks!
Again a precious tool. I’m going to use it immediately
Dear Yair,
as soon as I try to deploy a stand-alone application including propertiesGUI.m
a message box appears with “unauthorized use of JIDE products” and consequetly “please contact sales@jide.com“.
The same messagebox is displayed in the deployed application.
Am I doing something wrong? Or, which package from JIDE would you suggest.
Many thanks,
Andrea
@Andrea – try calling
com.mathworks.mwswing.MJUtilities.initJIDE
before your call to propertiesGUI. I think this should be enough – let us know.Dear Yair,
I added the line you suggested, but it still provides the same message when compiling using my licensed R2008 version, the one I use to deploy applications. It appears also when compiling propertiesGUI.m alone
Message is here: https://www.dropbox.com/s/95by7tkzbxwhgfz/JIDE%20Message.png
No problems when compiling with R2011a.
Thanks again!
Andrea
Andrea, could you find a solution for this. It happens in my application also when we have changed to 2015b
So it seems hgjavacomponents aren’t the original instantiation, otherwise I’d be able to (with my button) add another Property to the the Properties array in the PropertyTableModel object and see the Pane update automatically… is that right?
(This is still great though)
N
@Nick – after you update the
PropertyTableModel
you need to refresh the actual data table.grid.update()
should do the trick. To get the reference togrid
in external code, simply modify line #1 of propertiesGUI.m to also return thegrid
variable.[…] names and values, which is the reason it is used by Matlab’s inspect function and my generic propertiesGUI utility or Levente Hunyadi’s Property Grid utility. But in this case we wish to display a general […]
I saw that you used eval in order to simplify the assignment of multiple sub-fields (like ‘dimension.width’) because data.(‘dimension.width’) fails. I took a crack at a minimalist recursive struct assignment function and this is what I came up with:
Looked at doing it with inline anonymous functions and it’s just messier than it’s worth in my opinion.
Or you could use the standard MATLAB function setfield, which is presumably more efficient. Assuming answer already exists and is a scalar struct and all of the nested structs are scalar, too, this would simply be
or if your field names are contained in a cell-array named fields
or if you insist on using a single string with fields delimited by periods
which could of course be wrapped in a separate function for convenience. The documentation of the setfield function (which can do even more) is a bit confusing on first sight, so maybe it is not instantly obvious that it can be used to create a nested struct. Equally, getfield provides easy access to values of nested structs.
@Martin – nice touch!
I am struggling for a while with an efficient retrieving of dynamic changing substructures from a string. According to my measurements, classical for loop combined with regexp is the fastest one. However, regexp seems to be a big bottleneck in this case.
With times:
Elapsed time is 0.036253 seconds.
Elapsed time is 0.031294 seconds.
Elapsed time is 0.046321 seconds.
Elapsed time is 0.018785 seconds.
so
getfield
is the slowest option here and classical “sub-looping” the fastest.Nevertheless, a faster replacement for
regexp
(since I am running version withoutstrsplit
) would be definitely nice.How to create a text dynamic under GUI/MATLAB
Thanks again
@DJamel – you need to be much clearer in your question
Hello
Do you have an idea how to display the results of the command
window on the GUI front in a scrolling fashion?
OR
Does a “dynamic text” box exists that allows for updates (i.e. a
returned value to be displayed to the GUI front)?
Thanks again
I’m not sure I understand how this relates to propertiesGUI…
Hi Yair
do you know a way to set a file filter when using a file type with
com.jidesoft.grid.FileCellEditor
?I would like for example to browse only for ‘*.exe’ files.
Same for dialog title.
I could find a solution for the File filter:
I run into an error, trying the demo of the current version on R2013b:
If I uncomment line 482, I run into this problem:
any idea?
@Kai – I assume that you are running Matlab without Java (probably some limited console mode or something like that). propertiesGUI uses Java components and so cannot possibly run in such circumstances.
Hi Yair,
This was not the problem.
The error was the class_object_property example: matlab.desktop.editor.getActive.
In my case it was a “1×0 Document array with properties…”.
The editor must be open for it to work.
@Kai – are you sure that you’re using the latest propertiesGUI version (24 Dec 2013)? I believe it was already solved long ago, maybe you are using an old version…
Yes, that’s the version:
% $Revision: 1.09 $ $Date: 2013/12/24 14:33:21 $
And matlab:
MATLAB Version: 8.2.0.701 (R2013b)
then this is odd because as you can see in the code, the
matlab.desktop.editor.*
calls are placed within try-catch blocks. Perhaps you mis-diagnosed the problem?Dear Yair,
I can confirm that this problem holds. Tested in R2012b & R2014a. The error message arises from calling propertiesGUI with no arguments (in “demo mode”). If I supply a MATLAB structure as an argument, it seems to work fine.
Hi Yair,
this is a very useful submission. But wouldn’t it be better if this was written in OOP, so one could easily overload a function (e.g. a callback)?
If I got this right, I would have to make a copy of the function for every (different) purpose.
Actually, it should be not a big thing to turn it into an object, right?
Best wishes,
Jan
@Jan – when I provide public utilities that are expected to work on old Matlab releases, before Matlab classes in their new format were introduced in R2008a, I have no option but to use standard procedural programming. This does not reflect bad programming practices or the inability to adapt to modern times, just a conscious decision to support multiple Matlab versions. I wish that other programmers were as conscious of backward compatibility as I try to be.
Feel free to adapt the code as you see fit, if you only use a new Matlab release and none of your users use an old release.
Ah, okay. I was not aware of that as I work on 2012b. This explains a lot!
Hi Yair, nice to see CHAMP being used as an example here 🙂
Problems arose after upgrading to 2014a and from talking to the tech support,
is problematic. According to them, “…..The ‘UserData‘ property has been removed from the ‘DefaultProperty’ Java class in R2014a and this is an undocumented internal MATLAB component. One way to work around this would be to use a property which is defined for both versions of MATLAB like ‘Description‘. You could use the SET and GET functions to set and query values….”
@Rainer – it’s good to hear from you again and thanks for the update!
Hi Yair,
very good work! keep it up!
Actually I find it very useful to have a CellEditor for numeric matrix or for mixed-type table-data (cell-array).
Therefor I would like to use your app “Java-based data table” (http://www.mathworks.com/matlabcentral/fileexchange/14225-java-based-data-table), that is very useful for table-data!
But how can I link this Table-GUI-widget to the propertiesGUI? As I have read, we have to create a custom CellEditor that inherits from a JIDE CellEditor?
Can you give me some information how to start with that?
Or are there any built-in CellEditors that can do this?
@MI – I will be very happy to help you integrate sophisticated table controls in your Matlab GUI, for a consulting fee. Please contact me via email (altmany at gmail).
I think I found a bug in
propertiesGUI
. If you have multiple field names that exist in differentstruct
s, all of those property fields are set to the same type as the last field. Here’s an example:The result: the first row, which should be an editable dropdown with choice ‘A’, ‘B’, or ‘C’ is now just a text box, and there’s no dropdown.
Also, some questions regarding using dates: is there a way to set a date property to empty? I’d like the box to be empty until the user fills it in the first time. Secondly, when you fill the date box you get something like “Aug 19, 2015”, but when the date selector is opened the format is dd/MM/yyyy. (I figured out how to make it yyyy-MM-dd for my own use). How do I switch the “Aug 19, 2015” view to “2015-08-19”? I’m using MATLAB R2014b.
Thanks a lot! This is an overall excellent function to use.
Hi
I want to thank from Mr Yair Altman due to its comments in GUI area. I have a question:
Is is possible to call Tow other GUI in a main GUI in the main window without opening a new window ?
My means is that i want to merge some different GUI in a main GUI with one window.
B. Regards