Creating custom and reusable user interface control - matlab

In some GUI of my own I have created many controls (using uicontrol) to allow users configuring a filter used during later processing stage.
The filter edition consist in a combobox to select the filter type, plus many editboxes that update upon selected filter type and many callbacks to react upon user inputs.
I now need to add this filter selection in another GUI, and, of course, I don't want to copy-paste all the logic I have already done and would prefer to create some custom control that I can reuse as easily as:
filterEditor = uifilter('Parent', gcf);
set(filterEditor, 'FilterDescription', 'Cylinder (r = 45 cm, h = 1 m)');
set(filterEditor, 'Callback', #onFilterEditModified);
Is there a standard procedure to create custom "uicontrol" objects ? I searched the internet and matlab's documentation but did not find any good pointer yet ...
Currently I'm thinking to create custom class deriving from hgsetget:
classdef uifilter < hgsetget
properties
% Local properties
FilterDescription;
Callback;
end
properties(SetAccess=private, GetAccess=private)
% Internal controls
globalContainer;
comboFilterType;
edit1;
end
methods
function [this] = uifilter(varargin)
% Create a global `uicontainer` to hold my controls
[localPVpairs, genericPVpairs] = separatePVpairs(varargin{:});
this.container = uicontainer(genericPVpairs{:});
% Create my own controls and logic
this.comboFilterType = uicontrol('Parent', this.container, ...);
this.edit1 = ...
end
end
end
in order to mimic uicontrol behavior (set, get, findobj, etc...) but maybe there's more standard approach or some base class other than hgsetget to start from (i.e. some base class with Visible, Enable, HitTest etc... already defined with default implementation)?

I think this would be the right approach.
To do it properly, you'll probably need to implement your own set and get methods for each uicontrol property. These set and get methods will mostly just pass through values to and from the underlying uicontrol. You can probably get away without implementing some of the less-used properties in your first draft (e.g. FontAngle), adding them in as necessary and just living with the uicontrol defaults until then.
In some cases though, they'll need to do more, and you'll need to exercise a bit of care when you implement things such as set for the Parent property (it may need to destroy the original uicontrol and create a new one for the new parent). You'll also need to exercise care when implementing set for the Position and Units properties - for normal uicontrols they interact in quite a complicated way, and I think the outcome can sometimes depend on which is set first.
I'd also suggest that for the internal properties, as well as setting them to private, you might also set them to Hidden, to prevent users from trying to meddle with them.
One last point - I think, from some of your other questions, that you're making use of GUI Layout Toolbox. I haven't thought it through much, but you might need to think ahead about whether you need to do anything special to enable that.

Coming back on the issue, a very simple approach (equivalent to define a custom class inheriting from hgsetget or possibly some uicontrolbase class to have default behavior for Enable, Position, etc...) is to create a class inheriting from uiextras.Container in the GUI Layout toolbox.
Indeed this class is fully equivalent to the idea of having a uicontrolbase class. It exposes a protected UIContainer property which is the panel in which to put all child elements, so it is very easy to build reusable compound component from it:
classdef uimyfilter < uiextras.Container
%% --- Exposed properties
% NB: Can be accessed with set/get routines
properties(Dependent, Transient)
FilterDescription;
Callback;
end
methods
{% ... own custom set/get logic for exposed properties ... %}
end
%% --- Lifetime
methods
function [this] = uimyfilter(varargin)
% Consume or init local properties from varargin list
[c, otherPvPairs] = uimyfilter.extractOrInitPvPairs(varargin, { ...
'FilterDescription', #()'Cylinder (r = 10 cm, h = 42 cm)'; ...
'Callback', #()[]; ...
});
% Call superclass with other pv pairs
this#uiextras.Container(otherPvPairs{:});
% Build interface
grid = uiextras.Grid('Parent', this.UIContainer, 'Spacing', 5, 'Padding', 5);
c.handles.cbFilterType = uicontrol('Parent', grid, 'Style', 'Popup', 'String', { 'Cylinder', 'Sphere' }, 'Callback', #(s,e)onFilterTypeChanged(this,s,e));
uiextras.Empty('Parent', grid);
c.handles.cardFilterParams = uiextras.CardPanel('Parent', grid);
uiextras.Empty('Parent', grid);
set(grid, 'ColumnSizes', [90, -1]);
set(grid, 'RowSizes', [23, -1]);
uicontrol('Parent', c.handles.cardFilterParams, 'Style', 'Text', 'String', '... todo: params for cylinder ...', 'BackgroundColor', 'r');
uicontrol('Parent', c.handles.cardFilterParams, 'Style', 'Text', 'String', '... todo: params for sphere ...', 'BackgroundColor', 'r');
% Store local properties and handles for later calls
this.state = c;
% Init Gui
this.refresh();
end
end
%% --- Internal logic
methods(Access=private)
function [] = refresh(this)
set(this.state.handles.cardFilterParams, 'SelectedChild', get(this.state.handles.cbFilterType, 'Value'));
end
function [] = onFilterTypeChanged(this, s, e) %#ok
this.refresh();
if (~isempty(this.state.Callback)),
this.state.Callback(this);
end
end
end
methods(Access = protected)
function [] = redraw(this) %#ok
end
end
properties(GetAccess=private, SetAccess=private)
state;
end
%% --- Helpers
methods(Static, Access=protected)
function [c, otherPvPairs] = extractOrInitPvPairs(pvPairs, consumeDescriptor)
% Check arguments
if (nargin < 2),
error('Not enough input arguments.');
end
if (~isempty(consumeDescriptor) && ...
(~iscell(consumeDescriptor) || ~ismatrix(consumeDescriptor) || ...
~iscellstr(consumeDescriptor(:, 1)) || ~all(cell2mat(cellfun(#(x)isa(x, 'function_handle'), consumeDescriptor(:,2), 'UniformOutput', false)))))
error('Invalid descriptor for properties to consume.');
end
if (~iscell(pvPairs) || (~isvector(pvPairs) && ~isempty(pvPairs)) || (length(pvPairs(1:2:end)) ~= length(pvPairs(2:2:end))) || ~iscellstr(pvPairs(1:2:end)))
error('Invalid list or property names/values pairs.');
end
% Consume local properties
c = struct();
otherNames = pvPairs(1:2:end);
otherValues = pvPairs(2:2:end);
for ki = 1:size(consumeDescriptor, 1),
pname = consumeDescriptor{ki,1};
pinit = consumeDescriptor{ki,2};
idx = strcmpi(otherNames, pname);
if (isempty(idx)),
c.(pname) = pinit();
elseif (isscalar(idx)),
c.(pname) = otherValues{idx};
otherNames(idx) = []; otherValues(idx) = [];
else
error('Property `%s` appears more than once.', pname);
end
end
% Recompose other pv
otherPvPairs = cell(1, 2*length(otherNames));
otherPvPairs(1:2:end) = otherNames(:);
otherPvPairs(2:2:end) = otherValues(:);
end
end
end
Exposed properties and internal logic is of course fully tied to the compound component to have anyway, building interface is as simple as adding uicontrol or uiextras.(...) objects to this.UIContainer.
PS: For R2014b and later, you have to inherit from uix.Container in GUI Layout toolbox for HG2 anyway the idea is similar.

Related

How to declare inheritance of uicontrol objects in MATLAB

I'm learning how to programmatically create an app in MATLAB with a graphical user interface. I'm using MATLAB 2015a.
I don't understand why I am getting this error:
Error using GuiTitle
The specified superclass 'uicontrol' contains a parse error, cannot be found on MATLAB's search path, or
is shadowed by another file with the same name.
I'm trying to make a class called GuiTitle that has uicontrol as the superclass. My class GuiTitle looks like this:
classdef GuiTitle < uicontrol
methods
function obj = GuiTitle(text)
if nargin == 0
text = '';
end
obj#uicontrol('Style', 'text', 'String', upper(text));
end
end
end
Here is my code:
function hello_gui
% Test GUI
GuiConstants % contains constants that
GuiTitle %%
f = figure('Visible','off','Position',[POS_FROM_LEFT,POS_FROM_BOTTOM,...
WINDOW_WIDTH,WINDOW_HEIGHT]);
set(f, 'MenuBar', 'none')
titleText = 'process variable names';
%title = uicontrol('Style', 'text', 'String', upper(titleText));
title = GuiTitle(titleText) %%
title.Position = [0, 0, WINDOW_WIDTH, WINDOW_HEIGHT];
title.FontSize = FONT_SIZE;
f.Visible = 'on';
end
When I comment out lines with %% and uncomment
title = uicontrol('Style', 'text', 'String', upper(titleText));
The window displays properly:
What am I missing?
uicontrol is a function that creates an object of type matlab.ui.control.UIControl:
h = uicontrol;
class(h) % returns 'matlab.ui.control.UIControl'
However, this class is sealed and cannot be used as a superclass:
classdef myclass < matlab.ui.control.UIControl
...
>> a=myclass
Error using myclass
Class 'matlab.ui.control.UIControl' is Sealed and may not be used as a superclass.
Note that GUIs in MATLAB are designed very differently from what you might be used in other languages. There is no need to derive from UI classes to change their behavior, you define their behavior by setting callback functions:
h = uicontrol;
h.String = 'button';
h.Callback = #(src,event) msgbox('pressed the button!');

Can I code a deep learning layer without a backward function?

I want to create a layer that make two copies of the previous layer (input layer), here is my code:
classdef CopyLayer < nnet.layer.Layer
methods
function layer = CopyLayer(numOutputs,name)
% Set number of inputs.
layer.NumOutputs = numOutputs;
% Set layer name.
layer.Name = name;
% Set layer description.
layer.Description = "Make " + numOutputs + ...
" copies of the input layer";
end
function [varargout] = predict(X)
% Layer forward function for prediction goes here.
numOutputs = layer.NumOutputs;
[h,w,c] = size(X);
Z = zeros(h,w,c,numOutputs);
for i= 1 : numOutputs
Z(:,:,:,i) = X;
end
varargout = Z;
end
% function [] = backward()
% end
end
end
and when I try to create the layer by:
layer = CopyLayer(2,'copy');
the following error appears:
Abstract classes cannot be instantiated. Class 'CopyLayer' inherits
abstract methods or properties but does not implement them.
See the list of methods and properties that 'CopyLayer' must implement
if you do not intend the class to be abstract.
Error in SplitLayer (line 1)
layer = CopyLayer(2,'copy');
and I think it's because of the no existing of backward function. Is that correct? How can I resolve this error?
The error message is clear -- if you're used to reading error messages, and you're familiar with abstract classes.
A class is abstract if it contains elements that are not yet defined in a manner one could instantiate. They are intended particularly to be templates that the user can fill out to have the desired implementation details. nn.layer.layer is one such abstract class.
When you instantiate an abstract class, but fail to define every template element, then the result is another abstract class -- just with fewer "template" elements. That is what you've done: CopyLayer does not implement backward, so it's still abstract. When you instantiate with layer = CopyLayer(2,'copy'), you still have open elements, so you cannot make a concrete object from the class. Hence the error message.
You can work around this in a couple of ways:
Implement your own layer class, one that doesn't even mention a back-prop function. You'll have extra connection work to do, since you give up your inheritance from nn.layer.
Implement backward, but leave the body non-functional (empty). This will satisfy the compiler, and give you a very boring (but fast) back-prop stage.
thanks to Mr #Prune
the code after correction becomes as follow:
classdef CopyLayer < nnet.layer.Layer
properties
% (Optional) Layer properties.
NumOutputs
end
methods
function layer = CopyLayer(numOutputs,name)
% Set number of inputs.
layer.NumOutputs = numOutputs;
% Set layer name.
layer.Name = name;
% Set layer description.
layer.Description = "Make " + numOutputs + ...
" copies of the input layer";
end
function varargout = predict(layer,X)
numOutputs = layer.NumOutputs;
[h,w,c] = size(X);
Z = zeros(h,w,c,numOutputs);
for i= 1 : numOutputs
Z(:,:,:,i) = X;
end
varargout = Z;
end
function [dLdX] = backward(~,~,~,~,~)
end
end
end

MATLAB: Conditionally define get/set class methods

I have created some MATLAB classes to do some error-checking when I use certain types of structures. This improve development time by preventing errors in the code, but significantly slow down execution time.
One way to get around this is to comment out the set methods inside the class. Is it possible to do this programmatically? For example, only define these methods if a parameter in the constructor is true.
classdef MWE
%MWE Minimum working example
properties
A
B
C
end
methods
function obj = MWE(A, B, C)
if nargin ~= 3
error('A, B and C must all be provided.');
end
obj.A = A;
obj.B = B;
obj.C = C;
end
% function obj = set.A(obj, value)
% validate(obj, value, 'A');
% obj.A = value;
% end
%
% function obj = set.B(obj, value)
% validate(obj, value, 'B');
% obj.B = value;
% end
%
% function obj = set.C(obj, value)
% validate(obj, value, 'C');
% obj.C = value;
% end
end
methods (Access = private)
function validate(obj, value, name)
% Code here
end
end
end
Is it possible to do this programmatically? For example, only define these methods if a parameter in the constructor is true.
After some discussion, I see there are different ways of looking at your question. And, indeed, it may be that you cannot do what you are asking as interpreted by some. Here are two cases, however, that may suffice.
Case 1
You have computationally intensive or otherwise time consuming methods, that you use to "do some error-checking", in a development setting, but want to turn off in a production environment. And, these checks occur when the class is instantiated.
Place these methods in a wrapper function that is called from the constructor.
Here's an example
classdef classFoo
properties(Access=private)
fbar;
end
methods
function this = classFoo(arg1, argN, debugMode)
if(nargin>2 && debugMode)
if(~this.verifyStuff(arg1, argN))
throw(MException('classFoo:ConstructFailure','Could not verify'));
else
this.fbar = timeConsumingFunction();
end
else
this.fbar = 42; % defaultValue;
end
% continue construction
end
function didVerify = verifyStuff(this, varargin)
% complex code
didVerify = randi(2)-1; % 50/50
end
end
end
Then when creating objects you can choose to pass the debug mode flag as true like this:
a=classFoo(1,2,true)
or as false, like this:
a=classFoo(1,2,false)
or not at all, which is the same as the false case, like this:
a=classFoo(1,2)
Case 2
You have two different versions of a get/set method that you are commenting out depending on your development environment.
Add a private member parameter (e.g. isDebugging) and set it at time of construction. Now, instead of commenting out code in your get and set methods, you can handle the different cases with a simple if/else, predicated on your debug state like this:
classdef classFoo
properties(Access=private)
fbar;
isDebugging;
end
methods
function this = classFoo(debugMode)
if(nargin<1 || ~islogical(debugMode))
debugMode = false;
end
this.isDebugging = debugMode;
end
function setfbar(this, fIn)
if(this.isDebugging)
this.fbar = timeConsumingFunction(fIn);
else
this.fbar = fIn; % defaultValue;
end
end
end
end

Creating an unknown amount of uicontrols in matlab

So for a self project I'm creating a gui minesweeper like game in matlab and want to create an adjustable pushbutton grid however I'm not sure on how to do so. this is what I have got so far.
function createField()
xAmount = str2double(inputdlg('enter row length'));
yAmount = str2double(inputdlg('enter column length'));
for i = 1:xAmount
for j = 1:yAmount
%create buttons
end
end
end
One solution might be:
function create_field(hparent, nx, ny, width, padding)
% Test arguments
if ~test_parent_handle(hparent)
error('Parent must be a single valid graphic handle.');
elseif ~test_positive_integer(nx)
error('Number of buttons on X direction must be a scalar positive integer.');
elseif ~test_positive_integer(ny)
error('Number of buttons on Y direction must be a scalar positive integer.');
elseif ~test_positive_integer(width) ...
|| (width >= 100)
error('Button width must be a scalar positive integer smaller than 100.');
elseif ~test_positive_integer(padding) ...
|| (padding >= 20)
error('Button padding must be a scalar positive integer smaller than 20.');
end;
% Resize the parent to fit the button grid
set(hparent, 'Units', 'pixels');
ppos = get(hparent, 'Position');
ppos(3) = nx*width + (nx-1)*padding;
ppos(4) = ny*width + (ny-1)*padding;
set(hparent, 'Position', ppos);
% Create button grid
for p = 1:nx
for q = 1:ny
bpos = [ % Button spec:
(p-1)*(width+padding) % - X
(q-1)*(width+padding) % - Y
width % - W
width % - H
];
uicontrol( ...
'Units', 'pixels', ...
'Tag', sprintf('X%dY%d',p,q), ...
'Style', 'pushbutton', ...
'Parent', hparent, ...
'Position', bpos ...
);
end;
end;
% ----- NESTED FUNCTIONS -----
function tf = test_parent_handle(value)
tf = isscalar(value) ...
&& ishandle(value);
end
function tf = test_positive_integer(value)
tf = isscalar(value) ...
&& isreal(value) ...
&& isfinite(value) ...
&& (value > 0) ...
&& (fix(value) == value);
end
end
For a figure with 15 x 10 square buttons, each having the side 25 pixels with a padding of 3 pixels between the buttons, call:
create_field(figure(), 15, 10, 20, 3);
As with most problems there are many different approaches, having written something similar I'll give you the same prompts that I used when writing my helper function.
Your code is going to act based on what button is pressed so each button will need its own unique ID and properties. Depending on the MATLAB version used, each of your graphics elements will have a handle. Starting in R2014b, graphics objects can be addressed directly as objects rather than needing to utilize a numeric ID as a pointer.
Start with the figure window and check out the figure properties:
h.mainfig = figure; % Arbitrary figure window
get(h.mainfig); % Dump properties to command window
Right now we're probably most interested in the Units and Position properties of the main figure window, which you can use in a helper function to figure out how to size and space the buttons as you create them.
If we create a pushbutton graphics object with uicontrol() we're going to get mostly the same properties.
h.randombutton = uicontrol('Parent', h.mainfig, 'Style', 'pushbutton');
get(h.randombutton);
Again, we're interested in the Units and Position properties. We're also going to be interested in the Callback property, which is the function that executes when we interact with the button. Another good one is the Tag property, which can be used to set a unique string tag for each button for use with later logic.
You've probably noticed I'm using a structure array to store my graphics handles. This is similar to how MATLAB generates its object data when creating a GUI with GUIDE and has the huge advantage of a tidy data package to pass around our function. A great thing about structure arrays is that you can nest data structures, allowing us to easily generate and address graphics objects without needing to get clever with dynamic field references or eval() (yuck). Instead of having to do something like button_1, button_2, etc, we can do:
h.button(1) = uicontrol('Parent', h.mainfig, 'Style', 'pushbutton');
h.button(2) = uicontrol('Parent', h.mainfig, 'Style', 'pushbutton');
...
h.button(n) = uicontrol('Parent', h.mainfig, 'Style', 'pushbutton');
Now we know how to generate an arbitrary number of buttons programmatically and easily address them later on.
Other than the button generation we have another key function that I mentioned earlier, the button callback. Callback functions follow a slightly different syntax in that they natively pass along two arguments, the handle of the object whose callback is executing and the event data structure (see the documentation for more info). Because the function knows what UI object invoked it, we can make a pretty generic function.
Hopefully this helps!
Have you considered creating the ui in Java - undocumented matlab has some examples. Java would provide you nice LayoutManagers that will take care about resizing and more.

How to create an 'closure function' in Matlab as in python and js?

Background
In Python and JS we have closures, which returns a function with some of the variables pre-defined. e.g.
def make_printer(msg):
def printer():
print msg
return printer
Do we have similar stuff in Matlab?
Here I have a callback function
function show(object, eventdata)
that is to be set to the callback of my GUI
func = #show;
set(gcf, 'WindowButtonMotionFcn', func);
However, I want to add some additional parameters to this show function.
Currently I'm using global variables to do that. But I think it would be elegant if we have a 'closures function'.
About anonymous function
Yes we do have anonymous function in Matlab. However it seems to me that it is too simple to support 60-line procedures.
There was once a document floating about the internet by Sergey Simakov. It was very terse , not especially descriptive but covered the bases. This was in my experience the most authoritive text on matlab GUI's. I suspect it still is ...
You're solving two/three problems :
Closure Problem
Nested functions resolve this problem.
function iterator = count(initial)
% Initialize
if ~exist('initial','var')
counter = 0
else
counter = initial
end
function varargout = next() % [1]
% Increment
counter = counter + 1
varargout = {counter} % [1]
end
iterator = #next
end
Note(s) :
One may not simply return counter ! It must be wrapped in varargout or assigned to some other output variable.
Usage
counter = count(4) % Instantiate
number = counter() % Assignment
number =
5
Closed Scope + State problem
No ugly braces, worrying about scope, cell arrays of strings. If you need access to something it's under SELF no more FINDOBJ, USERDATA, SET/GETAPPDATA, GLOBAL nonsense.
classdef Figure < handle
properties
parent#double
button#double
label#double
counter#function_handle
end
methods
function self = Figure(initial)
self.counter = count(initial) % [1]
self.parent = figure('Position',[200,200,300,100])
self.button = uicontrol('String','Push', 'Callback', #self.up, 'Style', 'pushbutton', 'Units', 'normalized', 'Position', [0.05, 0.05, 0.9, 0.4])
self.label = uicontrol('String', self.counter(), 'Style', 'text', 'Units', 'normalized', 'Position', [0.05, 0.55, 0.9, 0.4])
end
function up(self,comp,data)
set(self.label,'String',self.counter())
end
end
end
Note(s):
One uses the function listed in the Closure problem above
Usage :
f = Figure(4) % Instantiate
number = get(f.label,'String') % Assign
number =
5
You may prefer :
f.label.get('String') % Fails (f.label is double not handle, go figure)
h = handle(f.label) % Convert Double to handle
number = h.get('String') % Works
number =
5
Since it looks like my comment was helpful, I'll explain what I mean about the struct thing. I think the way you are calling the callback functions is slightly different, so this may not work so well).
If you have a bunch of different callback functions, and a bunch of variables you want to pass to them, it is annoying and difficult to have a big list of input arguments for each function. Instead, I do something like this:
First create a bunch of UI components , but don't specify their callback functions, e.g
radio1_handle=uicontrol(panel1_handle,'Style','radiobutton',...
'Min',0,'Max',1,'Value',0,'Units','normalized','Position',[.8 .8 .2 .25]);
once you have made all the components, create a struct of variables you will be using
vars=struct('varName1',var1,'varName2',var2);
then update the UI components to include the callback functions
set(radio1_handle,'Callback',{#radio1_callback,vars});
now create the actual functions
function radio1_callback(object,eventData,vars)
So it's nothing fancy, just a potentially neater way than using multiple arguments.
Inspired by this question and #David's comment, I came up with the solution. (Maybe this is #David's answer, but not explicitly explained. So let me extend his comment into an answer.)
There is actually a way to add extra parameters to callbacks.
Just add the parameters to the end of the parameter list
function show(object, eventdata, extra)
and set the callback like this:
func = #show;
set(gcf, 'WindowButtonMotionFcn', func);
Reference
Mathworks
Passing Additional Input Arguments
You can define the callback function to accept additional input
arguments by adding them to the function definition:
function myCallback(src,eventdata,arg1,arg2)
When using additional
arguments for the callback function, you must set the value of the
property to a cell array (i.e., enclose the function handle and
arguments in curly braces):
figure('WindowButtonDownFcn',{#myCallback,arg1,arg2})