Common javacomponent problems

The javacomponent function, which I described here last year, is a very important built-in Matlab function that enables placing Java components in Matlab figure GUI. Using javacomponent is pretty straight-forward. However, there are a few quirks that users should be aware of. In today’s article I’ll try to highlight some of them and discuss workarounds.

Figure visibility

Java components can only be placed onscreen when their containing Matlab figure has been made visible. This means that calls to javacomponent cannot be placed at the GUIDE-created *_OpeningFcn() function, because that function is invoked before the figure window is made visible.

Of course, we can always force the figure to become visible by setting the figure’s Visible property to 'on' in this function, before calling our javacomponents. But IMHO, a better option would be to simply place the javacomponents in the corresponding GUIDE-created *_OutputFcn() function, which is invoked after the figure window is made visible.

Auto-hiding with parent container

Java components are not automatically hidden with their ancestor container panel. This is also the root cause of the failure of Java components to disappear when switching tabs in a uitab.

One simple workaround for this that I often use is to link the Visible properties of the javacomponent container and the parent container:

jButton = javax.swing.JButton('click me!');
[jhButton, hContainer] = javacomponent(jButton, [100,100,60,30], hParent);
setappdata(hParent, 'linked_props__', linkprop([hParent,hContainer],'Visible'));

This has indeed been fixed in R2010b. If you ask me, this should have been standard behavior of javacomponent since the very beginning…

Although there is no need for the workaround in R2010b onward, I usually keep the workaround because it doesn’t hurt and enables backward compatibility for users who may have an older Matlab release.

Possible parent container types

javacomponent accepts parent handles that are figures, toolbars, uipanels, or uicontainers (some of these are not documented as possible parents in some Matlab releases, but they are). Since R2008a, parents of type uisplittool and uitogglesplittool can also be used. Unfortunately, frames are not uicontainers and, therefore, cannot be used as javacomponent parents.

Note: Due to a bug in R2007a, javacomponents cannot be added to uicontainers, since javacomponent.m checks if isa(hParent,'uicontainer') (and similarly for 'uiflowcontainer', 'uigridcontainer'), instead of isa(hParent,'hg.uicontainer') (and similarly for the others). If we modify javacomponent.m accordingly (add “hg.” in lines 98-100), this bug will be fixed. Since R2007b, isa(…,'hg.uicontainer') is equivalent to isa(…,'uicontainer'), so this fix is unnecessary.

Input parameters

Unlike many other Matlab functions, javacomponent does not accept optional parameter-value (P-V) pairs. If we want to customize the appearance of the Java component, it is better to create it , customize it, and only then to present it onscreen using javacomponent. If we first present the component and then customize it, there might be all sorts of undesirable flicker effects while the component is changing its properties.

One of the parameters that unfortunately cannot be customized before calling javacomponent is the component’s position onscreen. javacomponent only accepts a position vector in pixel units. To modify the component to use normalized units, we need to modify the container’s properties:

jButton = javax.swing.JButton('click me!');
[jhButton, hContainer] = javacomponent(jButton, [100,100,60,30], gcf);
set(hContainer, 'Units','norm');

Similarly, we can set the container’s UserData and ApplicationData only after the call to javacomponent.

Background color

The default background color of javacomponents is a slightly different shade of gray than the default uicontrol background color. Please refer to my recent article for a detailed discussion of this issue.

Vertical alignment

Java components are slightly mis-aligned vertically with combo-box (Style=’popup’) uicontrols, even when positioned using the same Y position. This is actually due to an apparent bug in Matlab’s implementation of the combo-box uicontrol, and not in the Java component’s: Apparently, the Matlab control does not obey its specified height and uses some other default height.

If we place javacomponents side-by-side with a regular Matlab popup uicontrols and give them all the same Y position, we can see this mis-alignment. It is only a few pixels, but the effect is visible and disturbing. To fix it, we need to add a small offset to the javacomponent‘s container’s Position property to make both the initial Y position slightly lower, and the height value slightly higher than the values for the corresponding Matlab combo-box control.

Callbacks

Unlike Matlab uicontrol callbacks, Java callbacks are activated even when the affected value does not change. Therefore, setting a value in the component’s callback could well cause an infinite loop of invoked callbacks.

Matlab programmers often use the practice of modifying component value within the callback function, as a means of fixing user-entered values to be within a certain data range, or in order to format the displayed value. This is relatively harmless in Matlab (if done correctly) since updating a Matlab uicontrol‘s value to the current value does not retrigger the callback. But since this is not the case with Java callbacks, users should beware not to use the same practice there. In cases where this cannot be avoided, users should at least implement some sort of callback re-entrancy prevention logic.

EDT

