Matlab: Something like "relative" position with uicontrol/axis; keep fixed margins when resizing

tim picture tim · Dec 14, 2011 · Viewed 9.3k times · Source

I currently have a big headache to get a small GUI working nicely which isn't being created with GUI editor but programmatically! What I have so far is something like the following:

hFig = figure();
set(hFig, 'Position', [300 200 500 400]);
plot((1:10).^2, '*-r');

% Größe des Plots so anpassen, dass links Platz für Buttons
ap = get(gca, 'TightInset');
fp = get(gcf, 'Position');
set(gca, 'Position', [160/fp(3), 30/fp(4), (fp(3)-180)/fp(3), (fp(4)-60)/fp(4)]);

uicontrol('Style', 'pushbutton', 'String', 'foo', 'Position', [15 fp(4)-60 110 30]); 
uicontrol('Style', 'pushbutton', 'String', 'bar', 'Position', [15 fp(4)-100 110 30]); 

Try to resize it: It doesn't 'look' the same, which means that the uicontrol boxes don't stay at the same relative position and the margins from the axis to the figure window get bigger. What I want to achieve is:

Have a figure window with a given position (x/y, width and height) with a plot inside. The plot will have a title and labels for x and y. Make the plot as height and width to have the TightInset plus a margin in each direction of a certain px-size (e.g. TightInset + 10px) as big as the figure window; except leave 150px of free space on the left to place some uicontrol buttons, and have them stay in the same position: This would be the same as being able to give the position from top/left (top = 20, left = 10) instead of bottom/left.

Thanks a lot!

Answer

tim picture tim · Dec 15, 2011

Okay finally found a working solution I wanted it to be :-) Hopefully it is helpfull for somebody interested in it:

Main script file:

p = [300 300 1000 600];
fixedMargins = [250 0 0 0]; % [left, top, right, bottom]
f = figure('Position', p, 'Color', [0.9 0.9 0.9]);
plot(-10:10, (-10:10).^3, '*-r');
set(f, 'ResizeFcn', {@resizeCallback, gca, fixedMargins, {@myuiFunc, f, 40, 50}});

title('bla')
xlabel('foooooooooo')
ylabel('barrrrrrr')

Resize Callback Function:

% Need to pass the handle of the axis to modify (hAx) AND to pass the
% desired margins as second extra callback argument: 
% [left, top, right, bottom]!
function resizeCallback(hFig, ~, hAx, fixedMargins, func)    
    % Disable automatic rezising
    set(hAx, 'Units', 'pixels');

    % Figure-Size
    fp = get(hFig, 'Position');

    % Calculate Position of the axis
    margin = get(hAx, 'TightInset') * [-1 0 1 0; 0 -1 0 1; 0 0 1 0; 0 0 0 1];

    % Position to fill the figure minus the TightInset-Margin
    newPos = [0 0 fp(3:4)] - margin;

    % Change position based on margins
    newPos(1) = newPos(1) + fixedMargins(1);
    newPos(3) = newPos(3) - fixedMargins(1) - fixedMargins(3);
    newPos(2) = newPos(2) + fixedMargins(4);
    newPos(4) = newPos(4) - fixedMargins(2) - fixedMargins(4);

    % Set new position
    set(hAx, 'Position', newPos);

    % Call UI-Func
    if(nargin == 5)
        f = func{1};
        args = func(2:end);
        f(args{:});
    end
end

You can pass whatever function you want to be called when resizing the figure window, e.g. to update something in the figure. In my example it's the myuiFunc(), which is the following:

function myuiFunc(hFig, left, top)
    persistent handles;

    if(~isempty(handles))
        delete(handles);
        handles = [];
    end

    fp = get(hFig, 'Position');
    h1 = uicontrol('Style', 'pushbutton', 'String', 'Foo','Position', [left fp(4)-top 100 35]);
    h2 = uicontrol('Style', 'pushbutton', 'String', 'Bar','Position', [left fp(4)-top-50 100 35]);
    handles = [h1 h2];
end

I like it :) Hopefully you too!

Edit: No need to edit the resizeCallback Function! Should work if you just pass your desired margins to it and if you like, additionally a function handle with arguments which will be called for each resize!