MATLAB Answers

0

Nested function capability with non-nested functions for GUIs

Asked by D. Plotnick on 29 Jul 2019
Latest activity Commented on by TADA
on 1 Aug 2019
Hello all,
I am working on a GUI which started off rather small and simple, and so I used nested functions for handling the GUI itself. I am using the examples in the GUI Layout Toolbox as a guide. The code has ballooned, and from a simple organizational standpoint I would like to move the various sub-sections (individual panel creations, callbacks, etc.) into seperate *.m files. However, I also want maintain the ease-of-use from the nested functions.
As an example, another little random motion simulator. Here, I am handling the plotting and data calculations in nested functions. The data calculations (namely the motion speed) are dependent on values of the UI (the slider). During the loop, there is a check for data values which then changes the plotting parameters. In other words
  • Nested functions get values from the low-level GUI function (gui,data,state)
  • Nested functions set these values as well.
So, how can I retain this functionality while moving these sub-functions into their own files? Do I have to start returning the value of gui from each sub-function? Do I need to create a guiClass < handle so that I can modify the values directly from high level functions (possibly several layers deep)? Is there a way to migrate this away from nesting, without having to funamentally re-write the code? Is this any kind of way to run a railroad? Globals bad.
-DP
MWE:
function myGUI
gui = initializeGUI();
data = initializeData();
state = initializeState();
runLoop
function gui = initializeGUI()
gui.Window = figure('Name','Brownian Motion',...
'NumberTitle' , 'off',...
'MenuBar' , 'none',...
'ToolBar' , 'none',...
'HandleVisibility' , 'off' ...
);
gui.Panel = uipanel(gui.Window);
gui.Ax = axes('Parent',gui.Panel);
gui.Slider = uicontrol(gui.Panel,...
'Style','slider','Min',0.001,'Max',0.1,'Value',0.01);
% Do more initializing things
end
function data = initializeData()
data.sz = 100;
data.lim = 1.5;
data.x = randh(data.sz);
data.y = randh(data.sz);
end
function state = initializeState()
state.running = true;
end
function runLoop
plotData
while state.running
updateData
checkLims
refreshPlot
pause(0.02);
end
end
function plotData
gui.plot_h = plot(data.x,data.y,'.','Parent',gui.Ax);
axis(gui.Ax,[-data.lim data.lim,-data.lim,data.lim]);
drawnow;
end
function refreshPlot
set(gui.plot_h,'XData',data.x,'YData',data.y);
axis(gui.Ax,[-data.lim data.lim,-data.lim,data.lim]);
drawnow;
end
function updateData
spd = get(gui.Slider,'Value');
data.x = data.x + randh(data.sz)*spd;
data.y = data.y + randh(data.sz)*spd;
end
function checkLims
mx = max(abs([data.x;data.y]));
if mx > data.lim
data.lim = mx;
end
end
function v = randh(n)
v = 2*(rand(n,1) - 0.5);
end
end

  5 Comments

I'm not a fan, but you can use dynamicprops to mimic the struct dynamic fields
classdef StructLikeClass < dynamicprops
methods
function this = StructLikeClass()
this.addprop('x');
this.x = 'Blah';
this.addprop('y');
this.y = 1:100;
end
end
end
obj = StructLikeClass();
obj =
StructLikeClass with properties:
y: [1×100 double]
x: 'Blah'
Well, if the class represents the GUI I would have thought it quite natural to extend the class with new (hard-defined) properties when you extend the GUI. Surely the GUI isn't permanently fluid? It may change a lot while you are developing it, but you aren't going to be constantly adding and removing components are you?
You add your fields to the GUI object in your current example, the only real difference is you have to add one line to a properties block if it is in a class, but on the plus side it is also easy to see exactly what properties a class has, unlike a struct where you have to search through the code to know what you added to it or look at it in a debug situation to see what its fields are.

Sign in to comment.

0 Answers