Assigning a context menu to a UIAxes

I am using appdesigner with Matlab 2020b.
I have a UIAxes in the main window, and I would like to assign a context menu to that axis.
I have implemented the context menu, and I can attach it to the 'panel' that contains the UIAxes. The axis functions correctly when I right-click on the panel around the UIAxes.
But I cannot attach the context menu to the UIAxes itself. The menu only appears when I right-click on the panel, not on the UIAxes.
The context menu is assigned to the UIAxes (see attached PNG file).
Brian Wandell
wandell@stanford.edu
(lmperry@stanford.edu is the name of my lab manager. I would appreciate if you could make sure the message is also sent to my account).

 Accepted Answer

Hello Brian,
You can attach Context Menu component to the UIAxes component by drag & drop, from the Component Browser, or from the Context Menus (if it's already made).
If you're creating menus programmatically, then you can assign them to UIAxes by ContextMenu property.

8 Comments

I'm working on the project with Brian, and our problem is that even when we assign them, they don't appear at run time. -- David
Mario Malic
Mario Malic on 7 Nov 2020
Edited: Mario Malic on 7 Nov 2020
Note in the second link from the answer.
If the PickableParts property is set to 'none' or if the HitTest property is set to 'off', then the context menu does not appear.
Here's a demo app that has context menu on Panel and on UIAxes.
Good suggestion, thanks. Unfortunately for the app.axes2 (the relevant axes) in our
HitTest: on
PickableParts: 'visible'
All properties listed below. Anything else we might have set improperly? I will run your demo app to (a) make sure it runs on my system, and (b) to compare the axes settings.
Brian
K>> app.axes2
ans =
UIAxes (Absorptions per integration time) with properties:
XLim: [0.5000 149.5000]
YLim: [0.5000 149.5000]
XScale: 'linear'
YScale: 'linear'
GridLineStyle: '-'
Position: [10 11 379 355]
Units: 'pixels'
Show all properties
ALim: [0 1]
ALimMode: 'auto'
AlphaScale: 'linear'
Alphamap: [1×64 double]
AmbientLightColor: [1 1 1]
BeingDeleted: off
Box: on
BoxStyle: 'back'
BusyAction: 'queue'
ButtonDownFcn: ''
CLim: [3.1000 55.2800]
CLimMode: 'auto'
CameraPosition: [75 75 1.0536e+03]
CameraPositionMode: 'auto'
CameraTarget: [75 75 0]
CameraTargetMode: 'auto'
CameraUpVector: [0 -1 0]
CameraUpVectorMode: 'auto'
CameraViewAngle: 8.0890
CameraViewAngleMode: 'auto'
Children: [5×1 Graphics]
Clipping: on
ClippingStyle: '3dbox'
Color: [1 1 1]
ColorOrder: [6×3 double]
ColorOrderIndex: 1
ColorScale: 'linear'
Colormap: [64×3 double]
ContextMenu: [1×1 ContextMenu]
CreateFcn: ''
CurrentPoint: [2×3 double]
DataAspectRatio: [1 1 1]
DataAspectRatioMode: 'manual'
DeleteFcn: ''
FontAngle: 'normal'
FontName: 'Georgia'
FontSize: 14
FontSizeMode: 'manual'
FontSmoothing: on
FontUnits: 'pixels'
FontWeight: 'normal'
GridAlpha: 0.1500
GridAlphaMode: 'auto'
GridColor: [0.1500 0.1500 0.1500]
GridColorMode: 'auto'
GridLineStyle: '-'
HandleVisibility: 'on'
HitTest: on
InnerPosition: [27.5500 31.5500 356.4500 311.6000]
Interactions: [1×1 matlab.graphics.interaction.interface.DefaultAxesInteractionSet]
Interruptible: on
LabelFontSizeMultiplier: 1.1000
Layer: 'top'
Layout: [0×0 matlab.ui.layout.LayoutOptions]
Legend: [0×0 GraphicsPlaceholder]
LineStyleOrder: '-'
LineStyleOrderIndex: 1
LineWidth: 0.5000
MinorGridAlpha: 0.2500
MinorGridAlphaMode: 'auto'
MinorGridColor: [0.1000 0.1000 0.1000]
MinorGridColorMode: 'auto'
MinorGridLineStyle: ':'
NextPlot: 'replace'
NextSeriesIndex: 1
OuterPosition: [10 11 379 355]
Parent: [1×1 Panel]
PickableParts: 'visible'
PlotBoxAspectRatio: [74.5000 74.5000 1]
PlotBoxAspectRatioMode: 'auto'
Position: [10 11 379 355]
PositionConstraint: 'outerposition'
Projection: 'orthographic'
Selected: off
SelectionHighlight: on
SortMethod: 'childorder'
Subtitle: [1×1 Text]
SubtitleFontWeight: 'normal'
Tag: ''
TickDir: 'in'
TickDirMode: 'auto'
TickLabelInterpreter: 'tex'
TickLength: [0.0100 0.0250]
TightInset: [17.5500 20.5500 0 22.8500]
Title: [1×1 Text]
TitleFontSizeMultiplier: 1.1000
TitleFontWeight: 'bold'
TitleHorizontalAlignment: 'center'
Toolbar: [1×1 AxesToolbar]
Type: 'axes'
Units: 'pixels'
UserData: [1×1 struct]
View: [0 90]
Visible: off
XAxis: [1×1 NumericRuler]
XAxisLocation: 'bottom'
XColor: [0.1500 0.1500 0.1500]
XColorMode: 'auto'
XDir: 'normal'
XGrid: off
XLabel: [1×1 Text]
XLim: [0.5000 149.5000]
XLimMode: 'auto'
XMinorGrid: off
XMinorTick: off
XScale: 'linear'
XTick: [20 40 60 80 100 120 140]
XTickLabel: {7×1 cell}
XTickLabelMode: 'auto'
XTickLabelRotation: 0
XTickMode: 'auto'
YAxis: [1×1 NumericRuler]
YAxisLocation: 'left'
YColor: [0.1500 0.1500 0.1500]
YColorMode: 'auto'
YDir: 'reverse'
YGrid: off
YLabel: [1×1 Text]
YLim: [0.5000 149.5000]
YLimMode: 'auto'
YMinorGrid: off
YMinorTick: off
YScale: 'linear'
YTick: [20 40 60 80 100 120 140]
YTickLabel: {7×1 cell}
YTickLabelMode: 'auto'
YTickLabelRotation: 0
YTickMode: 'auto'
ZAxis: [1×1 NumericRuler]
ZColor: [0.1500 0.1500 0.1500]
ZColorMode: 'auto'
ZDir: 'normal'
ZGrid: off
ZLabel: [1×1 Text]
ZLim: [-1 1]
ZLimMode: 'auto'
ZMinorGrid: off
ZMinorTick: off
ZScale: 'linear'
ZTick: [-1 0 1]
ZTickLabel: ''
ZTickLabelMode: 'auto'
ZTickLabelRotation: 0
ZTickMode: 'auto'
Mario Malic
Mario Malic on 7 Nov 2020
Edited: Mario Malic on 7 Nov 2020
Well, there's nothing left for the context menu to be unavailable. It's weird that it doesn't show up. If you could share part of the code to recreate the issue, it would be useful.
Thanks for sticking with this. I very much appreciate your paying attention.
Dave and I discovered this about the Demo_Brian app that you sent.
If you add an opening function to the main window, and then you load the UIAxes with data, the context menu fails.
% Code that executes after component creation
function Demo_Brian_Opening(app)
% Adding this line breaks the context menu. Commenting it out,
% preserves the proper behavior (good context menu).
imagesc(app.UIAxes, randi([0,255],150,150));
end
The problem appears to be with the implementation of the UIAxes. Loading the axes with data detaches the context menu, even in your very simple app.
Hmm.
Thanks again,
Brian
You can use WindowButtonDown callback, example taken from here.
if ~isempty(event.Source.CurrentObject) && isequal(event.Source.CurrentObject.Type,'image')
open(app.ContextMenu, 250, 250);
end
You can set a unique tag for the UIAxes if you intend to use context menu specifically just for that uiaxes.
I forgot to check whether context menu is opened with left mouse click, if yes, you can add SelectionType, 'alt' in the if statement in a similar way.
Mario -- Thanks again. We're getting closer, but I still run into an issue with the latest version. The WindowButtonDown callback fires correctly, the if statement works, and if the UIAxes is empty, the open() command shows the ContextMenu. But once I load an image into the UIAxes, I don't see the ContextMenu display. The open() command still executes, it just doesn't have any effect (at least none that I can see). Thanks, as always, for any help, or if there is something else we need to do! -- David (& Brian)
I noticed your app.UIAxes has 5 children, if they are different types, and lay on top of each other, than it might be a problem for us to select image object.
You can see in command window which object gets selected by writing this in WindowsButtonDown callback.
event.Source.CurrentObject
This might be the solution
function UIFigureWindowButtonDown(app, event)
%Create if statement that determines if the user clicked on the
%line of the top UIAxes. If they didn't, do nothing
event.Source.CurrentObject
if ~isempty(event.Source.CurrentObject) && isequal(event.Source.SelectionType, 'alt') % valid selection object&right click
if isequal(event.Source.CurrentObject.Type,'image') || isequal(event.Source.CurrentObject.Parent, 'app.UIAxes') % image obj or parent
open(app.ContextMenu, 250, 250);
end
end
end

Sign in to comment.

More Answers (2)

Thanks. Your suggestions have been very helpful. I evolved to a variant of your code that brings up the context menu only when clicking on the image window.
It would be nice to have fewer if() checks. And I have no idea what the numerical parameters to open(XXX, 250,250) do. But so far so good.
if isa(event.Source,'matlab.ui.Figure')
if isa(event.Source.CurrentObject,'matlab.graphics.primitive.Image')
if isequal(event.Source.CurrentObject.Type,'image')
% open(app.ContextMenu, 250, 250);
open(app.imageMenu,250, 250);
end
end
end
This work around is helpful. I consider the situation with the Matlab product, however, to be a bug. How about you?
Brian

1 Comment

If you click on an image, you would be clicking on one of the children which is most probably child of UIAxes.
if isa(event.Source,'matlab.ui.Figure') % this works for UIFigure and figure - unnecessary
if isa(event.Source.CurrentObject,'matlab.graphics.primitive.Image') % same as below?
if isequal(event.Source.CurrentObject.Type,'image') %
% open(app.ContextMenu, 250, 250);
open(app.imageMenu,250, 250);
You also need to implement a right click, see my last comment.
If you have only one axes that you want to do something with, you can try with only selection type.

Sign in to comment.

Thanks for the comments. But, when I clicked around the different panels and objects with only the 'Type' check, some event.Source cases did not include the 'CurrentObject' slot. That's why I walked down the tests term by term.
I will experiment some more, but this is now working. Again - your help is much appreciated.
For anyone else reading this - particularly someone from Mathworks - I consider the Matlab 2020b to have a bug with respect to attaching the context menu to the UIAxes. What is described here is a work-around.

Categories

Find more on Graphics Object Properties in Help Center and File Exchange

Products

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!