Multiple objects sharing a context menu?

15 ビュー (過去 30 日間)
Niko
Niko 2015 年 8 月 3 日
編集済み: Rina Blomberg 2023 年 2 月 3 日
Hi all,
I'm trying to have multiple objects in the same figure share a common context menu. In the menu callback, is there a way to tell which one of the objects initiated the callback (i.e. was right-clicked on)? Or do I have to create a separate contextmenu object for each one of them?
Thanks!
Niko
  6 件のコメント
Niko
Niko 2015 年 8 月 21 日
Hi Adam, could you elaborate a little more on this? I know some MATLAB OOP but how do you get the object the object that was clicked on? Are you saying I should use WindowButtonDownFcn to detect mouse click and create a new instance, passing something like gco to it, rather than associating it with a fixed contextmenu using UIcontextmenu? Thanks!
Adam
Adam 2015 年 8 月 21 日
You can set the callback and when you do you can pass in other arguments than just those that come automatically. If your context menu is embedded in a class you only need to define the context menu once, but the class can also contain e.g. a line object handle or whatever else it needs to allow it to link together the context menu and the thing it is being attached to.
Then you would create and instance of your class for each context menu you want, each instance of the class would link to a different graphics object which it would then pass into the context menu's callback to allow you to get the context specific information.

サインインしてコメントする。

採用された回答

Adam
Adam 2016 年 2 月 18 日
編集済み: Adam 2016 年 2 月 18 日
This is far too late top be of use to the person asking the question I am sure, but today I had to solve this same problem myself, did an internet search and bizarrely landed here to find my own suggestion which I hadn't even considered when thinking about a solution to this before my search today. So I have now implemented my idea and since it is just a general wrapper type class without any proprietary code I will add my solution here.
It may be there is some simpler way that both myself and the original asker of the question both missed, but even if there is, I enjoyed creating my own solution anyway!
classdef GraphicsObjectWithContextMenu < handle
properties( SetAccess = immutable, GetAccess = private )
hGraphics = [];
hContextMenu = matlab.ui.container.ContextMenu.empty;
end
methods
function obj = GraphicsObjectWithContextMenu( hGraphics )
assert( isgraphics( hGraphics ), 'GraphicsObjectWithContextMenu:InvalidGraphicsObject', 'hGraphics must be a handle to a valid graphics object' );
obj.hGraphics = hGraphics;
obj.hContextMenu = uicontextmenu( );
obj.hGraphics.UIContextMenu = obj.hContextMenu;
end
function addMenuItem( obj, menuItem, callback )
validateattributes( menuItem, { 'matlab.ui.container.Menu' }, { 'scalar' } )
validateattributes( callback, { 'function_handle' }, { 'scalar' } )
assert( nargin( callback ) == 1, 'GraphicsObjectWithContextMenu:InvalidMenuCallback', 'callback must be a function handle of exactly 1 argument, representing the graphics object to which the context menu is attached' );
menuItem.Parent = obj.hContextMenu;
fCallback = @( src,evt ) callback( obj.hGraphics );
menuItem.Callback = fCallback;
end
end
end
is the class I mentioned in the comments to the question. I had to put a little thought in to its structure as my original quickly thrown out suggestion didn't fully consider all the difficulties. Also this is not the only way to setup this class of course, it is just the way that I am happy with for my work.
Obviously some aspects of it are optional, but I like to validate things in my classes and assert where I consider it appropriate too for ease of usage, but that is just my preference - it is not crucial to the bare minimum solution.
And here is some simple example code to show it in use with multiple line objects. I just added one menu item to the context menu as it is the only one I need for my current work, but others can be added in just the same manner.
figure; hAxes = gca;
hold on
hLines(1) = plot( hAxes.XLim, [2 2], 'LineWidth', 2 );
hLines(2) = plot( hAxes.XLim, [3 3], 'LineWidth', 2 );
hLines(3) = plot( hAxes.XLim, [7 7], 'LineWidth', 2 );
hLines(4) = plot( hAxes.XLim, [8 8], 'LineWidth', 2 );
m1 = uimenu( 'Label', 'Remove' );
linesWithContextMenu = GraphicsObjectWithContextMenu.empty( 0, numel( hLines ) );
for n = 1:numel( hLines )
linesWithContextMenu( n ) = GraphicsObjectWithContextMenu( hLines( n ) );
linesWithContextMenu( n ).addMenuItem( m1.copy( ), @( lineHandle ) delete( lineHandle ) );
end
The key points of the solution are:
  • The handle-derived class allows you to wrap up an individual graphics object handle together with a context menu so that the context menu knows its triggering graphics object
  • The menu items have been added via a function which takes the callback as a separate argument to the menuItem in which it would normally live. This you can see is because the callback should have 1 variable argument, the graphics object handle, which this class function fills in to create the actual callback which the menu item will trigger. This means that your callback function can be anything you want that works with the handle of the graphics object that triggered the callback.
  • Usage should be very simple, but beware to take a copy of the menu item object when adding it to multiple objects, otherwise the same item will just keep getting re-parented and only the last object it is assigned to will actually have the context menu item.
  2 件のコメント
