admin管理员组

文章数量:1359409

Preamble

Some time ago, I created an application in matlab where I could move/rotate some object directly from the mouse (i.e. some kind of mini-cadtool interface):

I created the application on the go and was directly attaching to WindowButtonMotionFcn callback in figure properties and using the undocumented hittest function to detect objects below the mouse and everything was fine ...

Now I would like to rework this code to extract cad-manipulation part to some sort of uicad object just like we can have uicontrol, uipanel, etc... so I can use it as component on the shelf and plug it in any figure I want.

Especially I wanted not to directly modify WindowButtonMotionFcn of the parent figure ... Indeed, if I want to do something modular, I cannot guarantee that this callback is not already in use (or will be in use) by someone else.

I then figured out that I could use equivalent WindowMouseMotion event in figure object so this way it is possible to attach as many event listeners as we want (see addlistener) without disturbing other listeners or even disturbing the WindowButtonMotionFcn property itself (which support only one listener).

Problem

Unfortunately when using event instead of callback (which obviously should be the same), hittest function no longer work as espected and no longer return the object which is below the mouse. Below code illustrate the issue:

function [] = TestFigEvents()
%[
    % Create figure
    fig = figure(666); 
    clf(fig, 'reset'); 
    fig.Color = 'black';

    % Add axes
    ax = axes(); 
    daspect(ax, [1 1 1]); 
    ax.Visible = 'off';
    ax.Clipping = 'off';
    view(ax, -45, 30);

    % Add something
    s = surface(ax, 160*membrane(1,100));
    s.EdgeColor = 'none';

    % Different way to attach to mouse move event
    if (true)
        fig.WindowButtonMotionFcn = @onMouseMove;
    else
        addlistener(fig, 'WindowMouseMotion', @onMouseMove);
    end
%]
end
function [] = onMouseMove(s, e)
%[
    ht = hittest(s); % Will only detect correct object if using 'WindowButtonMotionFcn'
    disp(ht);  
%]
end

When going through WindowButtonMotionFcn, the hittest function indeed return the correct surface object when the mouse is over it. But when going through WindowMouseMotion event it is always returning the figure object only.

Question

Well I guess nobody outside Mathworks staff can help me solving for undocumented hittest not working when using events instead of callbacks, anyway:

  • Is there another fast way I can detect objects below the mouse pointer rather than using hittest ?
  • Is there a way I can attach to WindowButtonMotionFcn callback even if some callback is already defined for it ?
  • Maybe there already exist some sort of uicad toolbox that I could use to move/rotate object easily from the mouse (paying toolbox is ok) ?

edit

I tested both with R2022b and R2024b ... and also both with figure and uifigure ... Here below is test code with uifigure:

function [] = TestUiFigEvents()
%[
    % Create figure
    fig = uifigure(); 
    fig.Color = 'black';

    % Add axes
    ax = uiaxes(fig); 
    daspect(ax, [1 1 1]); 
    ax.Visible = 'off';
    ax.Clipping = 'off';
    view(ax, -45, 30);

    % Add something
    s = surface(ax, 160*membrane(1,100));
    s.EdgeColor = 'none';

    % Different way to attach to mouse move event
    if (false)
        fig.WindowButtonMotionFcn = @onMouseMove;
    else
        addlistener(fig, 'WindowMouseMotion', @onMouseMove);
    end
%]
end
function [] = onMouseMove(s, e)
%[
    ht = hittest(e.Source); % Will only detect correct object if using 'WindowButtonMotionFcn'
    disp(ht);  
%]
end

Preamble

Some time ago, I created an application in matlab where I could move/rotate some object directly from the mouse (i.e. some kind of mini-cadtool interface):

I created the application on the go and was directly attaching to WindowButtonMotionFcn callback in figure properties and using the undocumented hittest function to detect objects below the mouse and everything was fine ...

Now I would like to rework this code to extract cad-manipulation part to some sort of uicad object just like we can have uicontrol, uipanel, etc... so I can use it as component on the shelf and plug it in any figure I want.

Especially I wanted not to directly modify WindowButtonMotionFcn of the parent figure ... Indeed, if I want to do something modular, I cannot guarantee that this callback is not already in use (or will be in use) by someone else.

I then figured out that I could use equivalent WindowMouseMotion event in figure object so this way it is possible to attach as many event listeners as we want (see addlistener) without disturbing other listeners or even disturbing the WindowButtonMotionFcn property itself (which support only one listener).

Problem

Unfortunately when using event instead of callback (which obviously should be the same), hittest function no longer work as espected and no longer return the object which is below the mouse. Below code illustrate the issue:

function [] = TestFigEvents()
%[
    % Create figure
    fig = figure(666); 
    clf(fig, 'reset'); 
    fig.Color = 'black';

    % Add axes
    ax = axes(); 
    daspect(ax, [1 1 1]); 
    ax.Visible = 'off';
    ax.Clipping = 'off';
    view(ax, -45, 30);

    % Add something
    s = surface(ax, 160*membrane(1,100));
    s.EdgeColor = 'none';

    % Different way to attach to mouse move event
    if (true)
        fig.WindowButtonMotionFcn = @onMouseMove;
    else
        addlistener(fig, 'WindowMouseMotion', @onMouseMove);
    end
%]
end
function [] = onMouseMove(s, e)
%[
    ht = hittest(s); % Will only detect correct object if using 'WindowButtonMotionFcn'
    disp(ht);  
%]
end

