how to return values from .mlapp to the .m caller

129 views (last 30 days)
I made a matlab application (.m) for which I want to setup some parameters using a parameter window built with appdesigner (.mlapp). I was perfectly able to send values from .m to .mlapp, but I am not able to make the oposite i.e. return values from the .mlapp to .m.
My parameter window just contains 2 sliders.
There is probably something I dont understand. Can someone help me on this. Thanks
  3 Comments
Bart McCoy
Bart McCoy on 14 Nov 2019
Edited: Bart McCoy on 14 Nov 2019
How to pass values in and out of an app.
Took me a bit to figure it out, but it's not hard.
PASSING DATA INTO AN APP DESIGNER OBJECT:
This has been discussed elsewhere, so I'll be more brief. After you design an app, you can call it from any MATLAB code just like you would a function. The latest AppDesigner (it's in the 2018 versions) allows you to pass arguments into an app.
To setup an input argument to your app, go to the AppDesigner tool. Click on [EDITOR] tab/ribbon at top. On the ribbon, click "App Input Arguments". You can add variable names to be passed into the app. Within your app, input args enter through the public "startupFcn" function. That's where you retrieve the data you passed in. Within "startupFnc", grab the var you passed in and assign your input to a public property of your "app" object... To create a public property, go to the same ribbon and click "Property" to add it.
% EXAMPLE: PASSING VALUES INTO AN APP
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% FROM YOUR EXTERNAL SCRIPT:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Some data storage class you created
myVar = DataStorage();
% Call your app, passing in myVar
myapp = SomeAppDesignerApp(myVar);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% FROM INSIDE YOUR APP DESIGNER APPLICATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Startup function for the app
startupFcn(app, myVar)
% Store myVar in the app object, or it will be lost when startupFcn
% finishes.
app.myVar = myVar;
end
PASSING DATA OUT OF AN APP DESIGNER OBJECT.
The method described here works for 2 common cases
a) Giving the app an object so the app can stored data in it; that data persists even after the app window has been closed and the app object deleted
b) For concurrent communication (while the app is running) between the app and the script that calld the app; concurrent communications between one or more running apps.
You can call an app just like a function, pass data in, but when you finally terminate the app, it seems that all data is lost. That's true if you pass data in by value (pass in a normal class, structure, array, etc). Once you interact with the app, the window is destroyed, and everything inside it is gone. It doesn't return values.
HOWEVER, if you pass in handle objects into the app, your data persists after the app window/object vanishes/deleted. In effect, a handle object operates like pass-by-reference.
You use a handle object like any other var. Pass it into your app startupFcn(app, myVar), store it in the app as app.myVar. When you want to store a result, just store it in app.myVar.result1 or whatever you name it. If myVar was a NORMAL var, that data would be lost when the app closes. When myVar is a handle object, it's storing it in the variable outside of the app. When you exit your app and the app is deleted from memory, your data is safe in myVar.
So how do you create a handle object to pass into your app?
Lookup help for a handle class to get all the details. But here's a quick example, passing in a data storage variable, 'ds'.
% EXAMPLE: GETTING DATA OUT OF THE APP
% FIRST, CREATE A SIMPLE HANDLE-CLASS OBJECT FOR STORING STUFF
% SAVE THIS AS "DataStorage.m" IN A SEPARATE FILE
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% THIS IS A HANDLE-CLASS, JUST FOR STORING STUFF.
% WITHIN THE MATLAB SCRIPT THAT LAUNCHES YOUR APP, CREATE AN
% INSTANCE OF THIS HANDLE CLASS AND PASS IT INTO YOUR APP WHEN
% YOU LAUNCH IT. THE APP WILL STORE STUFF IN IT.
% SINCE THIS IS A HANDLE CLASS, WHEN THE APP STORES DATA IN IT,
% IT'S MODIFYING THE *ORIGINAL* OBJECT CREATED IN YOUR SCRIPT,
% OUTSIDE OF YOUR APP.
% WHEN THE APP IS DESTROYED, YOUR DATA STILL EXISTS IN YOUR SCRIPT.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
classdef DataStorage < handle % <===== THIS LINE MAKES IT A HANDLE CLASS
properties
% We can store any amount of data in this empty structure
dataArea = struct();
end
methods
function obj = DataStorage(obj)
return;
end
end
end
% EXAMPLE: GETTING DATA OUT OF THE APP
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% FROM INSIDE YOUR MATLAB SCRIPT THAT CALLS YOUR APP
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Create an instance of our custom DataStorage HANDLE class.
% It has an empty structure ds.dataArea ==> can store anything in it
ds = DataStorage();
ds.dataArea % Show contents- empty
% Create an instance of your App Designer application,
% passing variable 'ds' into the app.
% Your app/gui will store stuff in 'ds'.
% The code is stuck at this line until your app closes, which destroys 'myapp'
% But the data it stored in your HANDLE class 'ds' object is safe & sound.
myapp=myNewApp(ds);
% At this point, the 'myapp' instance is gone, but the data it
% stored in 'ds' is still stored in 'ds'
% Print the value that the app stored in the empty structure, ds.dataArea
fprintf(1, 'Inside your app/GUI, you selected choice "%s"\n', ds.dataArea.UserSelection);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% INSIDE YOUR APP DESIGNER APPLICATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%--------------------------------------------------------------
properties (Access = public)
ds = -1; % Initial value before the startupFcn is called
end
%--------------------------------------------------------------
% This public function is called when your external script creates
startupFcn(app, ds)
% ds is the value passed into the app.
% Store it in
app.ds;
end
%--------------------------------------------------------------
% PushButton callback, executed when you press the button
somePushButtonCallback(app)
% Store the user's choice from the combo list
ds.dataArea.UserSelection = app.ComboList1.value;
end
%--------------------------------------------------------------
GENERAL COMMENTS ABOUT APP DESIGNER:
If you haven't updated AppDesigner in a year or so, then you might want to do that. It's had SIGNIFICANT improvements over a short period of time. There are lots of negative comments that are more related to the old "GUIDE" app. I've also used GUIDE as well as AppDesigner and they are vastly different.
GUIDE is the old function-based way of doing a GUI. References to the parent object must be passed into every callback, to get data in and out. User is forced to navigate the heirarchy of parent/children objects.
AppDesigner is object oriented and all functions have access to the top-level App object called "app". In any function, the user can directly address every object in the heirarchy with little effort.... very nice.
Adam
Adam on 15 Nov 2019
Object-oriented class based apps are definitely nice. Whether AppDesigner's version of them is nice is debatable, but the fact you have to do the above (which is my usual way to do class-based apps programmatically for the most part) is a huge downside.
You should consider adding it as an answer though since such methods are forced upon people and many are not familiar with using classes.
When I write a class that represents a UI/App myself and put it together programatically I can retain control over all these things and unless I want it to the class that is associated with the UI doesn't self-delete and take all its stored properties with it when you close the UI itself. I don't know if this is compulsory behaviour of an AppDesigner app or if there is a way you can tell it not to self-destruct the whole class when you close the window, but it is silly, even though my advice for more advanced creation of multi-window GUIs is always to pass handle-derived objects between them to define communication and data sharing.
The idea that you can't create a simple single window app that just stores a few properties that you can then access simply from the class itself after the window has closed is another reason I would hesitate to even go back to try AppDesigner.
It may be that some day AppDesigner will evolve into a tool for expert users as well as a 'lead you by the hand' tool for newer programmers, but until it does I find even GUIDE more useful myself, on those increasingly rare occasions I don't create a programmatic UI. I haven't done a programmatic UI using uifigures yet though. I guess I'll have to transition to them soon before it is forced upon me!

