Making a GUI, 02

advertisement
MATLAB
Minesweeper
Development of a MATLAB GUI
Start with an empty function
function MineSweeper_GUI()
end % of MineSweeper()
While not strictly required, a function that creates the GUI
provides flexibility for your program. If you have multiple
frames in your interface, functions permit separation of code
by allowing a main program to instantiate the frames
independently.
Create a basic figure
function MineSweeper_GUI()
fh = figure('Position', [400, 400, 250, 300]);
% Adjust appearance
set(fh, 'Resize', 'off');
set(fh, 'MenuBar', 'none');
set(fh, 'NumberTitle', 'off');
set(fh, 'Name', 'MATLAB MineSweeper');
end % of MineSweeper_GUI()
These are common attributes, although not every GUI will
necessarily set them.
Provide the controls
% Text will be provided and covered up by a button.
% will disappear when clicked.
The button
% Control to hold text (to display after button is pushed)
tbh = uicontrol('Units', 'normalized', 'Style', 'text');
set(tbh, 'String', '8', 'FontSize', 16);
set(tbh, 'Position', [0, 0, 0.1, 0.1]);
Now that we have one box, let’s
make the code repeat it.
Provide the rest of textboxes
c = 1;
for xpos = 1:9
for ypos = 1:9
% Control to hold text (to display after button is pushed)
tb(c) = uicontrol('Style', 'text', 'Units', 'normalized');
set(tb(c), 'String', '8', 'FontSize', 16);
set(tb(c), 'Position', [0.11*(xpos-1),0.11*(ypos-1),0.1,0.1]);
c = c+1;
end % of for ypos
end % of for xpos
(We assume a 9x9 grid – this can become a
feature selected by the user) These all
show the number ‘8’. We will change that
after we have a visually complete GUI –
then we can make it fully functional.
Provide pushbuttons to cover
% Cover with buttons
c = 1;
for xpos = 1:9
for ypos = 1:9
% These are drawn on the figure, not any panel
u(c)=uicontrol('Style', 'pushbutton', 'Units', 'normalized');
set(u(c), 'Position', [0.11*(xpos-1),0.11*(ypos-1), 0.1, 0.1]);
c = c + 1;
end
end
We now have a visually-complete game.
Now we can focus on the functionality.
Add button functionality
When actions are to be performed as a result of
clicking on a control, MATLAB places a call to a
“callback function”. It is the responsibility of
the programmer to provide that function, and
to tell MATLAB how to call that function.
The syntax for this is a little different from
typical function usage.
Defining a callback function
To define a callback function, simply make a
function in the same file as the GUI frame. The
name can be any legal function name. The first
two parameters of the function are required by
MATLAB – you can add any others you wish
afterwards:
function my_callback(H, E, a, b, c)
.
.
.
end % of my_callback()
Our callback function
% Callback (cb) for PushButtons (pb)
function cb_pb(H, E, bh, th, c, sz)
% What string is "under" the button that was pressed?
s = get(th(c), 'String');
if strcmp(s, '9')
hit_mine(c, sz, bh, th);
elseif length(s)==0 || strcmp(s, ' ')
hit_empty(c, sz, bh, th);
else
delete(bh(c));
uicontrol(th(c)); % set focus to current text
end % of if
end % of cb_pb
Tell MATLAB to use the
callback function
%
%
%
%
%
%
Because clicking a button can mean deleting other buttons in this
program, we want to send the entire array of button handles and text
handles to the callback functions. That is why this loop is AFTER
the original creation loop - if we had tried to set the callbacks in
the creation loop, the early buttons would know nothing about buttons
defined later.
for c = 1:n^2
% Handle a left-click
set(u(c), 'callback', {@cb_pb, u, th, cnt, n});
end % of for c
function MineSweeper_demo()
clc;
n=9;
fh = figure('Position', [400, 400, 250, 300]);
% Adjust appearance
set(fh, 'Resize', 'off');
set(fh, 'MenuBar', 'none');
set(fh, 'NumberTitle', 'off');
set(fh, 'Name', 'MATLAB MineSweeper');
c = 1;
for xpos = 1:9
for ypos = 1:9
% Control to hold text (to display after button is pushed)
tb(c) = uicontrol('Style', 'text', 'Units', 'normalized');
set(tb(c), 'String', '8', 'FontSize', 16);
set(tb(c), 'Position', [0.11*(xpos-1),0.11*(ypos-1),0.1,0.1]);
c = c+1;
end % of for ypos
end % of for xpos
% Cover with buttons
c = 1;
for xpos = 1:9
for ypos = 1:9
% These are drawn on the figure, not any panel
u(c)=uicontrol('Style', 'pushbutton', 'Units', 'normalized');
set(u(c), 'Position', [0.11*(xpos-1),0.11*(ypos-1), 0.1, 0.1]);
c = c + 1;
end
end
% Attach callback function
for c = 1:n^2
% Handle a left-click
set(u(c), 'callback', {@cb_pb, u, tb, c, n});
end % of for c
end % of MineSweeper_demo()
% Callback function
function cb_pb(H, E, bh, th, c, sz)
% What string is "under" the button?
s = get(th(c), 'String');
if strcmp(s, '9')
hit_mine(c, sz, bh, th);
elseif length(s)==0 || strcmp(s, ' ')
hit_empty(c, sz, bh, th);
else
delete(bh(c));
uicontrol(th(c)); % set focus to current text
end
end % of cb_pb()
Finishing off the code
At this point, most of the GUI code is complete. We
now provide code that will perform the tasks
desired upon certain events.
If a mine is beneath a clicked button:
hit_mine(c, sz, bh, th);
If a space is beneath a clicked button:
hit_empty(c, sz, bh, th);
hit_mine()
% What to do if a mine is hit
function hit_mine(i, sz, bh, th)
% Get rid of ALL buttons and show the mines
for r=1:sz
for c=1:sz
% Since using vectors to hold handles
v = (c-1)*sz + r;
% Is there still a button here?
if ishandle(bh(v))
% What is the textbox string?
ts = get(th(v), 'String');
% Change textbox to show an image of a mine exploding
if strcmp(ts, '9')
mineimg = imread('mine10.jpg');
set(bh(v), 'cdata', mineimg);
else
% Get rid of the button
delete(bh(v));
end
end
end % of for c
end % of for r
Add a grid for testing
hit_mine()
function MineSweeper_demo()
% ----------------------------% Testing values
n = 9;
for i=1:n^2
M(i) = num2str(randi(9,1));
r = randi(2)-1;
if r
M(i) = ' ';
end
end
% -----------------------------And we will change this line:
set(tb(c), 'String', '8', 'FontSize', 16);
To display the testing values:
set(tb(c), 'String', num2str(M(c)), 'FontSize', 16);
After adding the test values
With the mine images
hit_empty()
function hit_empty(pos, sz, bh, th)
% Find all of the surrounding empties and remove the buttons
recursive_remove(pos, pos, sz, bh, th, 1);
end % of hit_empty()
(see next slide)
recursive_remove()
% Remove all buttons over empty strings and over numbers that touch one of
% those empty strings (but don't remove over mines)
function recursive_remove(from, to, sz, bh, th, level)
if ~(to<=0 || to>sz^2 ||(mod(from, sz)==1 && mod(to, sz)==0) ||(mod(from, sz)==0 && mod(to, sz)==1)
to_s = get(th(to), 'String');
from_s = get(th(from), 'String');
% Empty or contains a number (but not a mine)
if strcmp(to_s, ' ') || (~strcmp(to_s, ' ') && str2num(to_s)>0 && str2num(to_s)<9)
% Does the button still exist?
if ishandle(bh(to))
% Remove the button
delete(bh(to));
% If it's empty, recurse to check the surrounding buttons
if strcmp(to_s, ' ')
level = level + 1;
recursive_remove(to,
recursive_remove(to,
recursive_remove(to,
recursive_remove(to,
recursive_remove(to,
recursive_remove(to,
recursive_remove(to,
recursive_remove(to,
level = level - 1;
end
end % of if
end % of else
end % of recursive_remove()
to-sz-1, sz, bh, th, level);
to-sz, sz, bh, th, level);
to-sz+1, sz, bh, th, level);
to-1, sz, bh, th, level);
to+1, sz, bh, th, level);
to+sz-1, sz, bh, th, level);
to+sz, sz, bh, th, level);
to+sz+1, sz, bh, th, level);
Last Details
One other property of the game
(other than scoring) involves
allowing the user to mark a
location as “containing a mine”
or “might contain a mine”.
We will do this by allowing the
user to right-click on a button
and placing “?” for “might be a
mine”, or “!” for “containing a
mine”.
Right-click Callbacks
A separate callback function must be placed for
handling “alternative clicks”. Note the added
line:
for c = 1:n^2
% Handle a left-click
set(u(c), 'callback', {@cb_pb, u, tb, c, n});
% Handle a right-click
set(u(cnt), 'ButtonDownFcn', {@right_click, c, u});
end % of for c
right_click()
% This function is executed when a button is right-clicked
function right_click(h, e, pos, bh)
% The button we pressed is in a figure.
% click that was made:
fh = ancestor(h, 'figure')
clickType = get(fh, 'SelectionType')
Only the figure knows the type of
% 'Alt' is a right-click
%
% This will allow using the right-click to cycle through the options
bs = get(h, 'String');
if strcmp(clickType, 'alt')
if strcmp(bs, '!')
% Option 1: Clear the button label
set(bh(pos), 'String', '');
end
if strcmp(bs, '')
% Option 2: Put a query (?) on the button - user isn't sure
set(bh(pos), 'String', '?');
set(bh(pos), 'FontSize', 16, 'ForegroundColor', [1, 0, 0]);
end
if strcmp(bs, '?')
% Option 3 : Put a bang (!) on the button - user thinks this is a mine
set(bh(pos), 'String', '!');
set(bh(pos), 'FontSize', 16, 'ForegroundColor', [1, 0, 0]);
end
end % of if
end % of right_click()
Source Code
Function:
http://kindy.egr115.com/MineSweeper.zip
Download