ScanImage 2019 : ScanImage API: Creating a simple user interface

ScanImage was developed using theĀ Model-View-Controller design pattern. As a demonstration of the scripting-flexibility that ScanImage provides us, this article will show an example of how to set up a simple GUI for a specific ScanImage-based task. This article uses the ScanImage API, which is described in more detail here.

Suppose we were to run a hypothetical experiment/procedure that only requires the user to change the name of a file and start a frame acquisition based on clicking. For this purpose the following \GUIDE GUI would be more than sufficient for our needs:

We shall now see how we can tie the ScanImage core to perform the desired task through this GUI. First of all, let's link a new controller-view to ScanImage. To do this, we can inspect the scanimage startup script. In the Matlab command window, type:

Inspect the ScanImage startup script
>> edit scanimage

Find the following section in the startup script:

scanimage startup script excerpt
		try
            hSI = scanimage.SI(mdf);
            hSI.initialize();
            
            % NOTE: This is the original scanimage controller
		    %hSICtl = scanimage.SIController(hSI);
		    %hSICtl.initialize(usr);
			% This is the new basic controller:
			hSICtl = scanimage.SILiteController(hSI);
			
			assignin('base','hSI',hSI);
            assignin('base','hSICtl',hSI.hController{1});
		catch ME
			...

Now we can set up the new controller

classdef SILiteController < handle
% CYCLEMANAGERCONTROLLER Controller class for cycle mode
    properties 
        model
        view 
    end
    % CONSTRUCTOR
    methods
        function obj = SILiteController(model)
            obj.model = model;
            obj.view = scanimage.SILiteView(obj);
        end
    end
    % USER METHODS
    methods
        function setLogFileName(obj,val)
            obj.model.hResScan.logFileStem = val;
        end
        function setEnableLoggingMode(obj,val)
            obj.model.hChannels.loggingEnable = logical(val);
        end
        function startGrabAcquisition(obj)
            obj.model.startGrab();
        end
        function abortAcquisition(obj)
            obj.model.abort();
        end
    end
end

Next comes the View. Here will choose explicit names for each element in the GUI.

GUI TagsDescription
cbEnableLogging
Check-box for toggling logging-enabled state
etLogFileName
Edit-box for setting the logging filename
pbGrab
Push-button for initiating a GRAB acquisition (not set in the view)
pbAbort
Push-button for stopping an acqusition (not set in the view)
classdef SILiteView < handle
% CYCLEMANAGER Model class for cycle mode
    properties
        gui
        model
        controller
    end
    methods
        function obj = SILiteView(controller)
            obj.controller = controller;
            obj.model = controller.model;
            obj.gui = SILiteGUI('controller',obj.controller);
            % Initialization call goes here
            obj.initializeGUIFromModel(obj.gui);
            addlistener(obj.model.hResScan,'logFileStem',...
                'PostSet',@(src,evnt)scanimage.SILiteView.handlePropEvents(obj,src,evnt));
            addlistener(obj.model.hChannels,'loggingEnable',...
                'PostSet',@(src,evnt)scanimage.SILiteView.handlePropEvents(obj,src,evnt));
        end
        function delete(obj)
            disp('View destructor');
            if ishandle(obj.gui)
                close(obj.gui);
            end
        end
        function initializeGUIFromModel(obj, hGUI)
            handles = guidata(hGUI);
            % Check the model's initial state
            set(handles.cbEnableLogging, 'Value', obj.model.hChannels.loggingEnable);
            set(handles.etLogFileName, 'String', obj.model.hResScan.logFileStem);
        end
    end
    methods (Static)
        function handlePropEvents(obj,src,evnt)
            evntobj = evnt.AffectedObject;
            handles = guidata(obj.gui);
            switch src.Name
                case 'logFileName'
                    set(handles.etLogFileName, 'Value', evntobj.logFileStem);
                case 'loggingEnable'
                    set(handles.cbEnableLogging, 'Value', evntobj.loggingEnable);
            end
        end
    end
end
function varargout = SILiteGUI(varargin)
...
% --- Executes just before SILiteGUI is made visible.
function SILiteGUI_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% varargin   command line arguments to SILiteGUI (see VARARGIN)
% Choose default command line output for SILiteGUI
handles.output = hObject;

% get handle to the controller
for i = 1:2:length(varargin)
    switch varargin{i}
        case 'controller'
            handles.controller = varargin{i+1};
        otherwise
            error('unknown input')
    end
end
% Update handles structure
guidata(hObject, handles);
...

After constructing

function varargout = SILiteGUI(varargin)
...
function pbGrab_Callback(hObject, eventdata, handles)
% hObject    handle to pbGrab (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
handles.controller.startGrabAcquisition();
% --- Executes on button press in pbAbort.
function pbAbort_Callback(hObject, eventdata, handles)
% hObject    handle to pbAbort (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
handles.controller.abortAcquisition();
% --- Executes on button press in cbEnableLogging.
function cbEnableLogging_Callback(hObject, eventdata, handles)
% hObject    handle to cbEnableLogging (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% Hint: get(hObject,'Value') returns toggle state of cbEnableLogging
handles.controller.setEnableLoggingMode(get(hObject, 'Value'));
function etLogFileName_Callback(hObject, eventdata, handles)
% hObject    handle to etLogFileName (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% Hints: get(hObject,'String') returns contents of etLogFileName as text
%        str2double(get(hObject,'String')) returns contents of etLogFileName as a double
handles.controller.setLogFileName(get(hObject, 'String'));
...

This concludes our simple implementation of the MVC approach for creating simple ScanImage user interfaces. This should be considered a starting point, given that error-checking and proper destructors should still be added to this implementation.

Attachments:

SILite.png (image/png)