Maybe a simple example will show what I mean. Here is a quick script to produce a window with a popup menu allowing the user to select one of three questions. The code then writes the question as a text uicontrol and places an edit uicontrol next to it. One question is long containing many characcters, the other is of medium length, and the last is short.
function static_labels clear all; close all; clc f = figure(); % List of question that will appear to the left of an edit uicontrol. q = {'How much wood could a woodchuck chuck if a woodchuck could chuck wood? ', ... 'What is the airspeed velocity of an unladen swallow? ', ... 'What is the meaning of the universe? '}; % Create the popup uicontrol and make its entries out list of questions % The menu's callback function changes the text in the text uicontrol % defined below pulldown_menu = uicontrol('Parent', f, ... 'Style', 'popupmenu', ... 'String', {'Question 1', 'Question 2', 'Question 3'}, ... 'Callback', @change_question, ... 'Position', [10, 400, 100, 20]); % Create a string variable str which contains the question corresponding % to which value is currently set in the popup menu switch pulldown_menu.Value case 1 str = q{1}; case 2 str = q{2}; case 3 str = q{3}; end % Create a text label whose string is set to str. label = uicontrol('Parent', f, ... 'Style', 'text', ... 'String', str, ... 'HorizontalAlignment', 'left', ... 'Position', [10, 300, 400, 17]); % Create an entry widget positioned so that it is just to the right of the % longest question. Its position is static not changing if the question % changes. entry = uicontrol('Parent', f, ... 'Style', 'edit', ... 'Position', [420, 300, 50, 20]); %%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Callback function %%% %%%%%%%%%%%%%%%%%%%%%%%%%%% function change_question(source, events) % Get the selected field of the pulldown menu N = source.Value; % Assign text depending on which value was chosen switch N case 1 str = q{1}; case 2 str = q{2}; case 3 str = q{3}; end % Adjust the string of the text uicontrol label.String = str; end end
Figure 1 shows the pulldown menu. Question 1, the longest, is selected and an edit uicontrol is just to the right of the text.
Figure 1: Pulldown menu selects question displayed below. |
We can see what happens when the question is changed. This is shown below in Figs 2a and 2b. The edit uicontrol stays in the same place while the length of the text changes. Depending on the project and complexity of the interface being built, this may lead to a less than aesthetic-looking design.
|
|
In my real-world case, there were perhaps 15 different possible configurations, and since this was developed over a period of several months, I handled them manually. But I always found this unsatisfying.
Unfortunately, MATLAB itself doesn't have a way to estimate the size of a uicontrol based on the text contained. We need first to get the handle to the Java object using the utility findjobj. Then, the workaround for the sizing issue is to use the getPreferredSize method. This method essentially gives hints to the layout manager as to the best size to make the widget
So I added a function, calculate_widget_positions, that gets the preferred size of the text uicontrol, and returns this number along with the position of the edit control calculated from the text size plus some amount of padding. This function is shown below.
function [width_label, x_entry] = calculate_widget_positions()
% Get the text uicontrol suggested width width_label = jlabel.getPreferredSize().getWidth(); % Add in some padding. The edit control starts this amount after % the text control ends padding = 15; % Horizontal starting position of the edit uicontrol x_entry = label.Position(1) + width_label + padding; end
The callback function is then modified to adjust the position of the edit uicontrol after setting the text.
function change_question(source, events) % Get the selected field of the pulldown menu N = source.Value; % Assign text depending on which value was chosen switch N case 1 str = q{1}; case 2 str = q{2}; case 3 str = q{3}; end % Adjust the string of the text uicontrol label.String = str; drawnow; [width_label, x_entry] = calculate_widget_positions() p = label.Position; p(3) = width_label; label.Position = p; p = entry.Position; p(1) = x_entry; entry.Position = p; end
Note also the use of the drawnow function after changing the string of the text control. This needs to be here so that MATLAB is forced to draw the widget. We can then get its new size for positioning the entry box. Perhaps if I were a little more clever in coding this, we could avoid this step, but this is more a proof-of-concept thing than something I am about to drop into production code. Now, for example when the third question is selected, the resulting window looks like Fig. 3 below.
Figure 3. The position of the edit entry box is determined from the size of the preceding text. |
So though MATLAB doesn't naively have a way to address this issue, we can work around it by fiddling under the hood with the Java methods that the uicontrols themselves use.
For the sake of completeness, here is the full code including the dynamic positioning.
function dynamic_labels clear all; close all; clc f = figure(); % List of question that will appear to the left of an edit uicontrol. q = {'How much wood could a woodchuck chuck if a woodchuck could chuck wood? ', ... 'What is the airspeed velocity of an unladen swallow? ', ... 'What is the meaning of the universe? '}; % Create the popup uicontrol and make its entries out list of questions % The menu's callback function changes the text in the text uicontrol % defined below pulldown_menu = uicontrol('Parent', f, ... 'Style', 'popupmenu', ... 'String', {'Question 1', 'Question 2', 'Question 3'}, ... 'Callback', @change_question, ... 'Position', [10, 400, 100, 20]); % Create a string variable str which contains the question corresponding % to which value is currently set in the popup menu switch pulldown_menu.Value case 1 str = q{1}; case 2 str = q{2}; case 3 str = q{3}; end % Create a text label whose string is set to str. label = uicontrol('Parent', f, ... 'Style', 'text', ... 'String', str, ... 'HorizontalAlignment', 'left', ... 'Visible', 'on', ... 'Position', [10, 300, 100, 17]); % Get the handle to the Java object jlabel = findjobj(label); % Create an entry widget positioned so that it is just to the right of the entry = uicontrol('Parent', f, ... 'Style', 'edit', ... 'Visible', 'on', ... 'Position', [420, 300, 50, 20]); % Set the initial position of the edit uicontrol [width_label, x_entry] = calculate_widget_positions(); p = label.Position; p(3) = width_label; label.Position = p; p = entry.Position; p(1) = x_entry; entry.Position = p; %%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Callback function %%% %%%%%%%%%%%%%%%%%%%%%%%%%%% function change_question(source, events) % Get the selected field of the pulldown menu N = source.Value; % Assign text depending on which value was chosen switch N case 1 str = q{1}; case 2 str = q{2}; case 3 str = q{3}; end % Adjust the string of the text uicontrol label.String = str; drawnow; [width_label, x_entry] = calculate_widget_positions() p = label.Position; p(3) = width_label; label.Position = p; p = entry.Position; p(1) = x_entry; entry.Position = p; end function [width_label, x_entry] = calculate_widget_positions() % Get the text uicntol suggested width width_label = jlabel.getPreferredSize().getWidth(); % Add in some padding. The edit control starts this amount after % the text control ends padding = 15; % Horizontal starting position of the edit uicontrol x_entry = label.Position(1) + width_label + padding; end end
No comments:
Post a Comment