- Undocumented Matlab - https://undocumentedmatlab.com/blog_old -
AppDesigner’s mlapp file format
Posted By Yair Altman On August 17, 2016 | 10 Comments
Six years ago, I exposed the fact that *.fig files are simply MAT files in disguise [3]. This information, in addition to the data format that I explained in that article, can help us to introspect and modify FIG files without having to actually display the figure onscreen.
Matlab has changed significantly since 2010, and one of the exciting new additions is the AppDesigner [4], Matlab’s new GUI layout designer/editor. Unfortunately, AppDesigner still has quite a few limitations in functionality and behavior. I expect that this will improve in upcoming releases since AppDesigner is undergoing active development. But in the meantime, it makes sense to see whether we could directly introspect and potentially manipulate AppDesigner’s output (*.mlapp files), as we could with GUIDE’s output (*.fig files).
A situation for checking this was recently raised by a reader on the Answers forum: apparently AppDesigner becomes increasingly sluggish [5] when the figure’s code has more than a few hundred lines of code (i.e., a very simplistic GUI). In today’s post I intend to show how we can explore the resulting *.mlapp file, and possibly manipulate it in a text editor outside AppDesigner.
Apparently, *.mlapp files are simply ZIP files in disguise (note: not MAT files as for *.fig files). A typical MLAPP’s zipped contents contains the following files (note that this might be a bit different on different Matlab releases):
<?xml version="1.0" encoding="UTF-8" standalone="true"?> <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"> <Default Extension="mat" ContentType="application/vnd.mathworks.matlab.appDesigner.appModel+mat"/> <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/> <Default Extension="xml" ContentType="application/vnd.mathworks.matlab.code.document+xml;plaincode=true"/> <Override ContentType="application/vnd.openxmlformats-package.core-properties+xml" PartName="/metadata/coreProperties.xml"/> <Override ContentType="application/vnd.mathworks.package.coreProperties+xml" PartName="/metadata/mwcoreProperties.xml"/> <Override ContentType="application/vnd.mathworks.package.corePropertiesExtension+xml" PartName="/metadata/mwcorePropertiesExtension.xml"/> </Types>
<?xml version="1.0" encoding="UTF-8" standalone="true"?> <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"> <Relationship Type="http://schemas.mathworks.com/matlab/code/2013/relationships/document" Target="matlab/document.xml" Id="rId1"/> <Relationship Type="http://schemas.mathworks.com/package/2012/relationships/coreProperties" Target="metadata/mwcoreProperties.xml" Id="rId2"/> <Relationship Type="http://schemas.mathworks.com/package/2014/relationships/corePropertiesExtension" Target="metadata/mwcorePropertiesExtension.xml" Id="rId3"/> <Relationship Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="metadata/coreProperties.xml" Id="rId4"/> <Relationship Type="http://schemas.mathworks.com/appDesigner/app/2014/relationships/appModel" Target="appdesigner/appModel.mat" Id="rId5"/> </Relationships>
<?xml version="1.0" encoding="UTF-8" standalone="true"?> <cp:coreProperties xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties"> <dcterms:created xsi:type="dcterms:W3CDTF">2016-08-01T18:20:26Z</dcterms:created> <dcterms:modified xsi:type="dcterms:W3CDTF">2016-08-01T18:20:27Z</dcterms:modified> </cp:coreProperties>
<?xml version="1.0" encoding="UTF-8" standalone="true"?> <mwcoreProperties xmlns="http://schemas.mathworks.com/package/2012/coreProperties"> <contentType>application/vnd.mathworks.matlab.app</contentType> <contentTypeFriendlyName>MATLAB App</contentTypeFriendlyName> <matlabRelease>R2016a</matlabRelease> </mwcoreProperties>
<?xml version="1.0" encoding="UTF-8" standalone="true"?> <mwcoreProperties xmlns="http://schemas.mathworks.com/package/2014/corePropertiesExtension"> <matlabVersion>9.0.0.328027</matlabVersion> </mwcoreProperties>
appdesigner.internal.serialization.app.AppData
) the information about the uifigure, similar in concept to the *.fig files generated by the old GUIDE:>> d = load('C:\Yair\App3\appdesigner\appModel.mat') Warning: Functionality not supported with figures created with the uifigure function. For more information, see Graphics Support in App Designer. (Type "warning off MATLAB:ui:uifigure:UnsupportedAppDesignerFunctionality" to suppress this warning.) d = appData: [1x1 appdesigner.internal.serialization.app.AppData] >> d.appData ans = AppData with properties: UIFigure: [1x1 Figure] CodeData: [1x1 appdesigner.internal.codegeneration.model.CodeData] Metadata: [1x1 appdesigner.internal.serialization.app.AppMetadata] ToolboxVer: '2016a' >> d.appData.CodeData ans = CodeData with properties: GeneratedClassName: 'App3' Callbacks: [0x0 appdesigner.internal.codegeneration.model.AppCallback] StartupFcn: [1x1 appdesigner.internal.codegeneration.model.AppCallback] EditableSection: [1x1 appdesigner.internal.codegeneration.model.CodeSection] ToolboxVer: '2016a' >> d.appData.Metadata ans = AppMetadata with properties: GroupHierarchy: {} ToolboxVer: '2016a'
<?xml version="1.0" encoding="UTF-8"?> <w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"> <w:body> <w:p> <w:pPr> <w:pStyle w:val="code"/> </w:pPr> <w:r> <w:t> <![CDATA[classdef App2 < matlab.apps.AppBase % Properties that correspond to app components properties (Access = public) UIFigure matlab.ui.Figure UIAxes matlab.ui.control.UIAxes Button matlab.ui.control.Button CheckBox matlab.ui.control.CheckBox ListBoxLabel matlab.ui.control.Label ListBox matlab.ui.control.ListBox end methods (Access = public) function results = func(app) % Yair 1/8/2016 end end % App initialization and construction methods (Access = private) % Create UIFigure and components function createComponents(app) % Create UIFigure app.UIFigure = uifigure; app.UIFigure.Position = [100 100 640 480]; app.UIFigure.Name = 'UI Figure'; setAutoResize(app, app.UIFigure, true) % Create UIAxes app.UIAxes = uiaxes(app.UIFigure); title(app.UIAxes, 'Axes'); xlabel(app.UIAxes, 'X'); ylabel(app.UIAxes, 'Y'); app.UIAxes.Position = [23 273 300 185]; % Create Button app.Button = uibutton(app.UIFigure, 'push'); app.Button.Position = [491 378 100 22]; % Create CheckBox app.CheckBox = uicheckbox(app.UIFigure); app.CheckBox.Position = [491 304 76 15]; % Create ListBoxLabel app.ListBoxLabel = uilabel(app.UIFigure); app.ListBoxLabel.HorizontalAlignment = 'right'; app.ListBoxLabel.Position = [359 260 43 15]; app.ListBoxLabel.Text = 'List Box'; % Create ListBox app.ListBox = uilistbox(app.UIFigure); app.ListBox.Position = [417 203 100 74]; end end methods (Access = public) % Construct app function app = App2() % Create and configure components createComponents(app) % Register the app with App Designer registerApp(app, app.UIFigure) if nargout == 0 clear app end end % Code that executes before app deletion function delete(app) % Delete UIFigure when app is deleted delete(app.UIFigure) end end end]]> </w:t> </w:r> </w:p> </w:body> </w:document>
I do not know why the code is duplicated, both in document.xml and (twice!) in appModel.mat. On the face of it, this does not seem to be a wise design decision.
We can presumably edit the app in an external editor as follow:
appData.CodeData.EditableSection.Code
and update the cell array with the lines of your updated code (one cell element per user-code line).appData.CodeData.GeneratedCode
(if existing), which holds the same data as appData.CodeData.EditableSection.Code
but also including the AppDesigner-generated [non-editable] code.In theory, it is enough to extract the classdef code and same it in a simple *.m file, but then you would not be able to continue using AppDesigner to make layout modifications, and you would need to make all the changes manually in the m-file. If you wish to continue using AppDesigner after you modified the code, then you need to save it back into the *.mlapp file as explained above.
If you think this is not worth all the effort, then you’re probably right. But you must admit that it’s a bit fun to poke around…
One day maybe I’ll create wrapper utilities (mlapp2m and m2mlapp) that do all this automatically, in both directions. Or maybe one of my readers here will pick up the glove and do it sooner – are you up for the challenge?
Note that the MLAPP file format is deeply undocumented and subject to change without prior notice in upcoming Matlab releases. In fact, MathWorker Chris Portal warns us [6] that:
A word of caution for anyone that tries this undocumented/unsupported poking into their MLAPP file. Taking this approach will almost certainly guarantee your app to not load in one of the subsequent releases. Just something to consider in your off-roading expedition!
Then again, the same could have been said about the FIG and other binary file formats used by Matlab, which remained essentially the same for the past decade: Some internal field values may have changed but not the general format, and in any case the newer releases still accept files created with previous releases. For this reason, I speculate that future AppDesigners will accept MLAPP files created by older releases, possibly even hand-modified MLAPP files. Perhaps a CRC hash code of some sort will be expected, but I believe that any MLAPP that we modify today will still work in future releases. However, I could well be mistaken, so please be very careful with this knowledge. I trust that you can make up your own mind about whether it is worth the risk (and fun) or not.
AppDesigner is destined to gradually replace the aging GUIDE over the upcoming years. They currently coexist since AppDesigner (and its web-based uifigures) still does not contain all the functionality that GUIDE (and JFrame-based figures) provides (a few examples [7]). I already posted a few short posts about AppDesigner (use the AppDesigner tag [8] to list them), and today’s article is another in that series. Over the next few years I intend to publish more on AppDesigner and its associated new GUI framework (uifigures [9]).
I will be traveling to Zürich for a business trip between August 21-31. If you are in the Zürich area and wish to meet me to discuss how I could bring value to your work, then please email me (altmany at gmail).
Categories: GUI, High risk of breaking in future versions, Undocumented feature
Article printed from Undocumented Matlab: https://undocumentedmatlab.com/blog_old
URL to article: https://undocumentedmatlab.com/blog_old/appdesigner-mlapp-file-format
URLs in this post:
[1] Image: http://undocumentedmatlab.com/feed/
[2] email feed: http://undocumentedmatlab.com/subscribe_email.html
[3] *.fig files are simply MAT files in disguise: https://undocumentedmatlab.com/blog/fig-files-format
[4] AppDesigner: https://undocumentedmatlab.com/blog/sliders-in-matlab-gui#AppDesigner
[5] AppDesigner becomes increasingly sluggish: https://www.mathworks.com/matlabcentral/answers/279042-app-designer-s-editor-is-slow-and-gets-stuck-alot
[6] warns us: https://www.mathworks.com/matlabcentral/answers/279042#comment_383621
[7] a few examples: https://undocumentedmatlab.com/blog/customizing-uifigures-part-1#comment-384672
[8] AppDesigner tag: https://undocumentedmatlab.com/blog/tag/appdesigner
[9] uifigures: https://undocumentedmatlab.com/blog/tag/uifigure
[10] A couple of internal Matlab bugs and workarounds : https://undocumentedmatlab.com/blog_old/couple-of-bugs-and-workarounds
[11] Undocumented button highlighting : https://undocumentedmatlab.com/blog_old/undocumented-button-highlighting
[12] Customizing uifigures part 3 : https://undocumentedmatlab.com/blog_old/customizing-uifigures-part-3
[13] Customizing web-GUI uipanel : https://undocumentedmatlab.com/blog_old/customizing-web-gui-uipanel
[14] HG2 update : https://undocumentedmatlab.com/blog_old/hg2-update
[15] Customizing uifigures part 1 : https://undocumentedmatlab.com/blog_old/customizing-uifigures-part-1
[16] : http://www.mathworks.com/matlabcentral/fileexchange/56237-mlapp2classdef
[17] : https://github.com/StackOverflowMATLABchat/mlapp2classdef
[18] : http://www.mathworks.com/help/matlab/ref/type.html
[19] : http://stackoverflow.com/q/38933254/3372061
[20] : http://undocumentedmatlab.com/blog/customizing-uifigures-part-1
Click here to print.
Copyright © Yair Altman - Undocumented Matlab. All rights reserved.
10 Comments To "AppDesigner’s mlapp file format"
#1 Comment By Sam On August 17, 2016 @ 8:19 pm
Though not quite the same as what you’re envisioning, there’s a proof of concept implementation of *.mlapp -> *.m classdef on FEX ( [16]).
It’s a pretty basic thing thrown together so I could use AppDesigner’s layout tools and get a programmatic GUI out of it, which is my preferred GUI medium. It also attempts to strip the new UI elements out in favor of the “traditional” ones so I could work with older versions of MATLAB.
#2 Comment By Sam On August 17, 2016 @ 8:23 pm
Forgot the GH link: [17]
#3 Comment By Dev-iL On August 17, 2016 @ 9:34 pm
I’d like to point out one of the things we found while making the tool that Sam had mentioned:
.mlapp -> .m
conversion can very easily be done using the built-in functiontype
( [18]). The earliest MATLAB version where it worked (out of those we collectively had access to) was 2014b.Regarding the customization of
.mlapp
figures – the fact they’re webpages might actually be beneficial for customization, as UI elements can now be customized using CSS. I have explored this a bit and [19] after reading your “ [20]” post. (Thanks!)#4 Comment By Yair Altman On August 17, 2016 @ 9:40 pm
Thanks for this – I believe that a guest post about your experience with customizing uifigures using CSS would be highly appreciated by readers. Care to give it a shot? Send me an email to discuss the technical details
#5 Comment By Terry Brennan On October 7, 2016 @ 6:15 pm
Why not use class inheritance to achieve your goals? As for working programmatically, one of the things I like about the app designer is that the resulting mlapp file defines a class so I can derive other classes from the gui-created class and work programmatically without the visual interface on the derived class. One can add components, modify properties etc. One can even declare methods and/or properties in the mlapp to be Abstract within the appdesigner. This allows the creation of a base class from which customizations can be derived.
For example if I have a working appGUI.mlapp I can create
To assist you in recalling component names, etc, while programming the derived class you can have an instance of the base class (if it is not Abstract) in the workspace
to interrogate its methods and properties. This is almost as good as having the appGUI code.
Of course, this approach should not become obsolete, unless the MathWorks does something dumb like making mlapp classes
Sealed
.#6 Comment By Oleg On March 21, 2017 @ 5:01 pm
The following methods load the data:
Or using the file reader, which allows to read Matlab code directly:
The
appdesigner.internal.serialization.FileWriter()
class allows to write code to the .mlapp file.#7 Comment By Tom On April 17, 2018 @ 4:43 pm
Thanks for this tutorial. You saved me a ton of work. After Matlab crashed and corrupted my .mlapp file, I was able to unzip the .mlapp file and get to my GUI code and recreate my GUI. Without this, I would be months behind recreating the GUI and rewriting all the code. This website was a lifesaver. Thank you very much.
#8 Comment By Antonio Martinez On July 30, 2019 @ 6:06 pm
So, I don’t know if anyone is still reading this comment section but I tried digging a bit more into how MLAPP files are generated, following up Oleg’s contribution.
My conclusion is that, as far as I have been able to reach without basically fully reverse engineering AppDesigner’s inner workings, there is no way to use the methods available in
appdesigner.internal
such asFileWriter()
to generate MLAPP files from the class definition in text format in an M file.Here’s what I found.
• In newer versions of MATLAB the MLAPP file is organized in pretty much the same way but the
appdesigner/appModel.mat
file has a different organization. Now the AppData field is deprecated but kept for compatibility, and the data it contained is replace by two new fields ‘code’ and ‘components’. The ‘code’ struct stores the code from the app in separated sections (EditableSectionCode which is the part that contains the methods and properties, Callbacks, StartupCallback, etc), and components stores the UIFigure and the group hierarchy (I have no idea what this is, is generated by the AppModel methodgetGroupHierarchy()
and is way to deep for me too understand already). The groups hierarchy was previusly stored insideappData.metadata
.• AppModel: probably the parent for all apps created in AppDesigner is the class
appdesigner.internal.model.AppModel
. The AppModel contains the save() method, and has the properties Metadata and CodeModel among others. CodeModel is generated from the classappdesigner.internal.codegeneration.model.CodeModel
, and contains the different sections of the code.• FileWriter(): as Oleg’s pointed correctly,
appdesigner.internal.serialization.FileWriter()
is a class that can generate and write to MLAPP files. It has 4 methods (writeMATLABCodeText, writeAppDesignerData, writeAppMetadata, writeAppScreenshot) that have to be called to succesfully contruct an MLAPP file, and can be called from the command line, which would allow using them to generate MLAPP files from its components. The problem is, this components can only be accessed by AppDesigner itself, which renders this methods useless.• Hypothesis for MLAPP file saving: my hypothesis is that when an app is saved, the
save()
method in the AppModel class is called, which in turn calls the AppModel methodwriteAppToFile()
, which creates anappdesigner.internal.serialization.MLAPPSerializer
instance. The AppModel’s methodsetDataOnSerializer()
copies all the required information toMLAPPSerializer
, including the code sections, the metadata, group hierarchy, etc. Then,MLAPPSerializer
creates afileWriter
instance, divides the different objects received from AppModel and passes them to the relevantfileWriter
methods to generate the MLAPP file.I’m afraid there is literally no way to correctly generate the data scructures that are necessary to reconstruct an MLAPP file from the plain code without putting a ton of hours into untangling all the logic behind AppDesigner. Maybe someone can use this information to take on that task in the future.
#9 Comment By Antonio Martinez On July 30, 2019 @ 6:07 pm
By the way, these are the relevant MATLAB files if someone wants so keep reverse engineering this thing
• C:\Program Files\MATLAB\R2018b\toolbox\matlab\appdesigner\appdesigner\+appdesigner\+internal\+model\AppModel.m
• C:\Program Files\MATLAB\R2018b\toolbox\matlab\appdesigner\appdesigner\+appdesigner\+internal\+codegeneration\+model\CodeModel.m
• C:\Program Files\MATLAB\R2018b\toolbox\matlab\appdesigner\appdesigner\+appdesigner\+internal\+serialization\MLAPPSerializer.m
• C:\Program Files\MATLAB\R2018b\toolbox\matlab\appdesigner\appdesigner\+appdesigner\+internal\+serialization\FileWriter.m
#10 Comment By Bradley Stiritz On November 5, 2019 @ 1:13 am
It’s great to see that Yair and others are digging into the internals of AppDesigner. It seems to have a lot of promise. At the same time, AppDesigner is still often unstable and unusable, as of R2019b. I just posted on the running Mathworks Answers thread about this–
[5]
Question for anyone who has studied the internals– have you also experienced problems in AppDesigner, and then gained any insights whatsoever via your sleuthing? Do you have any clues or hypotheses about what could be going wrong for so many users?
Surely, after all these years of hard work, the AppDesigner team should have worked out most of the minor bugs..?