Problem using diary with standalone application

6 views (last 30 days)
I am developing a MATLAB app using the appdesigner (MATLAB version R2019b), which is intented to be used as standalone '.exe' on Windows. For traceablity I wanted to include a log-file of all outputs. The 'diary()' function seemed to be a good function for this. In my code I have a lot of 'disp(...)' commands (also in code parts that I cannot or do not want to change). The output for these disp-commands works perfectly in the MATLAB command window when I run the application within the MATLAB environment. However, when run it as Windows standalone the behavior is not logical to me: In the WIndows cmd window the output is the same as in the MATLAB command window, but the diary file only logs the output for the 'startupFcn()' and the 'UIFigureCloseRequest()' functions and not for any callback, e.g. here from a button 'DispHelloWorldButtonPushed()'.
Does anybody know how to solve this issue and where it comes from? Btw, also the build-in 'Create log file' option during compilation shows the same behavior as the 'diary' function.
Here is a minimal working example of an app with this behavior:
classdef Test < matlab.apps.AppBase
% Properties that correspond to app components
properties (Access = public)
UIFigure matlab.ui.Figure
DispHelloWorldButton matlab.ui.control.Button
end
% Callbacks that handle component events
methods (Access = private)
% Code that executes after component creation
function startupFcn(app)
diary(fullfile(pwd,'log.txt'))
diary on
disp('Hello World!')
end
% Button pushed function: DispHelloWorldButton
function DispHelloWorldButtonPushed(app, event)
diary on
disp('Button: Hello World!')
end
% Close request function: UIFigure
function UIFigureCloseRequest(app, event)
disp('Bye bye World!')
diary off
delete(app)
end
end
% Component initialization
methods (Access = private)
% Create UIFigure and components
function createComponents(app)
% Create UIFigure and hide until all components are created
app.UIFigure = uifigure('Visible', 'off');
app.UIFigure.Position = [100 100 640 480];
app.UIFigure.Name = 'UI Figure';
app.UIFigure.CloseRequestFcn = createCallbackFcn(app, @UIFigureCloseRequest, true);
% Create DispHelloWorldButton
app.DispHelloWorldButton = uibutton(app.UIFigure, 'push');
app.DispHelloWorldButton.ButtonPushedFcn = createCallbackFcn(app, @DispHelloWorldButtonPushed, true);
app.DispHelloWorldButton.Position = [159 161 311 175];
app.DispHelloWorldButton.Text = 'Disp(''Hello World!'')';
% Show the figure after all components are created
app.UIFigure.Visible = 'on';
end
end
% App creation and deletion
methods (Access = public)
% Construct app
function app = Test
% Create UIFigure and components
createComponents(app)
% Register the app with App Designer
registerApp(app, app.UIFigure)
% Execute the startup function
runStartupFcn(app, @startupFcn)
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

Answers (1)

Garmit Pant
Garmit Pant on 5 Aug 2024
Hello Jonathan
The issue you're experiencing is due to the way MATLAB handles the standard output and the diary functionality when running as a standalone executable.
The ”diary” function logs the MATLAB Command Window to a text file. Since the standalone application is not running in the MATLAB environment, the Command Window is not in use and thus the “diary” function doesn’t work.
You can use the “fprintf” function to log the outputs to a log-file. You can add a private function to the app using 'Function' button in the ‘Editor’ tab. The following code snippet demonstrates how to write the custom function to log messages and use it:
classdef Test < matlab.apps.AppBase
% Properties that correspond to app components
properties (Access = public)
UIFigure matlab.ui.Figure
DispHelloWorldButton matlab.ui.control.Button
LogFileID % File ID for the log file
end
% Callbacks that handle component events
methods (Access = private)
% Custom logging function
function logMessage(app, message)
if ~isempty(app.LogFileID)
fprintf(app.LogFileID, '%s\n', message);
end
fprintf('%s\n', message); % Also print to command window
end
% Code that executes after component creation
function startupFcn(app)
app.LogFileID = fopen(fullfile(<insert path to file>,'a');
app.logMessage('Hello World!');
end
% Button pushed function: DispHelloWorldButton
function DispHelloWorldButtonPushed(app, event)
app.logMessage('Button: Hello World!');
end
% Close request function: UIFigure
function UIFigureCloseRequest(app, event)
app.logMessage('Bye bye World!');
if ~isempty(app.LogFileID)
fclose(app.LogFileID);
end
delete(app)
end
end
% Component initialization
methods (Access = private)
% Create UIFigure and components
function createComponents(app)
% Create UIFigure and hide until all components are created
app.UIFigure = uifigure('Visible', 'off');
app.UIFigure.Position = [100 100 640 480];
app.UIFigure.Name = 'UI Figure';
app.UIFigure.CloseRequestFcn = createCallbackFcn(app, @UIFigureCloseRequest, true);
% Create DispHelloWorldButton
app.DispHelloWorldButton = uibutton(app.UIFigure, 'push');
app.DispHelloWorldButton.ButtonPushedFcn = createCallbackFcn(app, @DispHelloWorldButtonPushed, true);
app.DispHelloWorldButton.Position = [159 161 311 175];
app.DispHelloWorldButton.Text = 'Disp(''Hello World!'')';
% Show the figure after all components are created
app.UIFigure.Visible = 'on';
end
end
% App creation and deletion
methods (Access = public)
% Construct app
function app = Test
% Create UIFigure and components
createComponents(app)
% Register the app with App Designer
registerApp(app, app.UIFigure)
% Execute the startup function
runStartupFcn(app, @startupFcn)
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
I hope you find the above explanation and suggestions useful!
  5 Comments
Garmit Pant
Garmit Pant on 6 Aug 2024
Hi Jonathan
Did you try changing how you are handling toggling "diary" to 'on' and 'off'? I have made some changes to that in the code snippet above. Specifically, I have removed the "diary on" statements in your code. The following statement also switches on logging:
diary(fullfile(<file path>))
GIven that you are toggling logging on multiple times using the "diary on" statement, the path of the log file could be changing. Please try to incorporate the code snippet in the comments since I was able to successfully log to a file using the "diary" function that way.
Jonathan Berthold
Jonathan Berthold on 6 Aug 2024
Hi Garmit,
I now found that the problem is somewhat deeper than I expected. I now tried the standalone app on a different machine and the diary worked in all cases! So what I can tell is that it works on MS Windows 10, Version 1809 but not on a MS Windows Server 2016, Version 1607 (which I use for development & testing). Maybe that helps you in finding the error with the different handling of the callbacks. Let me know what you think.

Sign in to comment.

Categories

Find more on Develop uifigure-Based Apps in Help Center and File Exchange

Products


Release

R2019b

Community Treasure Hunt

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

Start Hunting!