MATLAB Answers

How can I work around the fact that app designer does not support output arguments?

86 views (last 30 days)
I understand that the app designer does not permit output arguments. I am trying to work around that limitation. Some things I've tried include:
  1. I have tried to add properties/app data/etc., but found that the app handle gets deleted when the GUI is exited. One solution would be if there is a way to prevent the handle being deleted so the added app data stays accessible. I spent a long time fruitlessly trying to trace that down.
  2. Another way that would conceptually work would be to assignin to caller, but there are many levels of matlab inbuilt functions between the 'real' caller and the various callbacks and functions of the app. If there was a way to assign to a great great great Nth degree grandparent workspace, that would work, but I couldn't figure out how to assignin to anything other than 'base' or direct 'caller'. Similarly, I could assignin to base, but then I need to figure out how to get the data back down to the workspace that instantiated the app.
  3. I could simply save the data and load it later, but how do I trigger the calling function to load the data when it's available? I can manually add commands to check for and load data everywhere I use the app, but I'm not the only one that is likely to use this code and added complications make for mistakes.
  4. If I could setup some kind of listener at the 'real' caller level that could update that workspace, that might work too, but I'm not sure how to do that (or if it's possible given matlab's architecture)
The real driver of this is that I have various functions that are GUIs that are called by other functions (or even other GUIs) and I need to be able to pass data back to whatever called the data gathering GUI in the first place. Any ideas would be greatly appreciated!
Edit: Also... If I'm missing something and overcomplicating this, feel free to let me know what I'm missing. I hate to waste a bunch of time doing something inefficiently if there's an already tried and true simple way. I just want to be able to pop-up a customized GUI from within a function or higher level GUI, and get data from user interaction with images and video back to the calling function. (e.g. pops up live video and asks user to orient something and capture still, then confirm that still is subjectively suitable for purpose (or capture new still), then select points in still, and return points to caller)

Accepted Answer

Stephen Cobeldick
Stephen Cobeldick on 5 Sep 2019
Create a function which has two ways of being called:
  1. as a wrapper for the app itself, passing any input arguments, etc.
  2. as a callback, which stores whatever values you need using persistent or some nested function.
Set the function to be the DeleteFcn callback, when you can store the values you want.
Call the wrapper, which calls the app, use uiwait. The callback is triggered grab the values, which are then stored. uiwait resumes at that point, so you can return the values.
  3 Comments
Sean Cooper
Sean Cooper on 5 Sep 2019
Stephen,
Thanks for the idea. While I'm not sure how I would implement this as a function, the classdef /object version seems to work exactly as I hoped, with the only drawback of having to distribute the effect across two files.
(i.e.
  1. the first class is a handle subclass that spawns the second (which contains the GUI, and has the first class object as an input property), and holds the app handle and a placeholder for the desired data in properties. It returns a handle object to the calling workspace.
  2. Execution in the main app can continue on other tasks while waiting for the data to be returned, while the human operator interacts with the GUI.
  3. When the human is done, the GUI DeleteFcn main app can put the desired data in the data property of the original handle object returned by the first class using the handle object that was supplied at instantiation (and the calling function can determine at any time if that data is available by examining the handle object data property)
I don't think I would have thought of that solution without a lot of trial and error and beating my head against walls.
I was struggling to figure out how to get around the fact that the only output of apps from the app designer is a handle that self destructs on closing, and this circumvents that.
Thanks!

Sign in to comment.

More Answers (2)

Adam Danz
Adam Danz on 4 Sep 2019
Edited: Adam Danz on 5 Sep 2019
"I have various functions that are GUIs that are called by other functions (or even other GUIs) and I need to be able to pass data back to whatever called the data gathering GUI in the first place."
If your GUI was created with GUIDE or with uicontrol(), you can get outputs from the GUI but not in app designer. It's nice when GUIs are self-contained or only rely on built-in functions so that only the GUI file is needed to run the GUI in matlab. But if you have other functions or scripts that rely on a particular function, that function should be accessible outside of a GUI file. So, one solution would be to move the commonly used local function from the GUI to an external, independent function file. Afterall, GUI's are meant to be used by humans (or well trained non-human primates), not by other functions. So redesigning the flow of your code is probably the best solution.
In response to your listed options,
1) Forget about using the app structure outside of the app. There's no reason to save a bunch of graphics handles just to extract some embedded data. Besides, saving graphics handles can cause very large files and often times when you load the handle structure, it opens an additional instance of the GUI figure which is a nuisance.
2) Using assignin() or any of its friends causes more problems that what you started with. As you've correctly identified, this would be a cumbersome solution. Here's more background on why this is bad [link].
3a) "I could simply save the data and load it later"; If you can't move the local GUI functions to an external, independent function file, this may be a viable solution but it comes with more problems. The GUI can detect when it's been activated by a function rather than manually by a user. If it was activated by a function it could gather the needed outputs in a neat, tidy format and save it to a mat file. However, ...
3b) "but how do I trigger the calling function to load the data when it's available?" ...this is where things get complicated and ugly. I can imagine solutions for this problem but they all look like crumbling scaffolding around a demolished building in my imagination. For example, just before you call the GUI you could create an invisible graphics object. Just after calling the GUI you could add a while-loop that continually runs as long as the invisible graphics object exists (ishghandle). From within the GUI, when it's finished it searches for and deletes the inivisble graphics object which triggers the end of the while loop. Then the code can load the mat file into the function. This is a bad idea for so many reasons. What is the name of the file? Where does it exist? What if there's an error in the GUI and the while-loop runs for infinity? Why does a function need to run a GUI in the first place?
4) I don't see a big difference between this idea and idea #2.
  2 Comments
