-
-
Save CitizenInsane/307be5a548faa93827ec to your computer and use it in GitHub Desktop.
Shows how to create a responsive gui with Matlab without using multithreading.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
% | |
% 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