- ROI object handles are saved as a private (or public) property of the app.
- Every ROI object is assigned a unique tag name.
- The context menu is assigned to every ROI object.
- A listener is added to each ROI object and responds to a button click (right or left mouse).
- When the ROI is clicked, it updates the context menu tag to match the tag of the clicked ROI.
- When the cxMenuSelectionFcn is evoked by a right-button click, the function uses the recently updated context menu tag and matches it with all ROI tags to identify what object evokes the menu.
Handle of UI component from which a context menu has been opened
20 ビュー (過去 30 日間)
古いコメントを表示
Dear all,
I spent the last hour looking for a solution to this problem but I've not found anything useful. I have a very simple problem, and probably the solution is easy as well: I have a context menu shared among several UI components. How can I get an handle of the object for which the context menu has been opened? I can't use different context menus because the UI components are created interactively.
EDIT: I try to explain a little bit better my issue. I have a GUI created with App Designer. The GUI shows an image on which the user can draw several ROIs (line, boxes, etc.). The same context menu is attached on each ROI and contains two items only: rename and delete object. My point is: when I click on "rename" how can I get the handle of the element from which the context menu has been opened? I tried to setup the "ContextMenuOpeningFcn" but it doesn't work (the function is not called when the menu is opened).
Thank you for your help!
0 件のコメント
採用された回答
Adam Danz
2020 年 10 月 7 日
編集済み: Adam Danz
2020 年 10 月 7 日
Neither the cxMenuSelectionFcn nor the ContextMenuOpeningFcn functions indicate the object that evoked the context menu.
Check out Adam's excellent wrapper function but you'll need to make two important modifications that I listed in the comments under his answer.
Using gco or gcbo are other useful alternatives but they do not recognize ROI objects.
After some thinking, I came up with another way to identify which ROI objects evoked the context menu explained below. But this was before discovering Adam's (the other Adam) answer suggested above. I'd try that one first.
Method
Demo
The cxMenuSelectionFcn merely changes the color of the ROI. The full app is attached. Here are the key parts.
The variable containing the ROI handles is a private (or public) property of the app.
properties (Access = private)
ROIhandles=gobjects(1); % Stores ROI handles, produced in startupFcn
end
The startupFcn creates the context menu, plots the ROI objects, and assigns the listeners to the ROI objects. This could be done in any callback function. Importantly, unique tags must be assigned to the ROI objects and the context menu must exist prior to assigning the listeners.
% Code that executes after component creation
function startupFcn(app)
% Create context menu
cm = uicontextmenu(app.UIFigure);
m1 = uimenu(cm,'Text','ChangeColor','MenuSelectedFcn',@(mn,ad)cxMenuSelectionFcn(app,mn,ad));
% Plot 3 ROIs
% Assign unique tags to each ROI.
hold(app.UIAxes,'on')
app.ROIhandles(1) = images.roi.Rectangle(app.UIAxes,'Position',[.1 .1 .2 .2],'Tag','R1');
app.ROIhandles(2) = images.roi.Rectangle(app.UIAxes,'Position',[.5 .5 .1 .2],'Tag','R2');
app.ROIhandles(3) = images.roi.Rectangle(app.UIAxes,'Position',[.1 .6 .2 .3],'Tag','R3');
% Assign context menu to all ROIs (or any object that has a unique tag)
% This must come after assigning tags and before adding listeners below.
set(app.ROIhandles, 'ContextMenu', cm)
% Add listeners to the ROI objects.
% The listening will respond when the ROI obj is clicked (right or left mouse button)
% When clicked, the context menu tag will be changed to match the ROI's tag. This will
% inform the context menu selection function which object evoked it.
for i = 1:numel(app.ROIhandles)
app.ROIhandles(i).UserData = listener(app.ROIhandles(i), 'ROIClicked', ...
@(~,~)set(app.ROIhandles(i).ContextMenu,'Tag', app.ROIhandles(i).Tag));
end
end
Define the cxMenuSelectionFcn function
This is a private (or public) method defined in the App. Two assert()'s are used to make sure a tag was successfully assigned to the context menu and that one and only one ROI object was matched to the tag. It then changes the color of the ROI object to some random color.
methods (Access = private)
function cxMenuSelectionFcn(app,menu,~)
% cxMenuSelectionFcn; responds to selecting "ChangeColor" in context menu
% The context menu is assigned to ROI objects.
% Get ContextMenu tag
cmtag = menu.Parent.Tag;
assert(~isempty(cmtag),'Failure to assign tag to context menu.')
% get handle associated with tag
handleIdx = strcmp({app.ROIhandles.Tag}, cmtag);
assert(sum(handleIdx)==1, 'Failture to identify ROI.')
hRoi = app.ROIhandles(handleIdx);
% Change color of ROI
hRoi.Color = rand(1,3);
end
end
4 件のコメント
その他の回答 (2 件)
Jasmine Poppick
2023 年 9 月 20 日
Starting in R2023b, the MenuSelectedFcn and ContextMenuOpeningFcn callback event data contains the UI component that was right-clicked to open the context menu, stored in the ContextObject property.
For example, if you run this code and open the context menu on the button, the callback displays the Button object.
fig = uifigure;
cm = uicontextmenu(fig,ContextMenuOpeningFcn=@(src,event)disp(event.ContextObject));
m = uimenu(cm,Text="Menu");
btn = uibutton(fig,ContextMenu=cm);
0 件のコメント
Paola De Cecco
2023 年 1 月 23 日
One alternative could be to set UserData on all the context menus to the event Source parent.CurrentObject.
then you can read from UserData of each of the context menu, the handle of the uicontrol that created the context menu.
function ContextMenuOpening(app, event)
disp('can I get the menu owner here?')
L=length(event.Source.Children)
for i=1:L
set(event.Source.Children(i),'UserData',event.Source.Parent.CurrentObject);
end
end
0 件のコメント
参考
カテゴリ
Help Center および File Exchange で Migrate GUIDE Apps についてさらに検索
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!