propertiesGUI

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.

propertiesGUI demo

propertiesGUI demo

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. If parameters is not specified, then the global test_data will be used. If test_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:

Properties panel Integrated within a GUI

Properties panel Integrated within a GUI

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.

Categories: GUI, Java, Medium risk of breaking in future versions, Undocumented feature

Tags: , , , ,

Bookmark and SharePrint Print

34 Responses to propertiesGUI

  1. Andrea Merlo says:

    Thanks!
    Again a precious tool. I’m going to use it immediately

  2. Andrea Merlo says:

    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

  3. Nick says:

    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 to grid in external code, simply modify line #1 of propertiesGUI.m to also return the grid variable.

  4. Pingback: treeTable | Undocumented Matlab

  5. Brett says:

    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:

    function s = multiStruct(field,s)
    % Function to recursively generate a struct from a string of field names
    %
    % field = 'life.universe.everything'
    % value = 42
    % answer = multiStruct(field,value)
    %
    % Same as answer.life.universe.everything = 42
     
        index=[0,strfind(field,'.')];    
        if ~isempty(field)
            s = multiStruct(field(1:index(end)-1), struct(field(index(end)+1:end),s));
        end
    end

    Looked at doing it with inline anonymous functions and it’s just messier than it’s worth in my opinion.

    • Martin says:

      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

      answer = setfield(answer, 'life', 'universe', 'everything', 42);

      or if your field names are contained in a cell-array named fields

      fields = {'life', 'universe', 'everything'};
      answer = setfield(answer, fields{:}, 42);

      or if you insist on using a single string with fields delimited by periods

      fields = 'life.universe.everything';
      fields = strsplit(fields, '.'); % only R2013a or higher, but more elegant
      %fields = regexp(fields, '\.', 'split'); % portable, but a bit ugly
      answer = setfield(answer, fields{:}, 42);

      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!

    • Ondrej says:

      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.

      string1 = 'one.two.three.four';
      A.one.two.three.four = 1;
       
      tic
      for n = 1:1000
         eval(['out1 = A.', string1, ';']);
      end
      toc
       
      tic
      for n = 1:1000
         out2 = eval(['A.', string1]);
      end
      toc
       
      tic
      for n = 1:1000
         reg1 = regexp(string1, '\.', 'split');
         out3 = getfield(A, reg1{:});
      end
      toc
       
      tic
      for n = 1:1000
         reg2 = regexp(string1, '\.', 'split');
         out4 = A.(reg2{1});
         for n = 2:length(reg1)
            out4 = out4.(reg2{n});
         end
      end
      toc

      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 without strsplit) would be definitely nice.

  6. DJAMEL says:

    How to create a text dynamic under GUI/MATLAB
    Thanks again

  7. DJAMEL says:

    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

  8. Thierry says:

    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.

    • Thierry says:

      I could find a solution for the File filter:

      editor = com.jidesoft.grid.FileCellEditor;      %('Select an .exe file','exe')
      ComboBox = editor.getComboBox;
      jfilefilter = javaObjectEDT('javax.swing.filechooser.FileNameExtensionFilter','Exe-Files (*.exe)','exe');
      ComboBox.setFileFilter(jfilefilter);
  9. Kai says:

    I run into an error, trying the demo of the current version on R2013b:

    >> propertiesGUI
    No method 'setValue' with matching signature found for class
    'com.jidesoft.grid.DefaultProperty'.
    Error in propertiesGUI>newProperty (line 482)
          prop.setValue(thisProp);
    Error in propertiesGUI>preparePropsList (line 421)
                  newProp = newProperty(parameters, field_name, field_label, isEditable,
                  type, field_description, @propUpdatedCallback);
    Error in propertiesGUI (line 154)
      propsList = preparePropsList(parameters, isEditable);

    If I uncomment line 482, I run into this problem:

    >> propertiesGUI
    No constructor 'javax.swing.SpinnerNumberModel' with matching signature found.
    Error in propertiesGUI>newProperty (line 503)
                                model = javax.swing.SpinnerNumberModel(prop.getValue,
                                -intmax, intmax, 1);
    Error in propertiesGUI>preparePropsList (line 421)
                  newProp = newProperty(parameters, field_name, field_label, isEditable,
                  type, field_description, @propUpdatedCallback);
    Error in propertiesGUI (line 154)
      propsList = preparePropsList(parameters, isEditable);

    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.

    • Kai says:

      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…

    • Kai says:

      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?

    • Aleksei says:

      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.

  10. Jan says:

    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.

    • Jan says:

      Ah, okay. I was not aware of that as I work on 2012b. This explains a lot!

  11. Rainer Ng says:

    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,

    set(hProp,'UserData',propName)

    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….”

  12. MI says:

    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).

  13. C. Chu says:

    I think I found a bug in propertiesGUI. If you have multiple field names that exist in different structs, all of those property fields are set to the same type as the last field. Here’s an example:

    props = struct;
    props.Info_A.Field_1 = {'A' 'B' 'C' ''};
    props.Info_B.Field_2 = {'1' '2' '3' ''};
    props.Info_B.Field_3 = {'X' 'Y' 'Z'};
    props.Info_C.Field_1 = '';
    propertiesGUI(figure, props);

    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.

  14. Ahmad says:

    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

Leave a Reply

Your email address will not be published. Required fields are marked *