Sign in to comment.

Accepted Answer

Adam
Adam on 25 Jan 2019
Edited: Adam on 25 Jan 2019
An App designer UI is just a class. You can add functions and properties to this however you wish and access these from the scope in which you create the UI object.
In fact, last time I checked the uneditable code of the app designer UI had the ghastly feature that all the uicomponent properties have public access so all you need to do is something like this:
myApp = App( );
sliderVal = myApp.slider1.Value;
You will need to edit that as appropriate for your app name, slider name within the app etc and I don't remember off-hand if the property is called 'Value' for an app designer slider, but I assume it is.
  4 Comments
pascal plisson
pascal plisson on 25 Jan 2019
Thanks Adam for your answer, but it is not exactly what I want to do.
In my main app (.m), on a given event, I open the parameter window (.mlapp) in order to setup my sliders. This window is like a dialog window, with a cancel and an OK button. I want to transfer the slider.Value to my main app when I clic on OK, and then delete the parameter window.
What I notice is when I start my parameter window, both process (main app and parameter window) run concurrently. Main app doesn't wait for the end of parameter windows.
Any idea ?
Adam
Adam on 25 Jan 2019
This comes down to the GUI behaviour as to whether it is modal or not and whether your main program is being told to wait for it.
A class-based GUI can close down the GUI component of itself (i.e. the figure) whilst the object of the class still persists so if, on clicking OK, you store the values of the sliders in properties of the class then you can access these back in the main app as above, if you have set it up correctly to wait for the 2nd dialog to close. I'm not totally familiar with how to do this with appdesigner though.

Sign in to comment.

More Answers (3)

