Complete unexpected GUI-elements behavior

3 views (last 30 days)
Part of quite a large code base, so no code example.... Here's what happens:
  • I have a GUI with tabs, panels therein and several vertically stacked elements in each panel. All manually coded.
  • In a panel I have a checkbox which determines the visible elements: some may remain visible, others not.
  • When triggering the checkbox callback, I determine which elements remain visible in the panel, calculate their total height, set the panel to the required height and then reposition all the visible elements in that panel.
Now the unexpected happens:
  • Situation A: Very often, the resulting layout is wrong, all the elements in the panel are placed far too high although the panel height is OK.
  • Situation B: If I then force a layout redraw by resizing the window a little bit horizontally (this forces all above calculations to be done again) the layout suddenly appears correct.
  • When going from situation A to B I do not see the size of the panel change (correct...). But visually the vertical positions of the elements in the panel do change although the read-back positions via each element handle remain exactly the same for A and B (so it's not that my code has incorrect calculations). That's not right...
Conclusion: it seems to depend on some unknown internal uncertainties how Matlab deals with the GUI element positions that I provide. For window resizing I already implemented a timer delay to have the window redrawn 0.5 seconds after the last resize-callback (plus an additional pause after resizing the tabs to fit them in the window...) to prevent the layout being messed up completely, but things seems very unreliable.
Is an explanation known for the above behavior, and possibly a work-around to have things more reliable?
Because the application was build so users can quite simple add GUI elements with a few lines of code, it is no option to make things completely in AppDesigner or using some external toolbox.
  6 Comments
Rik
Rik on 22 Aug 2023
Also, have you checked whether the result is correct if you only call your layout function after calling drawnow? What happens if you omit the first call and only leave the second?
If our suggestion doesn't solve the problem, you might need to try to construct a minimal failing example. Otherwise you'll have to share the entire function for us to have a chance at reproduce this issue.
Jeroen Boschma
Jeroen Boschma on 22 Aug 2023
Edited: Jeroen Boschma on 22 Aug 2023
I made a small example which shows, at least on my laptop, the problem. With the following lines of code in function processLayout():
height = handles.tabs{1}.Position(4);
y = height - handles.panels{i}.panel.Position(4) - 5;
I expect that the top of the panels are positioned 5 pixels below the top of the tab. When (quickly) resizing the window by mouse, the top of the panels are however positioned 'randomly' and in 50% of the cases on the correct location. The change of panel location is often much larger than the resize of the window.
The case that I described earlier, changing the layout of elements within a panel with a checkbox callback without window resize, is not inhere but seems to suffer from the same behavior. Now I just hope I did not do something fundamentally wrong...
Addition: I did some testing while writing this comment. I added the two printf-statements, and it is very insightfull when resizing the window. What I do is basically:
  • in guiResizeCallback I set the Position of the tabgroup to align with the window
  • in processLayout I read a tab Position back to align the panels
What happens:
  • In many cases I get the wrong tab height, try this with a vertical window resize, and then
  • You get the correct tab-size and layout when doing a horizontal window resize after a vertical window resize.
In the case I tested above, it seems that when resizing a tabgroup, the tabs it contains are not adjusted directly, but only during a redraw that follows later. So when directly reading a tab-size, you probably get the old ones instead of the size corresponding with the resized tabgroup. Not sure if that qualifies as a bug.... Maybe my other case, changing the layout of elements within a panel with a checkbox callback without window resize, suffers from something similar. The fact that my other application seems to work reliable when calling the layout function twice seems to confirm the above findings (second time most read-back Positions are OK...).
A double check: if I do:
height = handles.tab_group.InnerPosition(4);
(tabgroup) instead of:
height = handles.tabs{1}.Position(4);
(tab) then the layout is consistent, so it confirms the above. Problem however is that 'InnerPosition' of the tabgroup does not seem to give the height of the tabs, so additional fudge offsets are required to get the layout correct.
Code:
function Matlab_GUI_test
% Clean up.
clc
close all
% GUI.
fig_width = 600;
fig_height = 300;
screen = get(0, 'ScreenSize');
handles.fig = uifigure('AutoResizeChildren', 'off', ...
'Position', [(screen(3)-fig_width)/2, (screen(4)-fig_height)/2, fig_width, fig_height], ...
'SizeChangedFcn', @guiResizeCallback);
% Add tabs.
tab_titles = {'Tab 1', 'Tab 2'};
handles.tab_group = uitabgroup(handles.fig);
handles.tabs = cell(size(tab_titles));
for i = 1:numel(tab_titles)
handles.tabs{i} = uitab(handles.tab_group, 'AutoResizeChildren', 'off', 'Title', tab_titles{i});
end
% Generate some crowded panels.
N = 5;
handles.panels = cell(1,N);
for i = 1:N
handles.panels{i} = generateCrowdedPanel(handles.tabs{1});
end
% Store GUI data.
guidata(handles.fig, handles)
guiResizeCallback(handles.fig)
end
function data = generateCrowdedPanel(tab)
data = [];
data.panel = uipanel(tab, 'Title', 'panel', 'Position', [1, 1, 60, 300]);
N = 10;
data.editors = cell(1, N);
for i = 1:N
data.editors{i} = uieditfield(data.panel, 'numeric', 'Value', i, 'Position', [1, 275 - 25*i, 50, 20]);
end
end
function processLayout(handles)
height = handles.tabs{1}.Position(4);
fprintf('Read tab height = %.0f\n', height)
for i = 1:numel(handles.panels)
x = 70*i;
y = height - handles.panels{i}.panel.Position(4) - 5;
if true % This switch is only meant to see if it makes a difference...
p = handles.panels{i}.panel.Position;
p(1) = x;
p(2) = y;
handles.panels{i}.panel.Position = p;
else
handles.panels{i}.panel.Position(1) = x;
handles.panels{i}.panel.Position(2) = y;
end
end
end
function guiResizeCallback(obj, varargin)
% Can be called while guidata() was not called yet, so catch errors.
try
handles = guidata(obj);
p = handles.fig.Position;
p(1:2) = 1;
handles.tab_group.Position = p;
fprintf('Set tab height = %.0f\n', p(4))
processLayout(handles)
catch
end
end

Sign in to comment.

Answers (1)

Image Analyst
Image Analyst on 22 Aug 2023
Maybe try to build it from scratch using App Designer instead of GUIDE.
  2 Comments
Rik
Rik on 22 Aug 2023
Just adding to this: in this thread I present a (thinly veiled) argument to write a GUI from the ground up in code. I still stand behind that advice.
Jeroen Boschma
Jeroen Boschma on 23 Aug 2023
I need too much flexibility in changing layouts depending on several settings in the GUI, plus that a user should be able to add a fully functioning input field with just a line of code. So I always write GUIs in code.

Sign in to comment.

Categories

Find more on Interactive Control and Callbacks in Help Center and File Exchange

Tags

Products


Release

R2020b

Community Treasure Hunt

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

Start Hunting!