0

I'm trying to build a GUI main routine that calls external functions to perform updates such as create_ui_buttons.m (to create my user interface buttons) and create_callback_fns.m (to create my callback functions). That is, outside of main, I want to define my callback functions and buttons in separate files (because the main file is getting long as is) but have main call them.

For example, I'd like to define my go button:

handles.go_button = uicontrol('style','pushbutton', 'Units', 'normalized', ...
'Position',[.1, .1, .1, .1], 'BackgroundColor', [0,1,0], ...
'FontWeight','bold', 'String', 'Go!', 'callback',@go_button_callback);

in a file called create_ui_buttons.m and have it know to look in another file create_callback_fns.m for the @go_button_callback reference. Right now, it is not seeing that reference.


As perhaps a more clear example, consider the uicontrol documentation from matlab:

function myui
    % Create a figure and axes
    f = figure('Visible','off');
    ax = axes('Units','pixels');
    surf(peaks)

    % Create pop-up menu
    popup = uicontrol('Style', 'popup',...
           'String', {'parula','jet','hsv','hot','cool','gray'},...
           'Position', [20 340 100 50],...
           'Callback', @setmap);    

   % Create push button
    btn = uicontrol('Style', 'pushbutton', 'String', 'Clear',...
        'Position', [20 20 50 20],...
        'Callback', 'cla');       

   % Create slider
    sld = uicontrol('Style', 'slider',...
        'Min',1,'Max',50,'Value',41,...
        'Position', [400 20 120 20],...
        'Callback', @surfzlim); 

    % Add a text uicontrol to label the slider.
    txt = uicontrol('Style','text',...
        'Position',[400 45 120 20],...
        'String','Vertical Exaggeration');

    % Make figure visble after adding all components
    f.Visible = 'on';
    % This code uses dot notation to set properties. 
    % Dot notation runs in R2014b and later.
    % For R2014a and earlier: set(f,'Visible','on');

    function setmap(source,event)
        val = source.Value;
        maps = source.String;
        % For R2014a and earlier: 
        % val = get(source,'Value');
        % maps = get(source,'String'); 

        newmap = maps{val};
        colormap(newmap);
    end

    function surfzlim(source,event)
        val = 51 - source.Value;
        % For R2014a and earlier:
        % val = 51 - get(source,'Value');

        zlim(ax,[-val val]);
    end
end

How can I define the functions setmap and surfzlim in another function called create_callback_fns.m and how can I define popup and sld in another function called create_ui_buttons.m outside of main and have them all communicate?

11
  • 1
    You cannot reference local or nested functions externally, so to go this route you'll either need a *.m file for every function or look into using an object oriented approach for your GUI. Using an abstract class with static methods that you either inherit or call directly would be a straightforward approach for your callbacks. Commented Oct 30, 2017 at 15:48
  • @excaza: Thanks! What do you mean by a *.m file for every function? I've created setmap.m and surfzlim.m, but how do I call those / reference those with function handles / callbacks? I was stuck using @setmap... Commented Oct 30, 2017 at 15:51
  • If you have setmap.m then your Callback is defined as @setmap. Commented Oct 30, 2017 at 15:56
  • Ok, maybe I'm confused about how I tell the callback for setmap that it takes source and event input? What are these inputs and how do I pass them from the main GUI to the callback? Commented Oct 30, 2017 at 16:01
  • See: Callback Definition Commented Oct 30, 2017 at 16:02

3 Answers 3

1

You really should go for a class. As Excaza told you. However, there're some things you should respect when working with classes in matlab:

  • First, only create handles type classes. It means that your classes should all inherit from handle: MyInterface < handle
  • Doing this will force you to declare the class itself as the first argument of a class methods. setmap(scr, evt) would become setmap(self, scr, evt) where self is "myself'
  • The handle to use for the method, to define in the callbacks, will then be @self.setmap

I worked on an object oriented mask framework and I will soon start working on an interface framework. You WANT to go with classes. Not only it is versatile, abstract, and reusable, but Matlab is also full of special behavior that can be excruciating. Using classes help you to control those behavior, once and for all.

10 years ago I was doing interface using structures of handles, just like you, to interact with the item after its creation. Pain in the ass to maintain. Classes are just so much more simpler, you always have the item using a simple getter.

Here is your example code, converted into a matlab class:

classdef myuiClass < handle

    %% Private constants
    properties (Access = private)
        mFigure;
        mAxes;

        mPopup;
        mButton;
        mSlider;
        mText;
    end

    %% Public methods
    methods (Access = public)

        %% Constructor
        function self = myuiClass()
            self.mFigure = figure('Visible', 'off');
            self.mAxes   = axes('Units', 'pixels');
            surf(peaks);

            self.createInterface();

            self.mFigure.Visible = 'on';
        end
    end

    %% Protected methods
    methods (Access = protected)

        %% setmap
        function setmap(~, source, ~)
            val = source.Value;
            maps = source.String;

            newmap = maps{val};
            colormap(newmap);
        end

        %% surfzlim
        function surfzlim(self, source, ~)
            val = 51 - source.Value;

            zlim(self.mAxes, [-val val]);
        end

    end

    %% Private methods
    methods (Access = private)

        %% createInterface
        function createInterface( self )

            self.mPopup = uicontrol('Style', 'popup',...
                'String', {'parula','jet','hsv','hot','cool','gray'},...
                'Position', [20 340 100 50],...
                'Callback', @self.setmap);

            self.mButton = uicontrol('Style', 'pushbutton', 'String', 'Clear',...
                'Position', [20 20 50 20],...
                'Callback', 'cla');

            self.mSlider = uicontrol('Style', 'slider',...
                'Min',1,'Max',50,'Value',41,...
                'Position', [400 20 120 20],...
                'Callback', @self.surfzlim);

            self.mText = uicontrol('Style','text',...
                'Position',[400 45 120 20],...
                'String','Vertical Exaggeration');            
        end
    end
end
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks! Suppose surfzlim is a large function, can I extract it to another file and reference that file in the method definition?
Yes but I would not do. If surfzlim is a large function, consider decoupling it in small function. If surfzlim has several responsibilities, create a class or a hierarchy of classes just for it. Then refer to surfzlim as a class member of your interface class and use it's methods handles the same way. As a general rule when developping an interface, you should take extreme care into decoupling the system and the graphics and never mix the two of them.
If this answer suits you may you please accept it ? Thanks
0

The below works for the matlab test case when all files are contained in the same folder.


surfzlim.m:

function surfzlim(source,event,ax)
    val = 51 - source.Value;
    % For R2014a and earlier:
    % val = 51 - get(source,'Value');

    zlim(ax,[-val val]);
end

setmap.m:

function setmap(source,event)
    val = source.Value;
    maps = source.String;
    % For R2014a and earlier: 
    % val = get(source,'Value');
    % maps = get(source,'String'); 

    newmap = maps{val};
    colormap(newmap);
end

main.m

function myui
    % Create a figure and axes
    f = figure('Visible','off');
    ax = axes('Units','pixels');
    surf(peaks)

    % Create pop-up menu
    popup = uicontrol('Style', 'popup',...
           'String', {'parula','jet','hsv','hot','cool','gray'},...
           'Position', [20 340 100 50],...
           'Callback', @setmap);    

   % Create push button
    btn = uicontrol('Style', 'pushbutton', 'String', 'Clear',...
        'Position', [20 20 50 20],...
        'Callback', 'cla');       

   % Create slider
    sld = uicontrol('Style', 'slider',...
        'Min',1,'Max',50,'Value',41,...
        'Position', [400 20 120 20],...
        'Callback', {@surfzlim,ax}); 

    % Add a text uicontrol to label the slider.
    txt = uicontrol('Style','text',...
        'Position',[400 45 120 20],...
        'String','Vertical Exaggeration');

    % Make figure visble after adding all components
    f.Visible = 'on';
    % This code uses dot notation to set properties. 
    % Dot notation runs in R2014b and later.
    % For R2014a and earlier: set(f,'Visible','on');

end

Comments

0

You can perform as follow :

file main.m

function main()

    vm = MyFullViewModel();
    %Do whatever you want here
    CreateGUI(vm);
    %Do whatever you want here

end

file CreateGUI.m

function CreateGUI(vm)

    f = figure('Visible','off');
    ax = axes('Units','pixels');
    surf(peaks)

    popup = uicontrol('Style', 'popup',...
           'String', {'parula','jet','hsv','hot','cool','gray'},...
           'Position', [20 340 100 50],...
           'Callback', @(src, evt)vm.setmap(src, evt));    

    btn = uicontrol('Style', 'pushbutton', 'String', 'Clear',...
        'Position', [20 20 50 20],...
        'Callback', 'cla');       

    sld = uicontrol('Style', 'slider',...
        'Min',1,'Max',50,'Value',41,...
        'Position', [400 20 120 20],...
        'Callback', @(src, evt)vm.surfzlim(src, evt, ax)); 

    txt = uicontrol('Style','text',...
        'Position',[400 45 120 20],...
        'String','Vertical Exaggeration');

end

file MyFullViewModel.m

function vm = MyFullViewModel()

    vm.setmap = @setmap;
    vm.surfzlim = @surfzlim;

end

function setmap(src, evt)
    val = src.Value;
    maps = src.String;
    newmap = maps{val};
    colormap(newmap);
end

function surfzlim(src, evt, ax)
    val = 51 - src.Value;
    zlim(ax,[-val val]);
end

You can easily proceed this way to put all your callbacks in the same file MyFullViewModel.m

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.