When going through WindowButtonMotionFcn, the hittest function indeed return the correct surface object when the mouse is over it. But when going through WindowMouseMotion event it is always returning the figure object only.

Question

Well I guess nobody outside Mathworks staff can help me solving for undocumented hittest not working when using events instead of callbacks, anyway:

  • Is there another fast way I can detect objects below the mouse pointer rather than using hittest ?
  • Is there a way I can attach to WindowButtonMotionFcn callback even if some callback is already defined for it ?
  • Maybe there already exist some sort of uicad toolbox that I could use to move/rotate object easily from the mouse (paying toolbox is ok) ?

edit

I tested both with R2022b and R2024b ... and also both with figure and uifigure ... Here below is test code with uifigure:

function [] = TestUiFigEvents()
%[
    % Create figure
    fig = uifigure(); 
    fig.Color = 'black';

    % Add axes
    ax = uiaxes(fig); 
    daspect(ax, [1 1 1]); 
    ax.Visible = 'off';
    ax.Clipping = 'off';
    view(ax, -45, 30);

    % Add something
    s = surface(ax, 160*membrane(1,100));
    s.EdgeColor = 'none';

    % Different way to attach to mouse move event
    if (false)
        fig.WindowButtonMotionFcn = @onMouseMove;
    else
        addlistener(fig, 'WindowMouseMotion', @onMouseMove);
    end
%]
end
function [] = onMouseMove(s, e)
%[
    ht = hittest(e.Source); % Will only detect correct object if using 'WindowButtonMotionFcn'
    disp(ht);  
%]
end
Share Improve this question edited Mar 27 at 17:06 CitizenInsane asked Mar 27 at 16:03 CitizenInsaneCitizenInsane 4,8752 gold badges29 silver badges61 bronze badges 4
  • Which version of MATLAB are you using? Answers concerning the latest JavaScript-based uifigure interaction might be very different to what came before – Wolfie Commented Mar 27 at 16:49
  • @Wolfie I tested both with R2022b and R2024b and both with figure and uifigure (same issue in all cases). I edited question accordingly. – CitizenInsane Commented Mar 27 at 17:07
  • Regarding difference between event and callback I suspect somehow like this is happening in matlab arcanes: Mouse is moved > HitTest cache is reset > Event WindowMouseMotion is fired > HitTest cache is updated > WindowButtonMotionFcn callback is called – CitizenInsane Commented Mar 27 at 17:25
  • I made some quick test and quite confirm above suspisions ... so i think i can workaround by temporarely replacing WindowButtonMotionFcn in addlistener event listener and restore everything at the end of the call ... I will make more clean code for workaround i'm thinking of. – CitizenInsane Commented Mar 27 at 18:02
Add a comment  | 

1 Answer 1

Reset to default 0

Suspicions I had that cascade of actions internally to matlab are likely as follow:

Mouse is moved > HitTest cache is reset > Event WindowMouseMotion is fired > HitTest cache is updated > WindowButtonMotionFcn callback is called

seems to be true, so I came out with following solution (here using UserData to store user-defined callback if any + keeping debug messages, just for demo purpose):

function [] = TestFigEvents()
%[
    ...

    % Attach as an event
    addlistener(fig, 'WindowMouseMotion', @onMouseMove);   
%]
end
function [] = onMouseMove(s, e)
%[
    if (~iscell(s.UserData))

        disp('Begin >> Event');

            % Called by 'WindowMouseMotion' event
            % So just temporarely replace 'WindowButtonMotionFcn'
            s.UserData = { s.WindowButtonMotionFcn };
            s.WindowButtonMotionFcn = @onMouseMove;
            
            % Not clear why but does not work if not performing this 'drawnow'
            drawnow; 

        disp('End << Event');

    else

        disp('Begin >> Callback');

            % Called by 'WindowButtonMotionFcn' callback
            % So do my stuff now that 'hittest' is ok
            disp('Begin >> My own stuff');
                ht = hittest(s);
                disp(ht);
            disp('End << My own stuff');                
    
            % Restore inal 'WindowButtonMotionFcn' an call it if not empty
            s.WindowButtonMotionFcn = s.UserData{1};
            s.UserData = [];
            if (~isempty(s.WindowButtonMotionFcn)), 
                disp('Begin >> User defined stuff');
                    s.WindowButtonMotionFcn(s, e); 
                disp('end << User defined stuff');
            end

        disp('End << Callback');

    end
%]
end

NB1: So far it is unclear why I need to call drawnow but without it is not working (adding limitrate option just decrease precision for hittest detection, and nocallbacks make it not to work).

NB2: Just doing a drawnow in initial event-listener does not work:

function [] = onMouseMove(s, e)
%[
    drawnow(); % This only does not make hittest to work
    ht = hittest(s);
    disp(ht);
%]
end

So I will go ahead with temporarely replacing WindowButtonMotionFcn callback if any (+ quite strange but mandatory drawnow).

本文标签: matlabUndocumented hittest no longer working as expectedStack Overflow