Java components typically need to use the independent Java Event processing Thread (EDT). EDT is very important for Matlab GUI, as explained in this article. Failure to use EDT properly in Matlab can lead to unexpected GUI behavior and even Matlab hangs or crashes.

If the javacomponent function is called in a very specific syntax format where the first input arg is a string (the name of the Java class to be created), then the newly-created component is placed on the EDT. However, this is not the normal manner in which javacomponent is used: A much more typical use-case is where javacomponent is passed a reference handle to a previously-created Java component. In such cases, it is the user’s responsibility to place the component on the EDT. Until R2008a this should be done using the awtcreate function; since R2008b, we can use the much simpler javaObjectEDT function (javaObjectEDT actually existed since R2008a, but was buggy on that release so I suggest using it only starting in R2008b; it became documented starting in R2009a). In fact, modern javacomponent saves us the trouble, by automatically using javaObjectEDT to auto-delegate the component onto the EDT, even if we happen to have created it on the MT.

The paragraph above may lead us to believe that we only need to worry about EDT in R2008a and earlier. Unfortunately, this is not the case. Modern GUI requires using many sub-components, that are attached to their main component (e.g., CellRenderers and CellEditors). javacomponent only bothers to place the main component on the EDT – not any of the sub-components. We need to handle these separately. Liberally auto-delegating components to the EDT seems like a safe and painless habit to have.

Categories: GUI, Java, Medium risk of breaking in future versions, Semi-documented function

Tags: , , , ,

Bookmark and SharePrint Print