Adam Danz
Adam Danz on 5 Sep 2019
Hi Sean,
"The disconnect seems to be that you seem to expect GUIs to be stand-alone programs that don't need to interact with anything else."
Kind of the opposite, actually. IMHO, GUIs can certainly interact with other programs (and even other GUIs when necessary). It potentially gets messy when a automated script or function outside of a GUI needs to interact with a GUI.
"As such, my GUIs tend to be spawned to facilitate a specific human override of an otherwise automated analysis or decision point in a process, therefore the GUI is spawned as directed by the executing function and needs to return the critical decision data to that function."
If the GUI is spawned by an external function so that there can be human interaction, then the external function must wait for a human-made response from the GUI. If this is your case there is a simple solution. Once the human interaction is complete the user presses a 'continue' button which contains a callback function that gathers the data and sends it back to the external function. This is a common workflow. What I understood in your question is that you'd like an external function to call a GUI and get output in an automated fashion that does not require human interaction. In that case there's no need for a user-interface in the first place which is why I suggested making those dependent functions external from the GUI.
"With regard to saving the data... The name and location of the data is trivial, as it would be provided by the calling function, but how to trigger the caller to go get it without opening up a can of ugliness is a problem I don't have a solution to."
I have a suggestion that I have used myself several times in similar circumstances. When the external function is called, it detects whether a filename is present in the inputs. If it is not present, the function calls upon the GUI and then terminates. The GUI detects that it was called automatically (not by a human), does whatever processing is needed, and creates the output filename which is sent back into the external function as an input. This time the external function detects that a filename is present and therefore skips the initial GUI process and jumps right to the next stage of analysis that acts on the file. This method is clean, easy to write and read, and doesn't require assignin() or any behind-the-scenes magic with invisible objects that acts as flags. I'd be glad to get into more detail if needed.

Sign in to comment.


Mark Thomson
Mark Thomson on 23 Mar 2021
Dear All,
I also had the situation of wanting to use an App as an intermediate user interface to tailor and return parameters, called within a running function.
Here is a simple example that seems to work fine, similar to the suggestion above:
In the running function:
app_routine='test_external_call_with_output1';
app=feval(app_routine)
waitfor(app.FinishButton,'UserData')
output=app.output
app.delete
and in the App:
function FinishButtonPushed(app, event)
app.output=rand(3,4); % Just to demonstrate...
app.FinishButton.UserData=1;
end
where .output was a public property:
properties (Access = public)
output % Description
end
Maybe there is a cleaner way than than this use waitfor and setting .UserData (e.g. with uiwait?). But I don't see any major problem suspending a running function and getting outputs from an App before continuing the function.
Regards, MT

Products


Release

R2018a

Community Treasure Hunt

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

Start Hunting!