Matlab图形中的子图标签

3

我想给我创建的子图一个简单的标签。不幸的是,我得到了一个丑陋的行为。考虑下面的函数:

function h = set_label1(label)
tlh = get(gca, 'Title');
if strcmp(get(tlh, 'String'), '')
    title(' ');
end
ylh = get(gca, 'YLabel');
if strcmp(get(ylh, 'String'), '')
    ylabel(' ');
end

ylp = get(ylh, 'Position');
x = ylp(1);

tlp = get(tlh, 'Position');
y = tlp(2);

h = text('String', label, ...
        'HorizontalAlignment', 'right',...
        'VerticalAlignment', 'Baseline', ...
        'FontUnits', 'pixels', ...
        'FontSize', 16, ...
        'FontWeight', 'bold', ...
        'FontName', 'Arial', ...
        'Position', [x y 0]);
end

这是一个简单的测试运行:
figure;
h1 = axes('OuterPosition', [0,0,.5 1]);
set(h1,'LooseInset',get(h1,'TightInset'));
h2 = axes('OuterPosition', [.5,0,.5 1]);
set(h2,'LooseInset',get(h2,'TightInset'));

axes(h1);
plot([0 1], [4 5]);
set_label1('A');  

axes(h2);
plot([0 1], [4 5]);
set_label1('B');

我得到的图片是:

enter image description here

如果你调整图像大小,标签将不再处于正确位置。这没关系,我早就预料到了(如果你知道如何把它们放回原位并告诉我们,那会让我非常高兴)。
我面临的问题是,我不想指定标签在“数据”单位中的位置。相反,我想使用归一化单位。因此,我使用了函数的修改形式。现在让我们使用这个:
function h = set_label2(label)
tlh = get(gca, 'Title');
if strcmp(get(tlh, 'String'), '')
    title(' ');
end
ylh = get(gca, 'YLabel');
if strcmp(get(ylh, 'String'), '')
    ylabel(' ');
end

oldUnits = replace_prop(ylh, 'Units', 'normalized');
ylp = get(ylh, 'Position');
x = ylp(1);
set(ylh, 'Units', oldUnits);

oldUnits = replace_prop(tlh, 'Units', 'normalized');
tlp = get(tlh, 'Position');
y = tlp(2);
set(ylh, 'Units', oldUnits);

h = text('String', label, ...
        'HorizontalAlignment', 'right',...
        'VerticalAlignment', 'Baseline', ...
        'FontUnits', 'pixels', ...
        'FontSize', 16, ...
        'FontWeight', 'bold', ...
        'FontName', 'Arial', ...
        'Units', 'normalized',...
        'Position', [x y 0]);
end

function oldvalue = replace_prop(handle, propName, newvalue)
oldvalue = get(handle, propName);
set(handle, propName, newvalue);
end

运行相同的测试:

figure;
h1 = axes('OuterPosition', [0,0,.5 1]);
set(h1,'LooseInset',get(h1,'TightInset'));
h2 = axes('OuterPosition', [.5,0,.5 1]);
set(h2,'LooseInset',get(h2,'TightInset'));

axes(h1);
plot([0 1], [4 5]);
set_label2('A');  

axes(h2);
plot([0 1], [4 5]);
set_label2('B');

我们得到了与之前完全相同的图像。唯一的问题是现在调整大小时会出现一些问题:
标签实际上处于正确的位置。但似乎我使用的“LooseInset”和“TightInset”属性会使轴表现得好像没有标签一样。 有没有解决办法?实际上我所做的只是获取标题和纵轴标签在归一化单位中的位置,而不是在数据单位中,这似乎会导致问题。
我需要以归一化单位获取它的原因是为了当我们得到一个3D绘图时,我可以根据标题和zlabel来定位标签的位置。

即使我们在测试代码中删除有关更改“LooseInset”的代码,我们仍然会使用“set_label2”获得相同的行为。 - jmlopez
顺便说一下,LooseInset是一个未记录的属性。 - Amro
2个回答

2

为了记录下来,这是我决定采用的版本。它做到了我期望的功能,但现在我遇到了一个问题,我不知道该怎么解决。好消息是,这里有一个名为axes_label的函数。

function c = axes_label(varargin)

if isa(varargin{1}, 'char')
    axesHandle = gca;
else
    axesHandle = get(varargin{1}{1}, 'Parent');
end

if strcmp(get(get(axesHandle, 'Title'), 'String'), '')
    title(axesHandle, ' ');
end
if strcmp(get(get(axesHandle, 'YLabel'), 'String'), '')
    ylabel(axesHandle, ' ');
end
if strcmp(get(get(axesHandle, 'ZLabel'), 'String'), '')
    zlabel(axesHandle, ' ');
end

if isa(varargin{1}, 'char')    
    label = varargin{1};
    if nargin >=2
        dx = varargin{2};
        if nargin >= 3
            dy = varargin{3};
        else
            dy = 0;
        end
    else
        dx = 3;
        dy = 3;
    end
    h = text('String', label, ...
        'HorizontalAlignment', 'left',...
        'VerticalAlignment', 'top', ...
        'FontUnits', 'pixels', ...
        'FontSize', 16, ...
        'FontWeight', 'bold', ...
        'FontName', 'Arial', ...
        'Units', 'normalized');
    el = addlistener(axesHandle, 'Position', 'PostSet', @(o, e) posChanged(o, e, h, dx, dy));
    c = {h, el};
