Axes toolbar is not shown when axes is re-parented

Viewed 346

In MATLAB R2019a (Update 2), when using the following code, the axes toolbar is not shown when selecting Tab2 or Tab3.

classdef test < handle
    properties
        Ax
    end
    methods
        function self = test()
            f = figure;
            tg = uitabgroup(f, 'SelectionChangedFcn', @self.onSelectionChanged);
            t1 = uitab(tg, 'Title', 'tab1');
            uitab(tg, 'Title', 'tab2');
            uitab(tg, 'Title', 'tab3');
            self.Ax = axes(t1);
        end

        function onSelectionChanged(self, ~, e)
            self.Ax.Parent = e.NewValue;

        end
    end
end

I would like to know how to make the toolbar appear in the selected tab.

Edit: The following code shows the problem better: The axes toolbar is stucked in the first panel, while the axes is in second panel. However, to reproduce this, don't copy and paste into your MATLAB command window. It only happens if the axes' parent changes after the axes toolbar is drawn. Thus I split the code into two sections:

f = figure;
p1 = uipanel(f, 'Units', 'normalized', 'Position', [0 0 .5 1]);
p2 = uipanel(f, 'Units', 'normalized', 'Position', [.5 0 .5 1]);
ax = axes(p1);

And now change the axes' parent:

ax.Parent = p2;

Here is the result:Result after re-parenting the axes

And here is what I expect:

expected result

So far, I haven't figured out how to trigger the event that puts the toolbar in the correct position.

Neither changing the Visible property of the figure, axes, axtoolbar or uipanel helped. Also, overriding the toolbar by a new one does not help.

3 Answers

I have some bad news and some good news: based on my attempts it appears that one cannot move the same toolbar between containers (tabs or panels) due to a MATLAB restriction, however, a workaround is possible. Below is the details of how I reached this conclusion.


Upon investigating this a bit, I noticed that several properties of the AxesToolbar change after it is shown for the first time, two of them caught my eye - HasTrueParent and NodeParent.

Aha. So the toolbar now has a parent, which means that somewhere in the hierarchy of children, the tab should see the toolbar. Let's investigate further:

I've added another property to your class called, Tabs, and this is how I populate it in the constructor:

      self.Tabs(1) = uitab(tg, 'Title', 'tab1');
      self.Tabs(2) = uitab(tg, 'Title', 'tab2');
      self.Tabs(3) = uitab(tg, 'Title', 'tab3');
      self.Tabs = handle(self.Tabs);

Then, setting after setting (and hitting) a breakpoint we can see that:

K>> allchild(self.Tabs(1))
ans = 
  2×1 graphics array:

  AnnotationPane
  Axes
K>> allchild(self.Tabs(2))
ans = 
  0×0 empty GraphicsPlaceholder array.

Notice that AnnotationPane? This is the layer that contains annotation and probably also the toolbar. How can we know for sure? We can set its Visibility to 'off', and the toolbar stops appearing.

So naturally, one might be tempted to change the Parent of that pane, but this results in the error:

Error using matlab.graphics.shape.internal.AnnotationPane/setParentImpl
Cannot change parent of AnnotationPane object.

So what can be done about this? If you create new axes in every new tab,

hFig = figure();
hTG = uitabgroup(hFig);
for iTab = 1 : 3
    hTab = uitab(hTG, 'title', "Tab" + iTab);
    hPanel = uipanel(hTab);
    hAxes(iTab) = axes(hPanel);
end

their toolbars will be created correctly. From there, it's a matter of relocating the children (of the axes) and updating the limits/viewport.

Since I considered the behaviour described above a bug, I have filed it to Matlab staff.

They responded to get around this issue, one shoud re-set the Parent of the axes toolbar. In case of the test class from above:

function onSelectionChanged(self, ~, e)
    self.Ax.Parent = e.NewValue;
    set(self.Ax.Toolbar,'Parent',[],'Parent',self.Ax);
end

Here is another answer from my previous comment:

Another solution is to put the axes in a uipanel (which is good practice anyway) and then re-parent the panel:

classdef test < handle
    properties
        Ax
    end
    methods
        function self = test()
            f = figure;
            tg = uitabgroup(f, 'SelectionChangedFcn', @self.onSelectionChanged);
            t1 = uitab(tg, 'Title', 'tab1');
            uitab(tg, 'Title', 'tab2');
            uitab(tg, 'Title', 'tab3');
            self.Ax = axes(uipanel(t1)); % uipanel as axes parent
        end

        function onSelectionChanged(self, ~, e)
            self.Ax.Parent.Parent = e.NewValue; % re-parent the uipanel

        end
    end
end
Related