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:
>> edit scanimage
Find the following section in the startup script:
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 Tags | Description |
---|---|
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.