pascal plisson
pascal plisson on 27 Jan 2019
Finaly, I finished to do someting working. The solution I found is not so elegant but it works. If someone has a better solution, he is welcome.
1) I did not rewrite my parameter window using GUI as sugested by Rick here above. I consider that when the software editor makes the effort to provide something new which is better than what he was providig before, we should make the effort to use it, even if it doesn't completely fullfill its previous tool. In that way, I dont agree with the Rick's comment. Things will improve release after release.
2) What I did : in my .mlapp file I added a public property called DONE. this is a kind of semaphore initialized to 0 at the beginning and set to 1 when I push the OK button. I also added a public function called CLOSETHEWINDOW that delete the window.
in my .m app I perform polling waiting for the .mlapp completion :
app = myParameterWindow(param1, param2); % create the parameter window
while app.done == 0 % polling
pause(0.05);
end
value1 = app.propertie1; % get the values set in the parameter window
value2 = app.property2;
app.closeTheWindow; % delete the parameter window
3) I think that appdesigner is a good tool. Not yet enought mature but going in the right way.
I suggest the development team to provide several templates of application that you can select when you create a new application. I suggest to provide a template for modal application in addition to the current one.
  2 Comments
Jonathan Avesar
Jonathan Avesar on 5 Jun 2020
Any updates on this? I have the same problem. Want to access public properties only after the user finishes and the .mlapp is closed.
Only thing I can think of is to use the "non-elegant" solution that 'pascal plisson' mentioned above
Vasista Adupa
Vasista Adupa on 20 Jun 2020
Please post the changes to be made in .mlapp file. Thank you.

Sign in to comment.


Rik
Rik on 25 Jan 2019
Since you only use a few objects, it would probably be easier to switch to GUI instead of an mlapp.
If you use a figure, you can use uiwait to stop your main function from continuing without the input of your parameter window.
  1 Comment
Rik
Rik on 25 Jan 2019
Also, don't use GUIDE for more than just figuring out what button positions make sense:
My small guide to avoid GUIDE:
  • Make a figure (with f=figure;) and look into the doc for figure which properties you want to turn off (you probably want to set Menu and Toolbar to 'none')
  • Create buttons and axes and everything you need with functions like uicontrol and axes. Save the handles to each element to fields of a struct (like handles.mybutton=uicontrol(___);)
  • When you've finished loading all data (and saving it to fields of your handles struct), and creating all the buttons, save your handles struct to the guidata of your figure like this guidata(handles.f,handles);. (You can also use getappdata and setappdata)
  • You can set the Callback property of many objects. If you do, use a function name with an @ in front, or a char array that can be evaluated to valid code. (like @MyFunction or 'disp(''you pushed the button'')')
  • Callback functions will be called with two arguments: the first is a handle to the callback object, the second is eventdata that may contain special information. To get access to your data, just use handles=guidata(gcbo);. You can replace the gcbo function with the name of the first input to your callback function if you prefer.
  • More information about callbacks can be found in multiple places in the doc, for example here.

Sign in to comment.


Max Spectrumdyn
Max Spectrumdyn on 27 Oct 2021
To expand further on Pascal Plisson's solution:
1)On the mlapp application side:
1.a - in my GUI.malpp application I have let's say 5 different edit Field (app.Field1,..,app.Field5) and 2 push buttons (Cancel and Proceed).
1-b In the startupFcn callback I reset a flag to 0. This flag will be modified to a different value depending on the button used by user. I chose as flag the app.UIFigure.UserData. The code is as follow:
app.UIFigure.UserData='0';
1.c I define a callback for each button when they are pressed. In this callback I can modified the value of the flag:
% Button pushed function: CancelButton
function CancelButtonPushed(app, event)
% Uppon cancellation, the GUI disappears and UIFigure.UserData is modified to 2
app.UIFigure.Visible='off';
app.UIFigure.UserData = '2';
2) Now back to the main matlab.m code
Remeber that my app is called GUI.mlapp and I want to retriev the user data in the main code
data=GUI(inputargument1, Inputargument2,...); %Inpuargument are optionnal, depending on your app
Now all the user input in the app are transferred to the data variable I just define. And i can access each of the data using the following code
Userdata1=data.Field1.Value;
Userdata2=data.Field2.Value;
%Field1, Field2,... being the data input fields that are define in mlapp app.
After calling the GUI, the code will proceed without waiting for the user to actually fill the various fields in the GUI, I therefore recommand to add a waitfor function for the code to pause until the user presses one of the button in the GUI
data=GUI(inputargument1, Inputargument2,...);
waitfor(data.UIFigure,'UserData'); % the code will wait until there's a modification in the data.UIFigure.UserData field
Only now you can close the GUI.mlapp from the main code (if yu close it before the user input will not be transfered to the main code!)
Now you just have to modifythe name of the various variable according to your needs.

Categories

Find more on Develop Apps Using App Designer in Help Center and File Exchange

Products


Release

R2018b

Community Treasure Hunt

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

Start Hunting!