octaveuicontrol

callback function throws unexpected "nonconformant arguments" error


I'm using GNU Octave, version 4.4.1. I'm trying to make an interactive plot of a simple oscillator by including two sliders which would allow the initial velocity and oscillator mass to be changed. The plot itself shows fine, as well as the script with one slider (for velocity). Here's a part of that script with the callback function:

function titranje_ia1 (hslider, event)

v0 = get ( hslider, "value" );
m = 1;
k = 1;
t = 0:0.1:30;
x = v0*sin(sqrt(k/m)*t);
axes ('position', [0.1, 0.2, 0.8, 0.75]);
h = plot ( t, x );
axis ([0 30 -11 11]);
set (h, "linewidth", 2);
set (gca, "xlabel", "t (s)", "ylabel", "x (m)", "fontsize", 12);
set (gca, 'XTick', 0:pi:10*pi)
set (gca, 'XTickLabel', {'0','\pi','2\pi','3\pi','4\pi','5\pi','6\pi','7\pi','8\pi','9\pi','10\pi'})
grid on;
l = legend (sprintf('v0 = %f', v0));
set (l, "fontsize", 12)

endfunction

However, when I include a second slider

function titranje_ia2 (hslider1, hslider2, event)

v0 = get ( hslider1, "value" );
m = get ( hslider2, "value" );
k = 1;
t = 0:0.1:30;
x = v0.*sin(sqrt(k./m).*t);
axes ('position', [0.1, 0.2, 0.8, 0.75]);
h = plot ( t, x );
axis ([0 30 -11 11]);
set (h, "linewidth", 2);
set (gca, "xlabel", "t (s)", "ylabel", "x (m)", "fontsize", 12);
set (gca, 'XTick', 0:pi:10*pi)
set (gca, 'XTickLabel', {'0','\pi','2\pi','3\pi','4\pi','5\pi','6\pi','7\pi','8\pi','9\pi','10\pi'})
grid on;
l = legend (sprintf('v0 = %f', v0));
set (l, "fontsize", 12)

endfunction

I receive an error:

error: titranje_ia2: product: nonconformant arguments (op1 is 0x0, op2 is 1x301) execution error in graphics callback function

Since I know that 'k' is a scalar and 't' a vector (but I'm not sure what v0 and m would be; I suppose scalars), I included an elementwise operations in function 'x' definition. 't' size is 1x301, so I assume that 'sqrt(k./m)' is 0x0 (as seen by Octave). Shouldn't it be 1x1? Indeed, when I try

size(m)

I receive ans = 0 0 (for size(v0) I get ans = 1 1). Could it be that there is a problem with slider definition? I include at the end both slider definitions:

%Definiramo ui element: 'klizac' za v0
hslider1 = uicontrol (
"style", "slider",
"units", "normalized",
"position", [0.1, 0.0, 0.8, 0.1],
"min", 1,
"max", 10,
"value", 4,
"callback", @titranje_ia2
);

%Definiramo ui element: 'klizac' za m
hslider2 = uicontrol (
"style", "slider",
"units", "normalized",
"position", [0.1, 0.05, 0.8, 0.1],
"min", 1,
"max", 10,
"value", 1,
"callback", @titranje_ia2
);

Solution

  • You seem to be misunderstanding a bit how callbacks work. Whenever you interact with a uicontrol object, the associated callback is always called automatically with [at least] two arguments: the first is always the 'handle' of the corresponding uicontrol object that triggerred he callback, and the second is the type of 'event' that was triggered. Therefore your callback function needs to always have a signature of callbackname( hndl, evt ) at the very least, so that it can handle those two arguments passed to it by default.

    You can specify extra arguments to passed to the callback if you want, but these will necessarily be interpreted as 3rd, 4th, and so on, inside the callback function (see example below).

    If you have a case like yours where you don't really care where the event originated from, and you want to affect (or in this case 'read from') two existing uicontrols in a single callback, regardless which one triggerred the event, the simplest thing to do is to make both slider handles to be extra arguments to the callback, and simply ignore the first argument (i.e. the 'active handle') inside your callback. (in fact, you're already ignoring the 'event' argument too!).

    Here's an example:

    %% In file makesliders.m
    function makesliders()
      % Label and slider for initial velocity
        IV_label  = uicontrol( 'style', 'text'  , 'units', 'normalized', 'position', [0.10, 0.025, 0.30, 0.075], 'string', 'Initial Velocity' );
        IV_slider = uicontrol( 'style', 'slider', 'units', 'normalized', 'position', [0.45, 0.025, 0.50, 0.075], 'min', 1, 'max', 10 );
    
      % Label and slider for oscillator mass
        OM_label  = uicontrol( 'style', 'text'  , 'units', 'normalized', 'position', [0.10, 0.125, 0.30, 0.075], 'string', 'Oscillator Mass ' );
        OM_slider = uicontrol( 'style', 'slider', 'units', 'normalized', 'position', [0.45, 0.125, 0.50, 0.075], 'min', 1, 'max', 10 );
    
      % Set callbacks and initial values to IV and OM sliders, and plot initial graph
        v0_init = 4;   m_init = 1;
        set( IV_slider, 'value', v0_init, 'callback', { @slider_callback, IV_slider, OM_slider } );
        set( OM_slider, 'value', m_init , 'callback', { @slider_callback, IV_slider, OM_slider } );
        plot_oscillation( v0_init, m_init );
    endfunction
    
    function slider_callback (active_handle, event, IV_slider, OM_slider )
        v0 = get( IV_slider, 'value' );
        m  = get( OM_slider, 'value' );
        plot_oscillation( v0, m );
    endfunction
    
    function plot_oscillation( v0, m )
        k  = 1;
        t  = 0 : 0.1 : 30;
        x = v0 * sin( sqrt( k / m ) * t );
    
        h = plot( t, x );   set( h  , 'linewidth', 2);
        set( gca, 'position', [0.1, 0.325, 0.85, 0.650], 'xlim', [0, 30], 'ylim', [-11, 11], 'xlabel', 't (s)', 'ylabel', 'x (m)', 'fontsize', 12, 'xtick', [0 : pi : 10 * pi], 'xticklabel', strcat( arrayfun( @num2str, 0:10, 'uniformoutput', false ), '\pi' ), 'xgrid', 'on', 'ygrid', 'on' );
        l = legend( sprintf( 'v0 = %.2f, m = %.2f', v0, m ) );   set( l, 'fontsize', 12 );
    endfunction