Skip to content

Instantly share code, notes, and snippets.

@CitizenInsane
Created February 7, 2015 03:19
Show Gist options
  • Save CitizenInsane/307be5a548faa93827ec to your computer and use it in GitHub Desktop.
Save CitizenInsane/307be5a548faa93827ec to your computer and use it in GitHub Desktop.
Shows how to create a responsive gui with Matlab without using multithreading.
%
% PURPOSE
%
% Shows how to create a responsive gui with Matlab without using
% multithreading.
%
% SYNTAX:
%
% [] = ResponsiveGui();
%
% REMARKS:
%
% See http://stackoverflow.com/q/28364243/684399
%
%% --- Entry point
function [] = ResponsiveGui()
%[
createGui();
%]
end
%% --- Events in the gui
function [] = onCloseRequest(dlg)
%[
% If already in computation mode
if (~isempty(get(dlg, 'UserData')))
% Just indicate close is requested and leave immediatly
set(dlg, 'UserData', 'CloseRequested');
data = guidata(dlg);
set(data.handles.btnGoCancel, 'Enable', 'off', 'String', 'Cancelling...');
return;
end
% Immediate close
delete(dlg);
%]
end
function [] = onGoCancelClick(dlg)
%[
% If already in computation mode
if (~isempty(get(dlg, 'UserData')))
% Just indicate cancel is requested and leave immediatly
set(dlg, 'UserData', 'CancelRequested');
data = guidata(dlg);
set(data.handles.btnGoCancel, 'Enable', 'off', 'String', 'Cancelling...');
return;
end
% Go into computation mode
[settings, err] = tryReadSettings(dlg);
if (~isempty(err))
waitfor(msgbox(err.message, 'Invalid settings', 'Error', 'Modal'));
else
enterComputationMode(dlg);
err = doProcessing(dlg, settings);
leaveComputationMode(dlg, err);
end
%]
end
function [settings, err] = tryReadSettings(dlg)
%[
data = guidata(dlg);
% Read settings
err = [];
try
n = str2double(get(data.handles.edIterationCount, 'String'));
if (~isfinite(n) || (n < 1) || (n ~= floor(n)))
error('Number of iteration must of finite positive integer.');
end
settings.NumberOfIterations = n;
catch err
end
if (~isempty(err))
settings = [];
end
%]
end
function [] = enterComputationMode(dlg)
%[
data = guidata(dlg);
% Disable the settings panel and go into 'running' mode
%set(data.handles.pnSettings, 'Enable', 'off'); % Unfortunatly enabling/disabling a uipanel as a whole is
set(data.handles.edIterationCount, 'Enable', 'off'); % not possible so disabling controls one-by-one instead ...
set(data.handles.btnGoCancel, 'String', 'Cancel');
set(dlg, 'UserData', 'Running');
%]
end
function [err] = doProcessing(dlg, settings)
%[
err = [];
try
% Connect instrumentation callbacks with the gui
instrumentation.CheckCancel = @(ratio)onCheckCancel(dlg);
instrumentation.Progress = @(ratio)onProgress(dlg, ratio);
% Perform the processing
processing(settings.NumberOfIterations, instrumentation);
catch err
% Either cancel or real error ... handled later.
end
%]
end
function [] = leaveComputationMode(dlg, err)
%[
data = guidata(dlg);
% In case close was requested
guiState = get(dlg, 'UserData');
if (~isempty(guiState) && strcmp(guiState, 'CloseRequested'))
% Stop immediatly.
delete(dlg);
return;
end
% In case of error during processing
if (~isempty(err))
if (strcmp(err.identifier, 'System:OperationCanceledException'))
% Just indicate user canceled operations.
waitfor(msgbox('Operations canceled by user.', 'Processing canceled', 'Help', 'Modal'));
else
% Here would be better have nice dialog to report processing error
% with callstack + some copy to clipboard button for bug report with
% OS/ARCH/Etc details...
waitfor(msgbox(err.message, 'Processing error', 'Error', 'Modal'));
end
end
% Reset GUI to normal state
set(dlg, 'UserData', []);
set(data.handles.btnGoCancel, 'Enable', 'on', 'String', 'Go');
uiprogress(data.handles.pbProgress, 0.0);
%set(data.handles.pnSettings, 'Enable', 'off'); % Unfortunatly enabling/disabling a uipanel as a whole is
set(data.handles.edIterationCount, 'Enable', 'on'); % not possible so re-enabling controls one-by-one instead ...
%]
end
%% --- Instrumentation callbacks
function [] = onCheckCancel(dlg)
%[
% Force interface to process its events
drawnow();
% Check 'UserData' has not been modified during events processing
guiState = get(dlg, 'UserData');
if (~isempty(guiState) && ....
strcmp(guiState, 'CancelRequested') || strcmp(guiState, 'CloseRequested'))
error('System:OperationCanceledException', 'Operation canceled');
end
%]
end
function [] = onProgress(dlg, ratio)
%[
% Update the progress bar value
data = guidata(dlg);
uiprogress(data.handles.pbProgress, ratio);
% Force interface to refresh
drawnow();
%]
end
%% --- The processing function
function [] = processing(count, instrumentation)
%[
for ki = 1:count,
instrumentation.CheckCancel();
instrumentation.Progress((ki-1)/count);
if (ki > 1), pause(1); end
instrumentation.CheckCancel();
instrumentation.Progress(ki/count);
end
%]
end
%% --- Create the graphical interface
% This is not important how it is created:
% > Could be done with GUIDE ....
% > or with GUI Layout Toolbox (far much better): http://www.mathworks.com/matlabcentral/fileexchange/27758-gui-layout-toolbox
function [dlg] = createGui()
%[
% Init
if (verLessThan('matlab', '8.4'))
figNumberTile = 'Number'; % R2014b
else
figNumberTile = 'NumberTitle'; % Before R2014b
end
% Create the figure
dlg = figure('Units', 'pixels', ...
'Name', 'Responsive GUI', ...
'IntegerHandle', 'off', ...
'MenuBar', 'none', ...
figNumberTile, 'off', ...
'Position', [0, 0, 310, 100], ...
'Toolbar', 'none', ...
'Visible', 'off');
% Add controls
pnSettings = uipanel('Parent', dlg, 'BackgroundColor', get(dlg, 'Color'), 'Title', 'Computation settings', 'Units', 'pixels', 'Position', [10 30 200 60]);
edIterationCount = uicontrol('Parent', pnSettings, 'BackgroundColor', [1 1 1], 'Style', 'edit', 'Units', 'pixels', 'Position', [10 15 180 20], 'TooltipString', 'Number of iterations', 'String', '20');
pbProgress = uiprogress(dlg); set(pbProgress, 'Units', 'pixels', 'Position', [10 10 200 10]);
btnGoCancel = uicontrol('Parent', dlg, 'String', 'Go', 'Units', 'pixels', 'Position', [220 10 80 80]);
% Attach events
set(btnGoCancel, 'Callback', @(s, a)onGoCancelClick(dlg));
set(dlg, 'CloseRequestFcn', @(s, a)onCloseRequest(dlg));
% Save handles for later
data = guidata(dlg);
data.handles.pnSettings = pnSettings;
data.handles.edIterationCount = edIterationCount;
data.handles.pbProgress = pbProgress;
data.handles.btnGoCancel = btnGoCancel;
guidata(dlg, data);
% Make the figure visible
hh = findobj(dlg, '-property', 'Units');
set(hh, 'Units', 'Normalized');
movegui(dlg, 'center');
set(dlg, 'Visible', 'on');
%]
end
%% --- A waitbar that can be embedded in a GUI figure.
% NB: Obtained from here => http://stackoverflow.com/a/16861297/684399
function h = uiprogress(varargin)
%[
if ishandle(varargin{1}) && size(varargin, 2) > 1
ax = varargin{1};
value = varargin{2};
p = get(ax,'Child');
x = get(p,'XData');
x(3:4) = value;
set(p,'XData',x)
return
end
bg_color = 'w';
fg_color = 'r';
h = axes('Units','pixels',...
'XLim',[0 1],'YLim',[0 1],...
'XTick',[],'YTick',[],...
'Color',bg_color,...
'XColor',bg_color,'YColor',bg_color, ...
'Parent', varargin{1});
patch([0 0 0 0],[0 1 1 0],fg_color,...
'Parent',h,...
'EdgeColor','none');%%%,...
%%'EraseMode','none');
%]
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment