Main Content

Create Simple Live Editor Task

This example shows how to create a simple custom Live Editor task and add it to a live script.

Define Live Editor Task Subclass

Define a class called NormalizeVectorData that creates a custom Live Editor task for normalizing vector data.

To define the class, create a file called NormalizeVectorData.m that contains the following class definition with these features:

  • State and Summary public properties that store the current state of the task and a dynamic summary of what the task does.

  • Private properties that store the drop-down lists, spinners, checkboxes, and grid layout managers for selecting input data and specifying parameters.

  • A setup method that initializes the task.

  • A generateCode method that updates the generated code for the task.

  • get.Summary, get.State, and set.State methods for getting and setting the summary and state of the task.

  • An updateComponents method that updates the task when a user selects input data or changes parameters.

  • A reset method that resets the state of the task.

classdef NormalizeVectorData < matlab.task.LiveTask
    properties(Access = private,Transient)
        InputDataDropDown               matlab.ui.control.DropDown
        MethodDropDown                  matlab.ui.control.DropDown
        ZscoreGrid                      matlab.ui.container.GridLayout
        ZscoreDropDown                  matlab.ui.control.DropDown
        RangeGrid                       matlab.ui.container.GridLayout
        LeftRangeSpinner                matlab.ui.control.Spinner
        RightRangeSpinner               matlab.ui.control.Spinner
        InputDataCheckBox               matlab.ui.control.CheckBox
        NormalizedDataCheckBox          matlab.ui.control.CheckBox
    end

    properties(Dependent)
        State
        Summary
    end

    methods(Access = private)
        function createComponents(task)
            g = uigridlayout(task.LayoutManager,[1,1]);
            g.RowHeight = {'fit' 'fit' 'fit' 'fit' 'fit' 'fit'};
            g.ColumnWidth = {'fit'};

            % Row 1: Select data section label
            uilabel(g,"Text","Select data","FontWeight","bold");

            % Row 2: Select data section components
            inputgrid = uigridlayout(g,"RowHeight",{'fit'},"ColumnWidth", ...
                {'fit','fit'},"Padding",0);
            uilabel(inputgrid,"Text","Input data");
            task.InputDataDropDown = uidropdown(inputgrid, ...
                "ValueChangedFcn",@task.updateComponents, ...
                "DropDownOpeningFcn",@task.populateWSDropdownItems);
            task.populateWSDropdownItems(task.InputDataDropDown);

            % Row 3: Specify method section label
            uilabel(g,"Text","Specify method","FontWeight","bold");

            % Row 4: Method section components
            methodgrid = uigridlayout(g,"RowHeight",{'fit'}, ...
                "ColumnWidth",{'fit','fit','fit'},"Padding",0);
            uilabel(methodgrid,"Text","Normalization method");
            task.MethodDropDown = uidropdown(methodgrid,"ValueChangedFcn", ...
                @task.updateComponents);
            task.MethodDropDown.Items = ["Z-score" "2-Norm" ...
                "Scale by standard deviation" "Scale to new range" ...
                "Center to mean 0"];
            task.MethodDropDown.ItemsData = {'zscore' 'norm' 'scale' ...
                'range' 'center'};

            % Subgrid 1 in method section
            task.ZscoreGrid = uigridlayout(methodgrid,"RowHeight",{'fit'}, ...
                "ColumnWidth",{'fit','fit'},"Padding",0);
            uilabel(task.ZscoreGrid,"Text","Deviation type");
            task.ZscoreDropDown = uidropdown(task.ZscoreGrid,"ValueChangedFcn", ...
                @task.updateComponents,"Items",{'Standard' 'Median absolute'}, ...
                "ItemsData",{'std' 'robust'},"Tooltip", ...
                "Center data to 0 and scale to deviation 1");

            % Subgrid 2 in method section
            task.RangeGrid = uigridlayout(methodgrid,"RowHeight",{'fit'}, ...
                "ColumnWidth",{'fit' 50 50},"Padding",0);
            task.RangeGrid.Layout.Row = 1;
            task.RangeGrid.Layout.Column = 3;
            uilabel(task.RangeGrid,"Text","Range edges");
            task.LeftRangeSpinner = uispinner(task.RangeGrid,"ValueChangedFcn", ...
                @task.updateComponents,"Tag","LeftRangeSpinner","Tooltip", ...
                "Left edge of new range");
            task.RightRangeSpinner = uispinner(task.RangeGrid,"ValueChangedFcn", ...
                @task.updateComponents,"Tag","RightRangeSpinner", ...
                "Tooltip","Right edge of new range");

            % Row 5: Display results section label
            uilabel(g,"Text","Display results","FontWeight","bold");

            % Row 6: Display results section components
            displaygrid = uigridlayout(g,"RowHeight",{'fit'},"ColumnWidth", ...
                {'fit','fit'},"Padding",0);
            task.InputDataCheckBox = uicheckbox(displaygrid,"Text", ...
                "Input data","ValueChangedFcn",@task.updateComponents);
            task.NormalizedDataCheckBox = uicheckbox(displaygrid,"Text", ...
                "Normalized data","ValueChangedFcn",@task.updateComponents);
        end

        function setComponentsToDefault(task)
            task.MethodDropDown.Value = "zscore";
            task.ZscoreDropDown.Value = "std";
            task.LeftRangeSpinner.Value = 0;
            task.RightRangeSpinner.Value = 1;
            task.InputDataCheckBox.Value = true;
            task.NormalizedDataCheckBox.Value = true;
        end

        function updateComponents(task,source,~)
            if nargin > 1
                if isequal(source.Tag,"LeftRangeSpinner")
                    if task.RightRangeSpinner.Value <= task.LeftRangeSpinner.Value
                        task.RightRangeSpinner.Value = task.LeftRangeSpinner.Value + 1;
                    end
                elseif isequal(source.Tag,"RightRangeSpinner")
                    if task.RightRangeSpinner.Value <= task.LeftRangeSpinner.Value
                        task.LeftRangeSpinner.Value = task.RightRangeSpinner.Value - 1;
                    end
                end
            end
            hasData = ~isequal(task.InputDataDropDown.Value,"select variable");
            task.MethodDropDown.Enable = hasData;
            task.ZscoreDropDown.Enable = hasData;
            task.LeftRangeSpinner.Enable = hasData;
            task.RightRangeSpinner.Enable = hasData;
            task.InputDataCheckBox.Enable = hasData;
            task.NormalizedDataCheckBox.Enable = hasData;
            % Show only relevant subgrids
            task.ZscoreGrid.Visible = isequal(task.MethodDropDown.Value,"zscore");
            task.RangeGrid.Visible = isequal(task.MethodDropDown.Value,"range");
            % Trigger the live editor to update the generated script
            notify(task,"StateChanged");
        end

        function populateWSDropdownItems(~,src,~)
            workspaceVariables = evalin("base","who");
            src.Items = ["select variable"; workspaceVariables];
            src.ItemsData = ["select variable"; workspaceVariables];
        end
    end

    methods(Access = protected)
        function setup(task)
            createComponents(task);
            setComponentsToDefault(task);
            updateComponents(task);
        end
    end

    methods
        function [code,outputs] = generateCode(task)
            if isequal(task.InputDataDropDown.Value,"select variable")
                % Not have enough information to generate code,
                % return empty values
                code = "";
                outputs = {};
                return
            end

            outputs = {'normalizedData'};

            code = "% Normalize data";
            code = code + newline + outputs{1} + " = normalize(";
            code = code + "`" + task.InputDataDropDown.Value + "`";

            if ~isequal(task.MethodDropDown.Value,"zscore") || ...
                    ~isequal(task.ZscoreDropDown.Value,"std")
                code = code + ", """ + task.MethodDropDown.Value + """";
                if isequal(task.MethodDropDown.Value,"zscore")
                    code = code + ", """ + task.ZscoreDropDown.Value + """";
                elseif isequal(task.MethodDropDown.Value,"range") && ...
                        (task.LeftRangeSpinner.Value ~= 0 || ...
                        task.RightRangeSpinner.Value ~= 1)
                    code = code + ",[" + num2str(task.LeftRangeSpinner.Value) ...
                        + ", " + num2str(task.RightRangeSpinner.Value) + "]";
                end
            end

            code = code + ");" + newline + newline + "% Visualize results" + ...
                newline + "figure" + newline;

            if task.InputDataCheckBox.Value
                code = code + "plot(`" + task.InputDataDropDown.Value + ...
                    "`, ""Color"",[109 185 226]/255," + ...
                    " ""DisplayName"",""Input data"")";
            end

            if task.NormalizedDataCheckBox.Value
                if task.InputDataCheckBox.Value
                    code = code + newline + "hold on" + newline;
                end
                code = code + "plot(normalizedData,""Color"",[0 114 189]/255, ..." + ...
                    newline + " ""LineWidth"",1.5,""DisplayName"",""Normalized data"")";
                if task.InputDataCheckBox.Value
                    code = code + newline + "hold off";
                end
            end

            code = code + newline + "legend";
        end
        
        function summary = get.Summary(task)
            if isequal(task.InputDataDropDown.Value,"select variable")
                summary = "Normalize vector data";
            else
                switch task.MethodDropDown.Value
                    case "zscore"
                        methodString = " using z-score";
                    case "norm"
                        methodString = " using 2-norm";
                    case "scale"
                        methodString = " using scaling by standard deviation";
                    case "range"
                        methodString = " by scaling to new range";
                    case "center"
                        methodString = " by centering the data to 0";
                end
                summary = "Normalized vector `" + task.InputDataDropDown.Value + ...
                    "`" + methodString;
            end
        end

        function state = get.State(task)
            state = struct;
            state.InputDataDropDownValue = task.InputDataDropDown.Value;
            state.MethodDropDownValue = task.MethodDropDown.Value;
            state.ZscoreDropDownValue = task.ZscoreDropDown.Value;
            state.LeftRangeSpinnerValue = task.LeftRangeSpinner.Value;
            state.RightRangeSpinnerValue = task.RightRangeSpinner.Value;
            state.InputDataCheckboxValue = task.InputDataCheckBox.Value;
            state.NormalizedDataCheckboxValue = task.NormalizedDataCheckBox.Value;
        end

        function set.State(task,state)
            value = state.InputDataDropDownValue;
            if ~ismember(value, task.InputDataDropDown.ItemsData)
                % In case the selected Input Data variable was cleared after
                % saving the mlx file and before reopening the mlx file
                task.InputDataDropDown.Items = [task.InputDataDropDown.Items {value}];
                task.InputDataDropDown.ItemsData = [task.InputDataDropDown.ItemsData {value}];
            end
            task.InputDataDropDown.Value = value;
            task.MethodDropDown.Value = state.MethodDropDownValue;
            task.ZscoreDropDown.Value = state.ZscoreDropDownValue;
            task.LeftRangeSpinner.Value = state.LeftRangeSpinnerValue;
            task.RightRangeSpinner.Value = state.RightRangeSpinnerValue;
            task.InputDataCheckBox.Value = state.InputDataCheckboxValue;
            task.NormalizedDataCheckBox.Value = state.NormalizedDataCheckboxValue;
            updateComponents(task);
        end
        
        function reset(task)
            setComponentsToDefault(task);
            updateComponents(task);
        end
    end
end

Configure Live Editor Task Metadata

To configure the task metadata, call the matlab.task.configureMetadata function and select the NormalizeVectorData.m file. The Task Metadata dialog box opens with all of the required task metadata details prepopulated.

Select OK to use the prepopulated metadata details. MATLAB creates a folder named resources inside the folder containing your task class definition file. Inside the resources folder, MATLAB generates a file named liveTasks.json. Add the folder containing the task class definition file to the MATLAB path by calling the addpath function or using the Add Folder button in the Set Path dialog box. To make your task available in the Live Editor in future MATLAB sessions, save the path by calling the savepath function or using the Save button in the Set Path dialog box.

Add Live Editor Task to Live Script

On a code line, type vector. MATLAB shows a list of suggested matches.

Select Normalize Vector Data from the list. MATLAB adds the Normalize Vector Data task to the live script.

See Also

| | |

Related Topics