How to loop through pushbuttons (=tiles in a maze) with a single callback?

I am building a 5 by 5 maze in Matlab, made of pushbuttons (each pushbutton is a tile in the maze). The beginning tile is green; the end tile is yellow. Open tiles are white and blocked tiles (the walls) are blue. Colors are preallocated through a 5 by 5 matrix (A), being white=0, blue=1, yellow=2 and green=3. My main function sets up a 5 by 5 matrix A and then calls a function "create_maze":
A = [0 3 0 1 0 ; 0 0 0 0 1 ; 1 0 0 0 1; 1 1 0 0 1; 1 0 2 0 0];
create_maze(A)
The function also makes only some tiles visible, the ones around the green tile.
So far so good. Now I would like to click on a white tile and have the adjacent tiles appearing. I would like to build a callback function that loops through all the pushbutton/tiles (so I'll need to write only one function). I have put all the pushbuttons in a matrix L and managed to set a callback function to it. The program does run without error (so the callback is kind of working) but... nothing happens! I don't understand why. If you could have a look I would really appreciate it!
This is my function create_maze:
function create_maze(A, varargin)
I set the screen:
scr = get(0, 'screensize');
f1 = figure(1);
set(f1, 'menubar', 'none');
set(f1, 'position', [scr(1) scr(2) scr(3) scr(4)]);
create 25 pushbuttons (sorry...)
h1 = uicontrol('Style', 'pushbutton',...
'BackgroundColor', [0.91 0.91 0.91],...
'String', '',...
'Position', [200 200 100 100]);
h2 = uicontrol('Style', 'pushbutton',...
'BackgroundColor', [0.91 0.91 0.91],...
'Position', [300 200 100 100]);
h3 = uicontrol('Style', 'pushbutton',...
'BackgroundColor', [0.91 0.91 0.91],...
'Position', [400 200 100 100]);
h4 = uicontrol('Style', 'pushbutton',...
'BackgroundColor', [0.91 0.91 0.91],...
'Position', [500 200 100 100]);
h5 = uicontrol('Style', 'pushbutton',...
'BackgroundColor', [0.91 0.91 0.91],...
'Position', [600 200 100 100]);
h6 = uicontrol('Style', 'pushbutton',...
'BackgroundColor', [0.91 0.91 0.91],...
'Position', [200 300 100 100]);
h7 = uicontrol('Style', 'pushbutton',...
'BackgroundColor', [0.91 0.91 0.91],...
'Position', [300 300 100 100]);
h8 = uicontrol('Style', 'pushbutton',...
'BackgroundColor', [0.91 0.91 0.91],...
'Position', [400 300 100 100]);
h9 = uicontrol('Style', 'pushbutton',...
'BackgroundColor', [0.91 0.91 0.91],...
'Position', [500 300 100 100]);
h10 = uicontrol('Style', 'pushbutton',...
'BackgroundColor', [0.91 0.91 0.91],...
'Position', [600 300 100 100]);
h11 = uicontrol('Style', 'pushbutton',...
'BackgroundColor', [0.91 0.91 0.91],...
'Position', [200 400 100 100]);
h12 = uicontrol('Style', 'pushbutton',...
'BackgroundColor', [0.91 0.91 0.91],...
'Position', [300 400 100 100]);
h13 = uicontrol('Style', 'pushbutton',...
'BackgroundColor', [0.91 0.91 0.91],...
'Position', [400 400 100 100]);
h14 = uicontrol('Style', 'pushbutton',...
'BackgroundColor', [0.91 0.91 0.91],...
'Position', [500 400 100 100]);
h15 = uicontrol('Style', 'pushbutton',...
'BackgroundColor', [0.91 0.91 0.91],...
'Position', [600 400 100 100]);
h16 = uicontrol('Style', 'pushbutton',...
'BackgroundColor', [0.91 0.91 0.91],...
'Position', [200 500 100 100]);
h17 = uicontrol('Style', 'pushbutton',...
'BackgroundColor', [0.91 0.91 0.91],...
'Position', [300 500 100 100]);
h18 = uicontrol('Style', 'pushbutton',...
'BackgroundColor', [0.91 0.91 0.91],...
'Position', [400 500 100 100]);
h19 = uicontrol('Style', 'pushbutton',...
'BackgroundColor', [0.91 0.91 0.91],...
'Position', [500 500 100 100]);
h20 = uicontrol('Style', 'pushbutton',...
'BackgroundColor', [0.91 0.91 0.91],...
'Position', [600 500 100 100]);
h21 = uicontrol('Style', 'pushbutton',...
'BackgroundColor', [0.91 0.91 0.91],...
'Position', [200 600 100 100]);
h22 = uicontrol('Style', 'pushbutton',...
'BackgroundColor', [0.91 0.91 0.91],...
'Position', [300 600 100 100]);
h23 = uicontrol('Style', 'pushbutton',...
'BackgroundColor', [0.91 0.91 0.91],...
'Position', [400 600 100 100]);
h24 = uicontrol('Style', 'pushbutton',...
'BackgroundColor', [0.91 0.91 0.91],...
'Position', [500 600 100 100]);
h25 = uicontrol('Style', 'pushbutton',...
'BackgroundColor', [0.91 0.91 0.91],...
'Position', [600 600 100 100]);
put the pushbuttons in a matrix L
L = [h1 h2 h3 h4 h5; h6 h7 h8 h9 h10;h11 h12 h13 h14 h15;...
h16 h17 h18 h19 h20; h21 h22 h23 h24 h25];
set the colors of the pushbuttons and...
for i = 1: size(A,1) % rows
for j = 1: size(A,2) % columns
if A(i,j) == 0
set(L(i,j),'BackgroundColor', 'w')
set(L(i,j), 'visible', 'off')
elseif A(i,j) == 1
set(L(i,j),'BackgroundColor','b')
set(L(i,j), 'visible', 'off')
elseif A(i,j) == 2
set(L(i,j),'BackgroundColor','y')
set(L(i,j),'String','GOAL')
set(L(i,j), 'visible', 'on')
elseif A(i,j) == 3
set(L(i,j),'BackgroundColor','g')
end
end
...make only some pushbuttons (the ones around the green one) visible
for i = 1: size(A,1) % rows
for j = 1: size(A,2) % columns
if (1 < i) && A(i-1,j) == 3 ||...
(1 < j) && A(i,j-1) == 3
set(L(i,j), 'visible', 'on')
elseif (i < length(A)) && A(i+1,j) == 3 ||...
(j < length(A)) && A(i,j+1) == 3
set(L(i,j), 'visible', 'on')
end
end
end
end
set the callback to the matrix L
set(L(i,j),'callback',{@my_funct,L,A});
set up the callback function
function [] = my_funct(source, callbackdata,L,A)
The following is the complete loop that should make the tiles adjacent to the tile I clicked on visible, but nothing happens (I don't get any error either).
for i = 1: size(A,1) % rows
for j = 1: size(A,2) % columns
if (1 < i) && A(i,j) == 0 ||...
(1 < j) && A(i,j) == 0
set(L(i,j-1), 'visible', 'on')
elseif (1 < i) && A(i,j) == 0 ||...
(1 < j) && A(i,j) == 0
set(L(i-1,j), 'visible', 'on')
elseif (i < length(A)) && A(i,j) == 0 ||...
(j < length(A)) && A(i,j) == 0
set(L(i+1,j), 'visible', 'on')
elseif (i < length(A)) && A(i,j) == 0 ||...
(j < length(A)) && A(i,j) == 0
set(L(i,j+1), 'visible', 'on')
end
end
end
end
end

 Accepted Answer

Serena - I think that you are missing an end at the end of your first double for loop block that sets the colours of the pushbuttons, and that missing end has been put at the end of the next double for loop block to make some of the pushbuttons visible. This causes two problems since both sets of double for loops use the same indexing variables i and j (as an aside, you may want to consider renaming these to something more meaningful since MATLAB also uses these to represent the imaginary number). This most likely causes a problem with the line
set(L(i,j),'callback',{@my_funct,L,A});
only being assigned to the (5,5) pushbutton control since it occurs outside of the previous double for loop. I suggest that you include this in the first block and end it appropriately as
for r = 1: size(A,1) % rows
for c = 1: size(A,2) % columns
set(L(r,c),'callback',{@my_funct});
if A(r,c) == 0
set(L(r,c),'BackgroundColor', 'w')
set(L(r,c), 'visible', 'off')
elseif A(r,c) == 1
set(L(r,c),'BackgroundColor','b')
set(L(r,c), 'visible', 'off')
elseif A(r,c) == 2
set(L(r,c),'BackgroundColor','y')
set(L(r,c),'String','GOAL')
set(L(r,c), 'visible', 'on')
elseif A(r,c) == 3
set(L(r,c),'BackgroundColor','g')
end
end
end
So now every pushbutton will be assigned the callback. Since the above outer for loop as been terminated with the end then you will have to remove the redundant end from the second block of code.
Note the change to your callback
set(L(r,c),'callback',{@my_funct});
Since the callback is a nested function within create_maze, you do not need to pass in parameters that are declared or are local to create_maze. The callback will have access to these elements since it is nested. This will require you to change the callback signature to
function [] = my_funct(source, callbackdata)
So that should get the callback to fire. The next step will be to revisit your callback logic - you probably only want "do" anything if the user is pressing a white pushbutton. You can use the source input along with the property BackgroundColor to retrieve the colour as
bgColor = get(source,'BackgroundColor');
if all(bgColor)
% pushbutton is white
% do something
end
Since the bgColor is a 1x3 array indicating the RGB color, if it is all ones then the button background is white.
Now you presumably need to determine the neighbours of this white pushbutton only. How can you do that since all you have is the source (which is the handle to the pushbutton)? Since the handles are of a floating precision (double) data type, you don't want to be looking through each element of L and comparing each to this handle. So how else can you determine where in L (and so A) this handle is? Hint - look back at how you assign the callback to the pushbutton and consider using the r and c as the unique identifier into L and A.

6 Comments

Hi Geoff,
First, thanks a lot for taking the time to answer my question. I have fixed the loops as you suggested. I have also understood how I can retrieve the color values and many other types of values. I tried before to get this information with many different attempts but always unsuccessfully. I would like to ask you if you could please point me to some beginner's internet resources to understand how to build a GUI programmatically in Matlab; most of the stuff I found online refers to GUIDE.
At this point I am still at loss in how to retrieve the value of the specific handle and connect it with L and A (and looping through using r, c).
I have tried many things; for example:
h1 = ...'Position', [200 200 100 100],'callback',{@my_funct,L});
and then:
function [] = my_funct(source, callbackdata, x)
but I get an error message.
If instead I write:
h1 = ...'Position', [200 200 100 100],'callback',{@my_funct,h1});
and then:
function [] = my_funct(source, callbackdata, x)
I can then retrieve the h1, but still, how to connect this handle with the values in A and L I can loop through?
I have also another question. I found that, with your code, I can retrieve the pushbutton's position as:
pos = get(source, 'Position')
left= pos(1)
bottom= pos(2)
widht = pos(3)
height = pos(4)
Is there any way I can use this data to loop through instead? Writing... "if the pushbutton is white, make the ones in surrounding position visible"?
Hi Serena - you could look at this video http://www.mathworks.com/videos/creating-a-gui-with-guide-68979.html to get an idea of how to create a GUI using GUIDE. There may be other tutorials or webinars that may provide some instruction too.
As for which element in L or A is the callback being fired for, consider this: we assign the callback for the (r,c) pushbutton as
set(L(r,c),'callback',{@my_funct});
Why not pass r and c into this callback too?
set(L(r,c),'callback',{@my_funct,r,c});
and update your callback signature to
function [] = my_funct(source, callbackdata,rIdx,cIdx)
where rIdx and cIdx are the row and column indices for this pushbutton into L and A. So whenever the callback fires, you will know which pushbutton in L and A that you are interested in
L(rIdx,cIdx)
and
A(rIdx,cIdx)
Hi Geoff,
Thank you so much! It works!
About the video, what I meant is that I am looking for a tutorial on how to build a GUI without using GUIDE (the way we are doing it here). Unfortunately most information online is about GUIDE and GUIDE's output. For example, I don't understand why this works:
bgColor = get(source,'BackgroundColor');
if all(bgColor)
bgColor = [1 1 1];
dips white
end
but this doesn't work:
if bgColor = [1 1 1]
dips white
end
I have also difficulties understanding the concept of handles and callback function. Is a handle a predetermined value? I thought it was. But if I write
{@my_funct,h1}
am I assigning the handle h1?
These are the kind of questions I have, that's why I would like to read a good tutorial. I haven't found one yet.
Glad it works, Serena! There are some tutorials found at http://www.mathworks.com/academia/student_center/tutorials/mltutorial_launchpad.html?s_cid=learn_tut which may prove helpful. I think just coming up with problems that you want to solve with MATLAB and using this forum and others (i.e. StackOverflow) are good places to get some useful ideas of what you can do with this tool.
For your examples,
if bgColor = [1 1 1]
dips white
end
remember that = is assignment and == is identical, so I think that you would have to change your above check to
if all(bgColor == [1 1 1])
dips white
end
and we use the all because we want a true (1) or false (0) answer for our condition. If we just look at
bgColor == [1 1 1]
then the answer would be an array of three elements of zeros and ones as we are comparing each element in bgColor with its corresponding element in [1 1 1].
As for the handle, all handle values are unique as they correspond to specific controls, graphics objects, etc. For the
{@my_funct,h1}
this was taken from your example of assigning the callback my_funct to your control. You are not assigning a handle in this case, but passing the handle h1 as an additional input to the my_funct function.
Sometimes using the MATLAB debugger is good step to see what is happening with your code. Put a breakpoint at the beginning of the file and run your script or function. Start stepping through the code to see what happens.
Hi Geoff,
Again, thanks a lot for your feedback. I have just another question about the final loop in the callback function.
So I have built the function:
function [] = my_funct(source, callbackdata, rIdx,cIdx)
L = [h1 h2 h3 h4 h5; h6 h7 h8 h9 h10;h11 h12 h13 h14 h15;...
h16 h17 h18 h19 h20; h21 h22 h23 h24 h25];
% disp(rIdx) correctly displayed
% disp(cIdx) correctly displayed
% disp(A) correctly displayed
bgColor = get(source,'BackgroundColor');
if all(bgColor)
bgColor = [1 1 1];
% disp(cIdx) correctly displayed
if (1 < cIdx)
set(L(rIdx,cIdx-1), 'visible', 'on')
elseif (1 < rIdx)
set(L(rIdx-1,cIdx), 'visible', 'on')
elseif (rIdx < length(A))
set(L(rIdx+1,cIdx), 'visible', 'on')
elseif (cIdx < length(A))
set(L(rIdx,cIdx+1), 'visible', 'on')
end
end
end
Strangely, it only works for the pushbutton in the position L(6,1) (that becomes visible when I click on the pushbutton L(1,1), but not for the other ones.
Serena - you will need more code as the above will only set the visibility for at most one neighbouring push button. As there can be at most eight (depending upon where the current button is located), then you will have to iterate over each one (if I correctly understand what happens when the user presses a push button) and set as visible if needed.

Sign in to comment.

More Answers (0)

Community Treasure Hunt

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

Start Hunting!