Use Callback function to change string stored in it

I'm writing a code to create an array of pushbuttons. Everytime a button is it should change it's color, based on how many times the user has already clicked the button. I thought about making one single callbackfunction for all buttons. Because I need to check what color the button is I thought about giving a number stored in the button to the callbackfunction. In the callbackfunction itself I will change the color of the button and assign another number to the corresponding button.
That's what I've got so far, but I don't know what the general structure of the callback function should look like and if this even works.
%% Create Bimaru Cells
xorigin=45;
yorigin=410;
distance=35;
for i=1:9 %% Number of columns
for j=1:9 %% Number of rows
xposcell= xorigin+(j-1)*distance;
yposcell= yorigin-(i-1)*distance;
bimarugui(i,j)= uicontrol('unit', 'pixels',... %%allocate an array of white pushbuttons
'background', 'white',...
'Style', 'pushbutton',...
'Value', [1,i,j],... %%stores the counter variable, and the location
'visible', 'on',... in the array
'position',[xposcell,yposcell,distance distance],...
'enable','on',...
'Callback',{@cell_callback,get(bimarugui(i,j),'Value')});

Answers (1)

Your idea to controll all button colors from a single callback function is a good idea but it can be simplified.
The callback function could store the order of the colors and one of its inputs would be the handle to the button whose color will be changed. All it has to do is look up the current color of the button within the matrix of colors and then assigne the following color. It would look something like this:
function changeButtonColor(buttonHandle)
% Define all colors
buttonColors = jet(10);
% Get current button color
currColor = buttonHandle.BackgroundColor;
% Look up which row of buttonColors matches current color
currColorIdx = find(ismembertol(buttonColors, currColor, 'ByRows', true));
% If a match wasn't found or if the index is the last row, assign row 0
if isempty(currColorIdx) || isequal(currColorIdx, size(buttonColors,1))
currColorIdx = 0;
end
% Assign next color to button
buttonHandle.BackgroundColor = buttonColors(cucurrColorIdx + 1);
*Not tested
To change the color of a button, you would just pass the button's handle to that function.
changeButtonColor(handles.Button1);

15 Comments

