Last week I presented an article explaining how to solve an issue with deployed (compiled) Matlab applications. Today I’d like to welcome guest blogger Alexander Mering, who will explain how to disable standard Matlab menu items in deployed docked Matlab figures. Alexander has been an avid follower of this blog, and from his CSSM posts we can tell that he’s been heavily using advanced GUI features presented in this blog. His article today nicely shows how we can use different building-blocks presented in different articles in this blog, to achieve something new and useful.
As Yair pointed out in many occasions, the power of Matlab could be greatly enhanced using the underlying Java mechanism. To me, while developing a larger standalone tool for technical calculations, these hints are worth a mint (as I guess for many of you).
One of these very useful hints is the ability to dock figure windows in standalone applications. This perfectly fits to my understanding of a “clean desktop”, i.e., having as less as possible separate windows. Since in many calculations dozens of figures are generated, the desktop gets up crowded very fast – if these are not grouped. So docking is essential (at least for me). Unfortunately there seems to be a serious problem with the resulting menu entries (at least in R2011b on Win XP), leading to a crash of the standalone application. Based on the posts by Yair, I will sketch a possible route to avoid this issue.
The symptom
In the compiled application, docking could be accomplished by accessing the figure frame’s underlying Java level:
% get java frame for sophisticated modifications jframe = get(handle(figure_handle), 'JavaFrame'); % allow docking jframe.fHG1Client.setClientDockable(true) |
Using this modification, the user is now allowed to dock and undock the figures manually. For initial docking of the figure,
javaFrame.fHG1Client.setClientWindowStyle(true,false) |
could be used during figure creation. Unfortunately, there are menu entries in the Figures container which are either unwanted (since not usable) or even directly crash the standalone applications:
Since crashing menu entries will be found and used by end-users (though these are somehow hidden), these prohibit the usage of the docking feature as long as these could be invoked. So how can we disable / remove these menu items?
The unsuccessful solution
Unfortunately, the straight forward solution of getting the handle to the Figures containers’ menu bar and remove the unwanted items does not work. The reason for this is the (to me unexpected behavior) that the menu bar seems to be rebuilt whenever a figure is docked/undocked.
This is actually the same behavior that automatically rebuilds the Editor’s menu bar whenever an editor file is added/removed. The Editor container is basically the same docking container as the Figures container, as shown by Yair’s setFigDockGroup utility:
Therefore, removing unwanted menu items only helps until the next figure docking/undocking. To make it even worse: also pressing any of the buttons within the document bar (if having more than one figure) somehow rebuilds the entire menu structure, reverting our changes. So the solution becomes a bit more complex.
The working solution
For the working solution, many pieces presented by Yair should be put together. The first piece results from the question how to detect a dock/undock event. Since no such callback is defined, we need to use a property listener as Yair showed in his post about the continuous slider callback:
% listen to the WindowStyle property to detect docking / undocking events hProp = findprop(handle(figure_handle),'WindowStyle'); % a schema.prop object % if the event occurs, invoke the callback hlistener = handle.listener(handle(figure_handle), hProp, 'PropertyPostSet',{@(source, event) Callback_DockingFcn}); % attach listener to the GUI since it needs to be known (as long as the figure exists) setappdata(figure_handle, 'Handle_Listener', hlistener); |
Now, whenever the figure’s WindowStyle property (which controls the docking state) is changed, our docking callback is invoked.
The next piece of the puzzle takes care of the menu rebuild whenever any document bar button is pressed. To overcome this behavior, the idea is to define the MousePressed callback of theses buttons to (again) invoke the docking callback. This is necessary for two reasons: First, pressing the button (i.e., changing the current figure) rebuilds the menu, overwriting our changed menu entries. Secondly, all other buttons are also somehow rebuilt and the callbacks are removed if a new figure is docked.
The handles to the document bar buttons could be found using Yair’s findjobj utility. We have already seen that the Editor container is analogous to the Figures docking container. So let’s use the method described by Yair for accessing the Editor container, to access the Figures container:
figures_container = javaObjectEDT(matlab_instance.getGroupContainer('Figures')); figures_frame = javaObjectEDT(figures_container.getTopLevelAncestor); |
Once we get the Java Frame for the Figures container, the buttons could be found by digging through its children. This finally allows to set the callback using
DTDocumentBar = javaObjectEDT(figures_frame.getRootPane.getLayeredPane.getComponent(1).getComponent(1).getComponent(0).getComponent(0).getComponent(1).getComponent(0)); ContentPanel = javaObjectEDT(DTDocumentBar.getComponent(0).getComponent(0).getViewport.getView); if ~isempty(ContentPanel.getComponents) % less than two documents are open and no DTDocumentbar exists drawnow; pause(0.05) GroupPanel = javaObjectEDT(ContentPanel.getComponent(0)); GroupPanel_Elements = javaObjectEDT(GroupPanel.getComponents); % change the MousePressed Callback for each of the buttons to invoke the function which disables the menu for n = 1 : GroupPanel.getComponentCount thisElement = GroupPanel_Elements(n); if isequal(char(thisElement.getClass.toString), 'class com.mathworks.widgets.desk.DTDocumentBar$DocumentButton') set(handle(thisElement, 'CallbackProperties'), 'MousePressedCallback', {@(source, event) Cbrake_Callback_Diagrams_DockingFcn}) end end drawnow; pause(0.05) end |
where the loop runs through the current buttons in the document bar.
As the last step of our procedure, we finally remove (or disable) the menu entries which are unwanted. This is achieved by extracting the handle to the Figures menu by:
figures_menu = javaObjectEDT(figures_frame.getJMenuBar); |
Running through the menu items, searching for the unwanted entries (as long as they have pre-defined menu-item names) at the end sets us into the position to take care of the menu items:
% run through top-level menu items for n = 1 : figures_menu.getMenuCount % completely deactivate Debugging options if isequal(char(figures_menu.getMenu(n-1).getName), 'DesktopDebugMenu') DesktopDebugMenuPos = n - 1; end % Remove some items from the Desktop menu if isequal(char(figures_menu.getMenu(n-1).getName), 'DesktopMenu') desktop_menu = javaObjectEDT(figures_menu.getMenu(n-1)); DeletePos = []; for m = 1: desktop_menu.getMenuComponentCount if ismember({char(desktop_menu.getMenuComponent(m-1).getName)}, ... {'ToggleFigure PaletteCheckBoxMenuItem', 'TogglePlot BrowserCheckBoxMenuItem', 'ToggleProperty EditorCheckBoxMenuItem'}) DeletePos(end+1) = m - 1; end end for m = length(DeletePos) : -1 : 1 desktop_menu.remove(DeletePos(m)) end end end % finally remove the "Debug" menu if ~isempty(DesktopDebugMenuPos) figures_menu.remove(DesktopDebugMenuPos) end |
Since this callback is invoked whenever a figure is docked/undocked, or the currently shown figure is changed (by pressing the document bar button), all unwanted menu items within the Figures menu could be removed.
As a result, the new Figures container menu looks like:
Remarks
I must admit that above solution is still imperfect. For instance, sometimes there is a larger delay between the docking (or button press event) and the removing of the menu item. Nevertheless, this solution allows me to distribute my standalone with docked figures without having menu items directly leading to a fatal error.
Obviously, the solution has some positive side effects:
- As could be seen from the screen shot, the Matlab desktop becomes available also within your compiled applications. This might be wanted. If not, it could be removed the same way as the other menu items. One drawback of making the desktop available should be mentioned: In my tests, the standalone Matlab desktop shows the whole list of recent files I have in the Matlab editor at compile time. This is somehow ugly but not that problematic.
- Additional menu items could be added, giving more possibilities for modifications.
I have uploaded a first version of the docking and creation functions, together with a small test project, to the Matlab file Exchange. Readers are welcome to download the code and send me improvement suggestions. Or you could simply leave a comment below.
As Aurelien just pointed out on the FEX, I do have a wrong definition of the MousePressedCallback. I overlooked it when extracting it from my project. So,
should be changed to
I already made an update to the FEX.
Hi,
In the Callback_DockingFcn of yours, I am getting error in the line.
Can you tell me whats wrong?
Thanks.
Raja
@Raja – as you can see from this complex line, it highly depends on the exact container hierarchy and so it is not surprising that it may be slightly different on a different platform and/or Matlab release. You will need to carefully look at the various elements to check how to modify it for your particular needs. A good way to see this is to run the following code in your Matlab command prompt after the figure is displayed:
You will see the hierarchy of the sub-containers and you can easily navigate them via .getComponent(index). Just remember that index 0 means the first child, 1 means the second etc.
If you need professional help, consider employing my consulting services. Use the email link at the top of this page.
Hi Yair,
It is taking a long time to execute this command, any idea?
Thanks.
Raja
Hi Yair,
I figured it out using your suggestions.
I am still fixing some issues, but I get the general Idea.
Thanks!
Raja
Hi Yair,
There is one little issue. When docking and undocking, the menu comes back. Is it possible to add a listener to the ComponentCount Property of the Menu Bar?
Thanks.
Raja
@Raja – try using ComponentAddedCallback or ComponentRemovedCallback. See here.
Instead of
one could use the great findjobj:
or for Matlab >=R2012a:
You can also view the elements structure by using findjobj without outputs.
hi
I’m having a similar yet different issue.
on my deployed application the user can generate plots(figures).
The figures which are generated on the compiled application has only ‘File’ in the MenueBar and does not have all the options on the ToolBar. The one which is mostly required is the ‘arrow’, with which the user can choose part of the figure such as the legend window, title, axes and edit them and their properties.
Obviously, when running on matlab the figure has all its tools.
I’m looking for a way to display and enable menu/tool entries in deployed figure
@Haya – the interactive plot editing functionality is not enabled in deployed application. You will need to do this programmatically in your code if you need it in the deployed app.
@Yair -first, thank you for your answer
This is precisely what i’m asking, I can I do this programmatically?
I compiled my code with MATLAB and I it as an .exe process from my .Net application.
The user can generate figures from the MATLAB .exe which is hosted in my .Net app, but once the figure opens it is missing the ‘Exploration.Pan’ option in the tools bar and also all option in the menu bar but the ‘File’
Any idea how to do this programmatically?
@Haya – you did not understand. The plot-editing functionality does not exist in deployed apps. You cannot simply restore the menu item because the functionality itself does not exist. You will need to program the entire functionality yourself if you wish (yes of course it is very difficult, quite possibly not worth all the effort).
@Yair thank you again for your answer.
I have no idea how to and from where to start this kind of code and unfortunately I must have the plot-edit functionality on my deployed app. If you have any idea how I can get started I’ll be happy to hear.
Thank you again
haya
Thanks for that.