28 Responses to Common javacomponent problems

  1. Jan says:

    Is there any way to access handles object in a Java callback? I’d like to mix java GUI controls with Matlab components which means that I need to have access to handles object with java callbacks.

    • @Jan – of course – you can either pass the handles structure as a third parameter to the callback. For example:

      hjButton = handle(jButton,'CallbackProperties');
      set(hjButton,'ActionPerformedCallback',{@myCallbackFcn,handles});

      and then define the callback as follows:

      function myCallbackFcn(hjButton, eventData, handles)
         ... % do something useful with <i>handles</i>
      end

      Alternately, you can use the use the guihandles function within the callback.

  2. Manuel S. says:

    Hello. I’m having problems when I try to display a plot in a “axes” in guide, calling from a java button, it shows me figure out a guide, it is a problem with the javacomponent?, because the button works well and calling the appropriate function.
    My calling code is almost the same as the answer you gave above.

    • @Manuel, without knowing your code it is impossible to know the exact problem, but it sounds to me more of a Matlab callback issue than a Java one.

    • Manuel S. says:

      Thanks for responding Yair. I show my code to create the button:

      P_out = uicontrol('Style', 'PushButton', 'units', 'normalized');
      jButton = java(findjobj(P_out));
      set(P_out, 'position', [.15 .01 .08 .08]);
      hjButton = handle(jButton,'CallbackProperties');
      set(hjButton,'ActionPerformedCallback',{@Play_Callback,handles});

      And this on the calling function, but instead of placing the images in the “axes3” opens a new figure and places the images in that figure while the axes nothing appears.

      function Play_Callback(hjButton,eventdata, handles)
      f=ones(1500,1500,3); axes(handles.axes3);h=imshow(f);
      ...something useful...

      Thank you very much for your help.!

    • Manuel S. says:

      the thing is that when I use a normal button “guide” everything works perfectly.
      Thank you again.

  3. sebbo says:

    Hi,

    I’m curious about this position issue of components used with javacomponent.
    I’ve observed that the displacement of java-components is not always the same.

    I e.g. had to shift an uitree-component to the right, while other components seem to need a shift to the left.
    I wonder whether this might have to do with the parent, e.g. whether the control is embeeded in a tab/panel etc.

    Is there a possibility to reconstruct this bug to provide a general solution?

  4. Bluesmaster says:

    Hello,

    I got a color´picker: com.mathworks.mlwidgets.graphics.ColorPicker

    and placed it into a uipanel via javacomponent( … , mypanel)

    Unfortunately the picker and its wrapper stay in the original figure
    when my panel switches the figure (undocking). What can i do?

    ( the panel itself is placed into another one and so on. I cannot use a UDD- ParentChanged listener directly)

    best regards

    Bluesmaster

    • @Bluemaster – you can reparent the javacomponent’s container, just like the panel. I don’t think this is done automatically.

    • Bluesmaster says:

      Thats possible ( even its very strange > reparent to new figure > to old figure > to new figure > to original panel ..then it works)

      But…the problem is WHEN to do this?

      It is a modul. The modul doesnt know anything about its enviroment.
      Can a panel listen to when its ancestor figure changed ? ( not its parent! which again can be a panel and so on…)

      Thank you

    • Yair Altman says:

      @Bluemaster – you cannot do this with a uipanel since it’s not a direct Java component. But reparenting uipanels requires a programmatic action and you can do it there, next to the actions. If you want asynchronous updates then you can use a small real Java component and listen to its hierarchy-change events. If you’d like me to show you the code for this, contact me offline (via email) and I’ll help you (for a small consulting fee).

  5. Is it possible to tie a java callback function (like java.awt.event.ActionListener) to a Matlab function?
    I am using a third party LookAndFeel with custom components (http://weblookandfeel.com) and I’d like to use them in a Matlab GUI to trigger plotting events.
    For example, I’d like to use a webswitch:

    webSwitch = javaObjectEDT('com.alee.extended.button.WebSwitch');
    javaObjectEDT('setLeftComponent',javaObjectEDT('com.alee.laf.label.WebLabel','on'));
    javaObjectEDT('setRightComponent',javaObjectEDT('com.alee.laf.label.WebLabel','off'));
    webSwitch = javacomponent(webSwitch,[10 10 50 20],gcf);
    % Tie callback to webSwitch here somehow

    However, the only callback method a webswitch has is addActionListener that takes a java.awt.event.ActionListener object.

  6. A says:

    I would like to know how to change my javacomponent font size.

    Eg

    set(jbutton,'FontSize','30');

    OR

    set(hbutton,'FontSize','30');
  7. A says:

    If it would help…

    I get -> The name ‘FontSize’ is not an accessible property for an instance of class …..

  8. Amir G. says:

    Hi Yair,

    I’ve looked deeper inside “uicomponent” function and I think I found a bug which related to the “hiding” problem.
    Inside “uicomponent” there is a function named “getParent” which suppose to check if parent is valid (figure / uipanel) and return the parent handle.
    but the following lines in the end of the function overwrites the actual parent that was found, so parent is always the main figure so the javacomponent isn’t auto hiding with tabs i.e.

            position = position + [parentPosition(1:2),0,0];  % pixel position relative to figure
            parent = ancestor(parent,'figure');

    I think the problem in line 470:

    elseif ~any(strcmpi(regexprep(class(hParent),'.*\.',''),{'figure','uicontainer','uiflowcontainer','uigridcontainer'}))

    should be:

    elseif ~any(strcmpi(regexprep(class(hParent),'.*\.',''),{'figure','uicontainer','uiflowcontainer','uigridcontainer','panel'}))

    what do you think?

    • @Amir – when uicomponent was first created 8 years ago (April 2007), I do not think that panels were accepted as javacomponent parents, so the code was probably correct for the Matlab release at that time. I’m not sure when exactly panels were added as legitimate javacomponent parents, but since that release your fix is good, except that it should also test for 'uipanel' (HG1), not just for 'panel' (HG2).

    • …and I would also add 'tab' and 'uitab' to this list, by the way…

    • Amir G. says:

      @Yair – the ‘regexprep’ function returns ‘panel’ string for the ‘uipanel’ class. so when I write ‘panel’ I actually mean ‘uipanel’.
      But I agree it should be tested for all possible use cases that was added since April-2007 release.

      I’ll try doing so and update, so ‘uicomponent’ source in Mathworks can be updated/expanded for new versions of matlab.

      Thanks

    • The version that I uploaded to FEX yesterday included panel, uipanel, tab and uitab as newly acceptable parents.

  9. Melec says:

    Hi,

    I am trying to add a JTable (which has a number of rows that can change) into a Matlab interface. To do so I put the JTable into a JScrollPane which I draw in my interface through the javacomponent function. The problem is that I can’t find a way to get the right size to assign in the javacomponent function.

    I tried to use the preferrable size of the jtable or the preferrable size of the jscrolpane but it is never perfect. Either the table doesn’t fit the all jscrollpane and there is a small band below the table or the jscrollpane is too small.

    Then I tried to put the jscrollpane into a JPanel which fixed the table to fit the jscrollpane size. But then the panel is bigger than the table which is OK but its margins change according to the number of rows.

    Does it exist a clean way to get the right size to apply in the javacomponent function?

    • @Melec – you could try to use normalized units in the container handle that is returned as the second output arg by javacomponent

    • Melec says:

      @Yair – I tried youר solution but it set the jpanel to the size of the matlab container and I want the jpanel to fit the jscrollpane.
      I found a way to force the jpanel to fit to the jscrollpane :

      jpanel.setSize(jscrollpane.getSize)

      but it only last for some time and then get back automatically to the previous state. Do you know why?

    • Melec says:

      @Yair – I noticed it get back to the previous state after some time or as soon as I put my cursor on the toolbar.

Leave a Reply


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