I actually like the idea and functionality but I don't know how this handles structure works. I already read some posts about that topic but I don't get how to implement that in my code. Besides I really wonder why there isn't an option to do something like this in app designer.
The handles structure contains handles to all of your app components (when using GUIDE). All you have to do is pass the button handle to that function.
I updated my answer to demonstrate that at the end.
BTW, you can use this exact same approach in appdesigner!
Let me know if you have any questions.
I'm not using Guide or appdesigner at the moment. I tried appdesigner, but I always struggled with adressing the specific pushbutton using the same callbackfunction. Could you help me with that?
The problem is i cant write the specific
changeButtonColor(handles.Button1);
in the function because the function doesn't know which pushbutton's color to change and it's not always the same button.
So If it's possible to translate the code to appdesigner that would be awesome. How is it done there?
if i type app. etc. all i can chose are the specific buttons, but the function should change the color of the actual button being pressed. thats why i came up with this whole number assignment thing.
"I'm not using Guide or appdesigner at the moment.",
Ah, right - I see clearly in the code in your question that you're using uicontrol. Good! This is the best approach to GUIs IMO. It's still a good idea to create a handles stucture to keep all of your GUI's handles together.
"I tried appdesigner, but I always struggled with adressing the specific pushbutton using the same callbackfunction. Could you help me with that?"
Sure. See "step 1" in this answer I provided a few days ago. It shows how to add a callback function to a button. I suggest creating a separate callback function for all of your buttons.
Then, from the code-view of appdesigner, you can add another function that will be the changeButtonColor function from my answer. One the left of the appdesigner UI you'll see "code browser", select "functions" and then press the green + button. Name the function and replace the automatically-generated "app" input with the "buttonHandle" input from my answer. Copy the rest of the code from my answer to that function.
From the button callback function, call the new function as I've demonstrated at the end of my answer.
"...the function doesn't know whch pushbutton's color to change"
Yes, it does know because that's the input: the handle to the pushbutton. This isn't a button callback function. This is an independent function that is called from anywhere within the app. A button's callback function could call this function any time it wants to change color.
I get now what handles do, but not how they are implemented/allocated...
Anyway what I did now in appdesigner:
-Created a trial program with two pushbuttons
-Added the color changing function as follows:
methods (Access = private)
function changeButtonColor(buttonHandle)
% Define all colors
buttonColors = jet(10);
% Get current button color
currColor = buttonHandle.BackgroundColor;
% Look up which row of buttonColors matches current color
currColorIdx = find(ismembertol(buttonColors, currColor, 'ByRows', true));
% If a match wasn't found or if the index is the last row, assign row 0
if isempty(currColorIdx) || isequal(currColorIdx, size(buttonColors,1))
currColorIdx = 0;
end
% Assign next color to button
buttonHandle.BackgroundColor = buttonColors(cucurrColorIdx + 1);
end
end
-Added the call to this function in the callback of the pushbutton:
changeButtonColor(handles.Button1);
Problem: There's an error saying -> Unable to resolve the name handles.Button1
I guess it doesnt know the structure handles yet. Your thoughts?
If you're using appdesigner, the handle to the button will be stored in the app structure. There is no handles structure in appdesigner unless you created one.
The handle will look something like this: app.Button1
Thanks! I think with this amount of buttons it's easier to work with the uicontrol function so i will work on my old code as shown in the question.
What I want to do now is Ive got this array of pushbuttons:
for i=1:9 %% Number of columns
for j=1:9 %% Number of rows
xposcell= xorigin+(j-1)*distance;
yposcell= yorigin-(i-1)*distance;
bimarugui(i,j)= uicontrol('unit', 'pixels',... %%allocate an array of white pushbuttons
'background', 'white',...
'Style', 'pushbutton',...
'Value', [1,i,j],... %%stores the counter variable, and the location
'visible', 'on',... in the array
'position',[xposcell,yposcell,distance distance],...
'enable','on');
And now I want to compare some properties of another than the actual pressed button in the callbackfunction, so i tried to pass the handles to these buttons but I think I got something wrong with the syntax here. Do you have a solution for that? Thank you so much!
for l=2:8
for m=2:8
set(bimarugui(l,m),'Callback',@buttonclick,bimarugui(l+1,m),bimarugui(l-1,m),bimarugui(l,m-1),bimarugui(l,m+1))
end
end
In app designer, GUIDE, and many other GUI systems in other languages, a GUI callback always receive two arguments: the object that triggers the callback and some specific event argument object (eg. the state of mouse buttons for a mouse clicks). typically the source is also one of the property of the event argument object, so you get the information twice.
With app designer, this breaks up a bit since App designer's event handling eats the source argument and give you the app instance instead. Thankfully, you still get the event argument with its Source property. The Source property will contain the actual button clicked, so your callback signature (which app designer won't let you edit anyway) should be:
function ButtonsPushedCallback(app, event) %same callback called by all buttons
actualbutton = event.Source;
curcolour = actualbutton.BackgroundColor;
%... compute next colour
actualbutton.BackgrounColor = newcolour;
end
@Guillaume, the changeButtonColor function from my answer is an independent function in the GUI, not a callback function. I'm proposing that the button callback functions include a line of code that calls the changeButtonColor function. That way each button would have its own function space without the need to duplicate the color-change code.
@Juri Augsburger, will the buttons have any actions other than changing their own color? If that's the buttons' only action, then the changeButtonColor function can be changed to a callback function (which is what Guillaume was getting to).
If the buttons' only actions are to change their own color:
Change the changeButtonColor function to behave like a callback function:
function changeButtonColor(buttonHandle, ~)
% (everything else the same)
and set the callback function to
set(bimarugui(l,m),'Callback',@changeButtonColor
If the buttons' callback functions each have different actions:
Assign the buttons callback functions and within each function, call the changeButtonColor function as demonstrated in my answer.
Hi guys, thank you a lot for your answers!
The color changing is "basically" the only function, but the color schould be different depending on what background color is assigned to the surrounding buttons. Therefore I need to pass the handles of the surrounding pushbuttons to the callbackfunctions. I just believe the array structure helps organizing a bit better than using 81 individual pushbuttons.
In that case, instead of passing several button handles as separate inputs, you could combine them in a cell array and pass them as a single input.
function changeButtonColor(hObj, ~, buttonList)
% hobj is the handle to the button that evoked the callback
% buttonList is a cell array of button handles
end
set(bimarugui(l,m),'Callback',{@changeButtonColor, {bimarugui(l-1,m),bimarugui(l,m-1), . . .}})
Thank you this makes sense.
How can I access a property with a handle from this list?
Here's what I tried:
function buttonclick(object_handle, ~,buttonlist)
if all(get(buttonlist(1),'background')-[1,1,1])==0
set(object_handle,'background','green');
else
set(object_handle,'background','black');
end
end
Juri, instead of storing the handles in a cell array, they can be stored in a handle array which will make indexing easier. I should have suggested that from the beginning. My bad.
set(bimarugui(l,m),'Callback',{@changeButtonColor, [bimarugui(l-1,m),bimarugui(l,m-1), . . .]})
% square brackets ^ .......................................^
To get a property from a single handle stored within a handle array
get(buttonlist(1),'BackgroundColor')
% or simply
buttonlist(1).BackgroundColor; % Case sensitive
To get a n x 3 matrix of BackgroundColors from all handles,
cell2mat({buttonList.Color}')
I'm confused now. Hasn't the GUI migrated to app designer? If so, there's no need for storing any handle. As I've explained the common callback to all the button will receive the button object that was pressed in its event.Source argument.
I can see why it's confusing since the dialog is jumping back and forth a bit. In this comment above, OP mentioned that they will stick with the uicontrol functions rather than appdesigner.
Also, each button is associated with surrounding buttons and when one is pressed, the color for the entire group changes. The additional input is not a handle to a single button. It's a vector of handles that are associated with the pressed button.
Depending on how the buttons are arranged, within a grid, for example, the callback function could detect which buttons are 'near' the pressed button in which case there wouldn't need to be an additional input of associated button handles.

Sign in to comment.

Categories

Find more on Interactive Control and Callbacks in Help Center and File Exchange

Asked:

on 23 Mar 2020

Commented:

on 27 Mar 2020

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!