Search code examples
matlabdebuggingcurve-fittingdisabled-controlundocumented-behavior

How can we use the Curve Fitting Tool during debugging?


MATLAB's Curve Fitting App (previously "tool", hence, cftool) is a graphical tool for interactive curve fitting1.

The general way of working with this tool is by choosing variables from the workspace:

enter image description here However, during debugging, data selection is disabled (this is documented):

enter image description here

... which is quite a nuisance, since we must save the data to a file, and either quit debugging or open a new instance of MATLAB before we can load this data again and use it in cftool.

I would assume that the reason for disabling the inputs is because during debugging we usually have multiple workspaces, and so iterating through them or providing user choice of the workspace is too cumbersome in terms of UX - so the developers decided to disable the inputs until such time that only a single workspace exists.

My question is this: How can we disable the "debugging detection" of cftool or otherwise specify the workspace that we're interested in, so that we can use cftool during debugging?


Solution

  • I did some digging, and here's what I found:

    • The curve fitting tool contains a special type of combo boxes for choosing the variables, which employ the com.mathworks.mlservices.MatlabDebugObserver class in order to detect the debug mode and disable the controls. This java class for these controls is

      MATLAB\R20###\java\jar\toolbox\curvefit.jar!
               com.mathworks.toolbox.curvefit.surfacefitting.SFDataCombo
      

      which I found by:

      a) Starting cftool and getting the handle to its window using,

      hSFT = getappdata( groot, 'SurfaceFittingToolHandle' );
      

      b) Exploring the properties and children of hSFT to find the java object which contains the panel where we specify the fit data.

      c) Locating the .jar file containing the above java class, using the command src:

      jObj.getClass().getProtectionDomain().getCodeSource().getLocation().getPath();
      
    • We can disable the debug listener by accessing the individual combo boxes and invoking their cleanup() method, which removes the debugging listeners (see notes about this in the code below). This involves accessing the private fields of several objects, for which we'll use reflection:

      function unlockCftool()
      % NOTES: 
      % 1) After unlocking cftool, it will no longer update the list of workspace variables, so 
      % make sure all desired variables exist in the base workspace before proceeding, or you'll 
      % need to restart cftool.
      % 2) DO NOT execute this code while debugging, since then the variable selection fields in
      % cftool will be stuck in their disabled mode until it is restarted.
      
      hSFT = getappdata( groot, 'SurfaceFittingToolHandle' );
      jEFP = hSFT.FitFigures{1}.HFittingPanel.HUIPanel.Children.java.getJavaPeer();
      f = jEFP.getClass().getDeclaredField('fittingDataPanel');
      f.setAccessible(true);
      jFDP = f.get(jEFP);
      f = jFDP.getClass().getDeclaredFields(); f = f(1:4); % <- shortcut for:
      %{
      f = [jFDP.getClass().getDeclaredField('fXDataCombo');
           jFDP.getClass().getDeclaredField('fYDataCombo');
           jFDP.getClass().getDeclaredField('fZDataCombo');
           jFDP.getClass().getDeclaredField('fWDataCombo')];
      %}
      java.lang.reflect.AccessibleObject.setAccessible(f, true);
      for ind1 = 1:numel(f)
        f(ind1).get(jFDP).cleanup();
      end
      

    So now we can do the following:

    X = 0:9;
    Y = 10:-1:1;
    cftool();
    % <select the X and Y variables in cftool to get a decreasing slope>.
    unlockCftool();
    % <enter debug mode, for example using: dbstop in unlockCftool; unlockCftool(); >
    assignin('base', 'X', 5:-1:-4);
    % <re-select X to update the data - resulting in a rising slope>.