else
    h = varargin{1}{1};
    delete(varargin{1}{2});
    if nargin >= 2    
        if isa(varargin{2}, 'char')
            set(h, 'String', varargin{2});
            if nargin >=3
                dx = varargin{3};
                dy = varargin{4};
            else
                dx = 3;
                dy = 3;
            end
        else
            dx = varargin{2};
            dy = varargin{3};
        end
    else
       error('Needs more arguments. Type help axes_label'); 
    end
    el = addlistener(axesHandle, 'Position', 'PostSet', @(o, e) posChanged(o, e, h, dx, dy));
    c = {h, el};
end
posChanged(0, 0, h, dx, dy);
end

function posChanged(~, ~, h, dx, dy)
    axh = get(h, 'Parent');
    p = get(axh, 'Position');
    o = get(axh, 'OuterPosition');
    xp = (o(1)-p(1))/p(3);
    yp = (o(2)-p(2)+o(4))/p(4);
    set(h, 'Units', get(axh, 'Units'),'Position', [xp yp]);
    set(h, 'Units', 'pixels');
    p = get(h, 'Position');
    set(h, 'Position', [p(1)+dx, p(2)+5-dy]);
    set(h, 'Units', 'normalized');
end

好的,那么我们如何使用这个糟糕的函数呢?我设计了以下用法:

%   c = axes_label('label')
%      Places the text object with the string 'label' on the upper-left 
%      corner of the current axes and returns a cell containing the handle
%      of the text and an event listener.
%
%   c = axes_label('label', dx, dy)
%      Places the text object dx pixels from the left side of the axes
%      and dy pixels from the top. These values are set to 3 by default.
%
%   c = axes_label(c, ...)
%      Peforms the operations mentioned above on cell c containing the
%      handle of the text and the event listener.
%
%   c = axes_label(c, dx, dy)
%      Adjusts the current label to the specifed distance from the
%      upper-left corner of the current axes.

如果我们执行与之前相同的测试:

figure;
h1 = axes('OuterPosition', [0,0,.5 1]);
set(h1,'LooseInset',get(h1,'TightInset'));
h2 = axes('OuterPosition', [.5,0,.5 1]);
set(h2,'LooseInset',get(h2,'TightInset'));

axes(h1);
plot([0 1], [4 5]);
axes_label('A');  

axes(h2);
plot([0 1], [4 5]);
axes_label('B', 250, 250);

现在我们得到了我想要的。标签“A”位于轴外框的左上角。而标签B,我明确将其设置为距其左上角250像素。下面是绘图结果:enter image description here 我喜欢这个函数的原因是,如果我存储了从中返回的单元格,然后再放回去,我可以改变位置。例如,如果label = axes_label('A');,那么在命令提示符上,我可以执行label = axes_label(label,10,20);,我就会看到我的标签移动了。
我现在面临的问题与函数export_fig有关。如果我尝试使用以下内容:
export_fig('testing.png', '-nocrop', '-painters');

那么这就是我得到的图形。

enter image description here

这就是我为什么夸大了标签B的原因。在我添加事件监听器之前,export_fig可以在我放置它们的位置打印标签。但不知何故,现在export_fig没有做它声称要做的事情。主要是以以下方式导出图像:

将图形/轴复制为屏幕上显示的样子

如果我们删除选项-painters,则会得到以下结果:

enter image description here

由于我对监听器不熟悉,因此代码中可能存在错误,因此如果有人可以修复此行为和/或可以改进此代码,请随时这样做并将其作为答案分享。


1
首先,我喜欢您使用标题/ y-标签将文本定位于左上角的想法,聪明 :)
现在,不要使用规范化单位,继续使用数据单位,并创建一个事件侦听器,以便每当标题或y-标签更改其位置时,使用它们的新值重新调整创建的文本。
因此,请在您的set_label1函数末尾添加以下内容:
addlistener(ylh, 'Position', 'PostSet', @(o,e) posChanged(o,e,h,1))
addlistener(tlh, 'Position', 'PostSet', @(o,e) posChanged(o,e,h,2))

这里是用于两种情况的回调函数(我们使用最后一个参数idx来控制是设置x坐标还是y坐标):

function posChanged(src,evt,hTxt,idx)
    posLabel = evt.NewValue;          %# new position of either title/y-label
    posText = get(hTxt, 'Position');  %# current text position
    posText(idx) = posLabel(idx);     %# update x or y position (based on idx)
    set(hTxt, 'Position',posText)     %# adjust the text position
end

谢谢您向我展示如何添加监听器。最终,我决定使用标准化单位使其工作。但是这次我获取了轴的“位置”和“OuterPosition”。使用此信息,我可以指定标签相对于左上角的位置。这样它也可以在3D中工作。 - jmlopez
@jmlopez:为了后人的利益,您应该考虑发布您的新代码。 - Amro
好的,我只是在尝试解决我对事件监听器的一个问题。 - jmlopez
@jmlopez:是的,只需存储返回值:lh = addlistener(..); 然后在想要删除它时调用 delete(lh) - Amro
这就是我不想做的事情...无论如何,最终我还是将事件监听器和标签的处理程序返回到单元格中。 - jmlopez
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接