Niko
Niko 2016 年 2 月 18 日
Hi Adam, thank you so much for the very detailed answer! I will look more into it and maybe use this method in my future projects.
Adam Danz
Adam Danz 2020 年 10 月 7 日
Excellent method, Adam!
I had to make two changes to get this working in App Designer when the context menu belongs to ROI objects on a UIAxes.
1. Define uimenu parent as figure handle
m1 = uimenu( 'Label', 'Remove' );
% changed to
m1 = uimenu(app.UIFigure, 'Label', 'Remove' );
2. Define uicontextmenu parent as figure handle
obj.hContextMenu = uicontextmenu( );
% changed to
obj.hContextMenu = uicontextmenu(ancestor(hGraphics,'figure') )

サインインしてコメントする。

その他の回答 (3 件)

Phil
Phil 2016 年 10 月 4 日
There is a simpler answer. You can use "gco" in callbacks called by the context menu to get the handle of the current graphics object that was clicked. Thus the menu can be shared between objects without difficulty and still have different behavior for each object. For example, to delete the specific object clicked, use the callback "@(~,~)delete(gco)".
  4 件のコメント
Kristoffer Walker
Kristoffer Walker 2020 年 10 月 21 日
Folks,
This does not work with AppDesigner. Is there a similar simple solution for AppDesigner? This is a fundamental need for developers. Many thanks to all of you who contribute to knowledge sharing on this forum!!
Kris
Adam Danz
Adam Danz 2020 年 10 月 21 日
編集済み: Adam Danz 2020 年 10 月 21 日
@Kristoffer Walker, that's because the HandleVisibility of uifigures is set to 'off' by default which prevents getting access to the figure handle or any object within the figure.
solution 1: set the uifigure's HandleVisibility to 'callback' or 'on'--(not recommended)
solution 2: use gcbo() instead of gco().
solution 3 (best): instead of relying on the object being 'current', pass the object's handles into the function or use a pre-defined obj handle in the function definition.

サインインしてコメントする。


래충 강
래충 강 2021 年 12 月 17 日
get(app.UIFigure, 'CurrentObject')
It tell you which object called the context menu
  1 件のコメント
Rina Blomberg
Rina Blomberg 2023 年 2 月 3 日
編集済み: Rina Blomberg 2023 年 2 月 3 日
Thank you!! Exactly the solution I needed.

サインインしてコメントする。


Walter Roberson
Walter Roberson 2015 年 8 月 3 日
You could Label or Tag them differently; http://www.mathworks.com/help/matlab/ref/uicontextmenu.html
  3 件のコメント
Walter Roberson
Walter Roberson 2015 年 8 月 3 日
No, notice that the single UiContextMenu callback is used in the example there and that it pulls information out of the Label (in that case) to decide what to do. It is not well-documented in the description of uicontextmenu what the source object is (the first parameter passed in) but you can deduce from the example that it is the menu object rather than the uicontextmenu object
Niko
Niko 2015 年 8 月 4 日
編集済み: Niko 2015 年 8 月 4 日
Sorry I'm not talking about multiple menu items inside a single context menu, but multiple objects in the same figure that share a context menu, so that the same menu appears when I right-click on any of them, but the effect of the callback is reflected only on that specific object. I think the source parameter only contains information about the menu item, not the object I clicked on.

サインインしてコメントする。

カテゴリ

Help Center および File ExchangeInteractive Control and Callbacks についてさらに検索

Community Treasure Hunt

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

Start Hunting!

Translated by