Search code examples
matlabuser-interfacecallbackmatlab-guideuielement

How can I change the callback of a uielement using get()?


Some background

I've created a GUI using a combination of a GUIDE built Figure and procedurally placed checkboxes that are added at runtime. The checkboxes are in a matrix where the number of rows and columns is determined at runtime. To keep track of each checkbox I use a tag convention like cb_r1_c1 or cb_r7_c10 which indicates the location in the matrix.

I've created a function foo_cb_callback() which examines the hObject information passed to it to determine the tag of the checkbox that was just clicked. Using this information (row and col of the checkbox) this function knows what to do next. I find this to be a much cleaner and safer way to handle the callbacks of an unknown amount of checkboxes. So far this strategy is working great. There is only one problem.

The Problem

I'm having trouble changing the callback property of my procedurally added checkboxes to properly call my foo_cb_callback() function.

When you examine an existing checkbox with an existing call back using get(objhandles.cb_r1_c1,'callback')

the returned variable is of class "function_handle" and prints a string of text to the screen

@(hObject,eventdata)testfuig('checkbox1_Callback',hObject,eventdata,guidata(hObject))

I would like to change this to

@(hObject,eventdata)testfuig('foo_cb_callback',hObject,eventdata,guidata(hObject))

What I Tried First

My first attempt was something like this.

callbackstr = '@(hObject,eventdata)testfuig(''foo_cb_callback'',hObject,eventdata,guidata(hObject))';
set(objhandles.cb_r1_c1,'callback',callbackstr);

But this doesn't work since it is just a string and not a function_handle classed object. It seems like there should be some way to take a string or cell array and convert it to a function_handle class. This would be the most ideal situation since I could easily control the callback of any uielement easily.

What I Tried Next

Since I was already constructing the checkboxes using the 'uicontrol()' function I tried to add the callback at the time of creation.

tagstr = 'cb_r1_c1';
uicontrol(objhandles.mainfig, 'Style','checkbox','Tag',tagstr,'callback', 'foo_cb_callback(''hObject''));

This created the checkbox and indeed had foo_cb_callback(hObject) in the callback property, but when the checkbox is clicked I get an error ??? Undefined function or variable 'hObject'.

So I Gave Up, and Worked Around It

I found two non-ideal solutions to work around this issue. The first was to create a non visible checkbox in GUIDE with tag cb_hidden. Then I was able to use the following code.

callback = get(objhandles.cb_hidden, 'callback');
set(objhandles.cb_r1_c1,'callback',callback);

This correctly set the callback of cb_r1_c1 to the same callback as cb_hidden. In this way I was able to redirect all checkbox callbacks to this dummy checkbox, and from there call foo_cb_callback(hObject).

However, this solution is non ideal because it requires a hidden checkbox somewhere in the GUI.

Another Work Around

Since I was creating unique tags at runtime for each checkbox, I decided to write the tag directly into the callback string.

tagstr = 'cb_r1_c1';
uicontrol(objhandles.mainfig, 'Style','checkbox','Tag',tagstr,'callback', 'foo_cb_callback(''' tagstr  '''));

This sets the callback to foo_cb_callback('cb_r1_c1') which correctly calls foo_cb_callback() and passes the string 'cb_r1_c1'. The first few lines of foo_cb_callback() look like this

function foo_cb_callback(tag)
global gdata
hObject = gdata.objhandles.(tag);

From there I can do pretty much anything I would have been able to do using an ideal solution. However, this solution is still not ideal, since I am unable to pass in variables (other than non changing strings). Also, I would really like to know the proper way to do this, I'm sure I will find a use for it someday.


Solution

  • Does something simple like this not work?

    function myCallback(src, event)
        disp('myCallback called');
    end
    
    
    set(hObject, 'callback', @myCallback);