matlabmatlab-app-designer

Refresh positions of all draw points after deleting or moving - Matlab App designer


I did an app designer GUI with two buttons and axes. The first one (LoadimageButton) is loading the pappers image and I can mark points until I press escape. The second button is printing out the point coordinates (PositionButton).

I have noticed that after pressing the two buttons I can move points in the axes and change their positions or delete them. The issue is that when I press the delete (in the context menu) I get this error after pressing the PositionButton:

Error using images.roi.Point/get
Invalid or deleted object.

Error in tempDrwPnt1/PositionButtonPushed (line 61)
positions = cell2mat(get(app.pointhandles, 'position'))

Error while evaluating Button PrivateButtonPushedFcn.

How can I refresh the app.pointhandles after deleting a point?

Code:

function LoadimageButtonPushed(app, event)
            imshow('peppers.png','Parent',app.ImageAxes); 
          
            userStopped = false; 
            app.pointhandles = gobjects(); 
                   while ~userStopped
                        roi = drawpoint(app.ImageAxes); 
                        if ~isvalid(roi) || isempty(roi.Position)
                            % End the loop
                            userStopped = true;
                        else
                            % store point object handle
                            app.pointhandles(end+1) = roi;
                        end
                   end 
            

            addlistener(roi,'MovingROI',@allevents);
            addlistener(roi,'ROIMoved',@allevents);
  
            app.pointhandles(1) = []; 
            
            function allevents(src,evt)
                evname = evt.EventName;
                switch(evname)
                    case{'MovingROI'}
                        disp(['ROI moving previous position: ' mat2str(evt.PreviousPosition)]);
                        disp(['ROI moving current position: ' mat2str(evt.CurrentPosition)]);
                    case{'ROIMoved'}
                        disp(['ROI moved previous position: ' mat2str(evt.PreviousPosition)]);
                        disp(['ROI moved current position: ' mat2str(evt.CurrentPosition)]);
                end
            end 
end


% Button pushed function: PositionButton
function PositionButtonPushed(app, event) 
positions = cell2mat(get(app.pointhandles, 'position'))
end

Solution

  • You could add a check within the PositionButtonPushed function for whether or not each pointhandles element is valid, report on it if so or remove it if not

    Something like

    function PositionButtonPushed(app, event)
        nPts = numel(app.pointhandles);
        positions = NaN(nPts,2); % array to hold positions
        for iPt = nPts:-1:1 % loop backwards so we can remove elements without issue
            if isvalid( app.pointhandles(iPt) )
                positions(iPt,:) = get(app.pointhandles(iPt),'position');
            else
                % No longer valid (been deleted), remove the reference
                app.pointhandles(iPt) = [];
                % Also remove from the positions output
                positions(iPt,:) = [];
            end
        end
    end
    

    I haven't got a compatible MATLAB version available right now to test this, but I am assuming the in-built function isvalid works for Point objects, otherwise you will have to add your own validity check. Alternatively you could try to get the position, and do the deletion (handled by else above) within a catch, but I usually recommend against try/catch if you can check for a specific (known) issue instead.

    I've often used something similar (e.g. for axes), but bundled the cleanup functionality for invalid handles into its own function. In this case it would have let you add a single function call before getting the positions of the remaining valid points.

    I've also made this more condensed by using arrayfun, but under the hood it's the same approach as the above loop:

    function PositionButtonPushed(app, event )
        app.checkHandleValidity(); % cleanup handles just in case
    
        positions = cell2mat(get(app.pointhandles, 'position'));
    end
    
    function checkHandleValidity( app )
       bValid = arrayfun( @isvalid, app.pointhandles ); % Check validity
       app.pointhandles( ~bValid ) = [];                % Remove invalid elements
    end