Match legend to axes objects
Asked Answered
H

3

6

Context:

I have a (programmatic) GUI which contains several axes objects inside some uipanel parenting structure. Some of these axes have legend objects associated, some don't.
I want to include a button in my GUI which copies the currently visible plot into a new figure including its legend if it has one.

I know how to get the handles to the currently visible uipanel and all axes objects inside it. I also know how to tell the axes apart from the legends.

Question:

How can I match the legends to the axes?

For example, in one case my GUI shows 2 axes with some plots, each of which has its own legend. When I click the 'export' button, I want 2 new figures to be created, each containing one axes with its corresponding legend.
What I'm currently able to do is

  1. put everything in one figure (they overlap in that case because their positions in the original uipanels are the same),
  2. put each axes and each legend into their own respective figures,
  3. put all axes in one and all legends in another figure and
  4. put all axes in their own figure with all legends within the same panel.
  5. split it up by panel, that is, put all subplots into the same figure and each group of plots in their own figure.

Problem:

The problem is, I don't have the handles to either of those objects. I only have the handles to the uipanel objects. The graphics inside the panels are built by another function which contains all sorts of tricky stuff, but doesn't return handles. Also the parenting structure of said panels makes it rather hard to do this with tricks like get(handles.panels{1},'Children') because it will work in some, but not all cases.
I thought about simply exporting the panels (and have actually a working version which does this), but this has several problems, mainly related to figure tools and resizing. I want to get rid of the panels when I use the "Export" button.

Code Snippet / Example:

The following code snippet will create an example GUI with access to all handles I have access to in my complete GUI. Clicking the buttons will show the different versions I got to "work". What I want is one figure for each axes including its legend, if it has one. the 4th version (same parent) comes close, but breaks if it encounters subplots, the 5th version (by panel) simply puts entire subplot groups into one window (in which case, at least, they don't overlap). Copy the code into a new .mfile to try it.

function test
    figure(1)
    clf
    t=(0:0.1:10)'; %'// dummy comment
    p2 = uipanel('Visible','off','Position',[0 0 1 1]);
    p1 = uipanel('position',[0 0 1 1]);
    p11 = uipanel('Parent',p1,'Position',[0 0 0.5 0.9]);
    p12 = uipanel('Parent',p1,'Position',[0.5 0 0.5 0.9]);
    uicontrol('Style','push','String','all in one','Units','norm',...
        'Position',[0.05 0.91 0.14 0.06],'Callback',@export1);
    uicontrol('Style','push','String','all in own','Units','norm',...
        'Position',[0.24 0.91 0.14 0.06],'Callback',@export2);
    uicontrol('Style','push','String','by type','Units','norm',...
        'Position',[0.43 0.91 0.14 0.06],'Callback',@export3);
    uicontrol('Style','push','String','same parent','Units','norm',...
        'Position',[0.62 0.91 0.14 0.06],'Callback',@export4);
    uicontrol('Style','push','String','same panel','Units','norm',...
        'Position',[0.81 0.91 0.14 0.06],'Callback',@export5);
    subplot(1,1,1,'Parent',p11)
    plot(t,[sin(t) cos(t)])
    legend('Sine','Cosine')
    subplot(2,1,1,'Parent',p12)
    plot(t,[polyval([0.05 -1 2],t) exp(-t) abs(t-3)])
    subplot(2,1,2,'Parent',p12)
    plot(t,erf(t))
    legend('Error function')

    function export1(~,~)
        current = findobj('Type','uipanel','Parent',1,'Visible','on');
        visible_axes = findobj(current,'Type','axes');
        copyobj(visible_axes,figure);
    end

    function export2(~,~)
        current = findobj('Type','uipanel','Parent',1,'Visible','on');
        visible_axes = findobj(current,'Type','axes');
        for i=1:length(visible_axes)
            copyobj(visible_axes(i),figure);
        end
    end

    function export3(~,~)
        current = findobj('Type','uipanel','Parent',1,'Visible','on');
        visible_axes = findobj(current,'Type','axes','Tag','');
        visible_legends = findobj(current,'Tag','legend');
        copyobj(visible_axes,figure);
        copyobj(visible_legends,figure);
    end

    function export4(~,~)
        current = findobj('Type','uipanel','Parent',1,'Visible','on');
        visible_axes = findobj(current,'Type','axes','Tag','');
        visible_legends = findobj(current,'Tag','legend');
        for i=1:length(visible_axes)
            par = get(visible_axes(i),'Parent');
            same = findobj(visible_legends,'Parent',par);
            h=figure;
            copyobj(visible_axes(i),h)
            copyobj(same,h)
        end
    end

    function export5(~,~)
        current = findobj('Type','uipanel','Parent',1,'Visible','on');
        visible_axes = findobj(current,'Type','axes');
        parents = cell2mat(get(visible_axes,'Parent'));
        uparents = unique(parents);
        for i=1:length(uparents)
            copyobj(visible_axes(parents==uparents(i)),figure)
        end
    end
end
Herald answered 14/2, 2014 at 12:27 Comment(0)
P
2

In a figure, graphical objects are organized hierarchically and can all be handled individually. For instance, axes is a child of a figure, plot is a child of an axes, and legends are build as axes.

The following example plots 2 lines (red and blue, with legends), then mixes plots and legends using copyobj.

figure;
subplot(1,2,1)
hp1 = plot(1:10,'r')
hl1 = legend('red')

subplot(1,2,2)
hp2 = plot(1:10,'b')
hl2 = legend('blue')

hf = figure;
hax = axes;
copyobj(hp1, hax);    %copy plot to axes
copyobj(hl2, hf);     %copy legend to figure

enter image description here

enter image description here

Not tested with a GUI though.

Parthenia answered 14/2, 2014 at 13:40 Comment(3)
The problem is, I don't have access to hl1and hl2 as in your example. Thus, I'm trying to retrieve them later. Imagine your first figure (with the subplots) being saved as .fig and re-opened in another function/script/session. You don't have the handles then. How would you achieve the same thing (or actually copy the matching ones, but that revolves to the same) in that case?Herald
Saving a figure saves all the child axes, including legend. I don't understand why plots/legends are dissociated in your scenario. In addition, you can always recreate a legend based on a figure by simply calling legend. Finally, you can use set(gcf, 'UserData', legend_handle)to embed the legend data within the figure to retrieve it later.Parthenia
They're not dissociated, I want to dissociate them. I want to open the .fig containing your subplot figure, then create a new figure containing only the red axes including its legend. I can't save the legend handle in the UserData because I never get it in the first place. The graphics are created and loaded into uipanel objects by an external function which I can't change. That function won't return me the legend handle.Herald
H
1

I think the simpler solution is to save the axes of the figure you're about to save as a fig file.

h = figure(1);

x = linspace(1,100);
y = 2*x;

ax = findall(h,'type','axes');
plot(x,y);

save('youraxes', 'ax');
hgsave(h, 'yourfig.fig');

I'm using Matlab R2012a, alternatively in R2013a or b the function to save the fig is now savefig.

Hung answered 14/2, 2014 at 14:17 Comment(1)
How does this help towards the problem? If you're referring to my comment to Macduff's answer, that was simply an example to clarify what I want to do. It's not really related to my actual problem.Herald
A
0

Once you have obtained the axes handle you can find the corresponding legend handle using

legend_handle = legend(axes_handle)

Allare answered 2/12, 2014 at 10:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.