python猜数字1001untitled_如何利用Matlab GUI制作猜数字游戏

第二堂课布置了一个Mission Impossible:如何利用Matlab GUI制作猜数字游戏

这个任务对很多人来说会非常困难,首先要面对的是 Matlab GUI的机制,这是完全未知的机制,在面对未知的时候,人自然都会产生一种恐惧感!学生通常都会产生“好难啊,老师没有教过 ... ...” 这样的本能反应。在现行的教育体制下,学生已经习惯了完成老师布置的已知边界内的任务,面对未知往往素手无策甚至很容易产生恐惧。希望能够通过这个系列的教程治愈这种畏惧未知的病,提升学生面对未知时的效能感!不会,没有教过,有什么关系,你那么聪明,那么优秀,快速学会它不就行了。

步骤一:用"Matlab"和“GUI”作为关键词在百度进行搜索,所得如下:

(1)从一个叫做“matlab gui是什么_百度知道”里边获取的信息:

G=graphic图形 U=user用户 I=interface界面

打个比方,GUI就是windows,没有GUI就是DOS。

Matlab是利用命令和脚本来进行交互的,所以,理论上只是采用命令窗口的输入和输出机制,也是可以进行猜数字游戏的编程的,不用涉及图形界面操作;但是,当前的任务要求利用图形界面进行猜数字游戏的编程,就需要了解Matlab GUI机制。

(2)如何开启GUI机制就成了接下来的关键:

技术有一个特点,你会的话,就没有难度;你不会的话,哪怕是一层窗户纸,真的就是一层窗户纸,你也会被蒙蔽双眼,不知道如何下手。找到度娘的第一个有关的信息就是“matlab_gui初学者教程”里边有提到

开启GUI机制的密码是 Guide

这个新建GUI界面的对话框有两个panel,第一个是"Create New GUI",第二个是"Open existing GUI",我们现在的工作目录什么都没有,自然是新建一个GUI。

可以新建一个简单的blank(空)的界面,然后往这个界面里边放置各种控件。

一个Gui程序,由两个部分组成。界面是界面,是表层,通过.fig文件进行控制;代码是代码,是底层,通过与.fig相同文件名的.m文件进行控制。

步骤一到这里就完成了,目的是初步了解Matlab GUI机制,它有表层的界面,也有底层的代码。接下来我们要通过步骤二更深入地理解Matlab GUI机制。

步骤二:如何利用GUI机制载入一幅简单的图片?

我们以魔方图片为例子,看看如何载入魔方图片。用photoshop切割并适当加工成800*600的.jpg格式的图片,命名为MagicMatrix_800600.jpg。

保存在当前工作目录E:\Courses\MatlabLearningCourse\MissionImpossible2_GuessNumber。

在保存GUI文件的一刹那,Matlab会自动生成两个文件,一个是.fig文件,对应guide开发界面的样子;另外一个是.m文件,是和.fig文件平行的代码控制文件。

GUI界面的样子:

代码控制.m文件里边的样子:

这时候是最具恐惧感的时刻,面对未知的恐惧,弥漫到整个全身,大部分人在这个时候都会有一种寸步难行举步维艰的感觉!我也曾经有过这种感觉,面对复杂的GUI机制有一种无力感,觉得自己无论如何都是搞不定GUI的。

* 为什么在你学习一门新的语言的时候需要一个老师,可能他的作用就是消除这层恐惧,让你能够大踏步地向前,直到“胜利”的彼岸。然而,老师的存在也会起到副作用,就是学生在面对已知的时候,得心应手;而在面对未知的时候,寸步难行!教育的终极目的是打造可持续发展的个体,也就是面对未知不会产生恐惧的个体,这些人才是真正的托起这个民族复兴梦想的人啊!

怀揣着梦想,我们把GUI .m文件里边的代码拷出来看看:

function varargout = LoadPic(varargin)

% LOADPIC MATLAB code for LoadPic.fig

%      LOADPIC, by itself, creates a new LOADPIC or raises the existing

%      singleton*.

%

%      H = LOADPIC returns the handle to a new LOADPIC or the handle to

%      the existing singleton*.

%

%      LOADPIC('CALLBACK',hObject,eventData,handles,...) calls the local

%      function named CALLBACK in LOADPIC.M with the given input arguments.

%

%      LOADPIC('Property','Value',...) creates a new LOADPIC or raises the

%      existing singleton*.  Starting from the left, property value pairs are

%      applied to the GUI before LoadPic_OpeningFcn gets called.  An

%      unrecognized property name or invalid value makes property application

%      stop.  All inputs are passed to LoadPic_OpeningFcn via varargin.

%

%      *See GUI Options on GUIDE's Tools menu.  Choose "GUI allows only one

%      instance to run (singleton)".

%

% See also: GUIDE, GUIDATA, GUIHANDLES

% Edit the above text to modify the response to help LoadPic

% Last Modified by GUIDE v2.5 22-Sep-2015 12:03:43

% Begin initialization code - DO NOT EDIT

gui_Singleton = 1;

gui_State = struct('gui_Name',       mfilename, ...

'gui_Singleton',  gui_Singleton, ...

'gui_OpeningFcn', @LoadPic_OpeningFcn, ...

'gui_OutputFcn',  @LoadPic_OutputFcn, ...

'gui_LayoutFcn',  [] , ...

'gui_Callback',   []);

if nargin && ischar(varargin{1})

gui_State.gui_Callback = str2func(varargin{1});

end

if nargout

[varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});

else

gui_mainfcn(gui_State, varargin{:});

end

% End initialization code - DO NOT EDIT

% --- Executes just before LoadPic is made visible.

function LoadPic_OpeningFcn(hObject, eventdata, handles, varargin)

% This function has no output args, see OutputFcn.

% hObject    handle to figure

% eventdata  reserved - to be defined in a future version of MATLAB

% handles    structure with handles and user data (see GUIDATA)

% varargin   command line arguments to LoadPic (see VARARGIN)

% Choose default command line output for LoadPic

handles.output = hObject;

% Update handles structure

guidata(hObject, handles);

% UIWAIT makes LoadPic wait for user response (see UIRESUME)

% uiwait(handles.figure1);

% --- Outputs from this function are returned to the command line.

function varargout = LoadPic_OutputFcn(hObject, eventdata, handles)

% varargout  cell array for returning output args (see VARARGOUT);

% hObject    handle to figure

% eventdata  reserved - to be defined in a future version of MATLAB

% handles    structure with handles and user data (see GUIDATA)

% Get default command line output from handles structure

varargout{1} = handles.output;

一开始,真的是无从下手啊,这都是些什么呀!!但是,只要你不因为害怕或者恐惧而拒绝去摸索它,仔细观察这些代码你会发现:虽然从外围看不懂代码的具体含义,但是,还是可以大致了解到这些代码都是由 "function"领衔的,GUI是一个“函数”分类机制?这是一种感觉,这种感觉已经非常接近Matlab GUI机制的本质。Matlab是脚本语言,.m文件是由一组命令组成的,运行某个.m文件相当于逐行执行.m文件里边的命令。而Matlab GUI则遵循的是“分发”的规则,所有的函数都处于待命的状态,一旦你有所动作,比如点击了一个按钮,它就会触发这个动作背后对应的函数实现某种功能。这两种机制分别对应两种程序的思维模式,前一种是过程性的,后一种则是事件驱动的(学习过VB的,会对事件驱动不陌生,没错,VB就是典型的事件驱动的编程)。

问题就变成了,我们如果想做点什么,比如要载入图片,要从哪里开始切入,代码要写在哪个模块呢?要是理解了Matlab GUI机制,我们只要以某个事件去驱动它就可以了。

这里一开始,在什么都没有添加的情况下,总共有这么3个模块(那些注释,都是障眼法而已):

function varargout = LoadPic(varargin)

function LoadPic_OpeningFcn(hObject, eventdata, handles, varargin)

function varargout = LoadPic_OutputFcn(hObject, eventdata, handles)

通过百度搜索,我们了解到,添加任意一个控件,比如按钮或者编辑框,GUI的代码层也会增加函数,比如添加一个编辑框控件,它自动命名成edit1,那么就会在代码中出现一个新的函数:

编辑框控件在工具界面里边:

左边这列的第3个。

先往gui界面上添加一个editbox,

这时候.m文件里边的内容还没有任何改变,点击“保存”之后,这个时候.m文件就增加了一个新的Callback函数:

% --- Executes during object creation, after setting all properties.

function edit1_CreateFcn(hObject, eventdata, handles)

% hObject    handle to edit1 (see GCBO)

% eventdata  reserved - to be defined in a future version of MATLAB

% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.

%       See ISPC and COMPUTER.

if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))

set(hObject,'BackgroundColor','white');

end

* 事件驱动基本上就是这么一种特点,某个事件对应某个功能函数,事件触发函数/功能。

如果是这样,我们是不是可以尝试添加一个控件,比如按钮控件,点击这个按钮,在这个按钮对应的函数里边实现载入图片的功能。

这时候,我们要重新借助度娘,找到按钮控件对应的实现载入图片的代码,比如我们找到这样的代码:

% --- Executes on button press in pushbutton1.

function pushbutton1_Callback(hObject, eventdata, handles)

% hObject    handle to pushbutton1 (see GCBO)

% eventdata  reserved - to be defined in a future version of MATLAB

% handles    structure with handles and user data (see GUIDATA)

[filename,pathname] = uigetfile({'*.*';'*.jpg';'*.bmp';'*.gif';'*.png';'*.tif'},'Read Pic');

str = [pathname filename];

global src_img;

if ~isequal([pathname,filename],[0,0])

src_img = imread(str);

axes(handles.axes1);

imshow(src_img);

end

这段代码可以简单解读为两个部分,第一部分是如何选取图片文件;第二部分是读取该图片文件并呈现的部分。

[filename,pathname] = uigetfile({'*.*';'*.jpg';'*.bmp';'*.gif';'*.png';'*.tif'},'Read Pic');

这里用了一个 uigetfile的机制,在matlab的命令窗口中,输入这段代码,回车,这段代码会调用一个对话框:

当然,也可以通过在命令窗口里边输入 "help uigetfile" 了解uigetfile函数的机制:

uigetfile Standard open file dialog box.

[FILENAME, PATHNAME, FILTERINDEX] = uigetfile(FILTERSPEC, TITLE)

displays a dialog box for the user to fill in, and returns the filename

and path strings and the index of the selected filter. A successful

return occurs only if the file exists.  If the user  selects a file

that does not exist, an error message is displayed,  and control

returns to the dialog box. The user may then enter  another filename,

or press the Cancel button.

The FILTERSPEC parameter determines the initial display of files in

the dialog box.  For example '*.m' lists all MATLAB code files.  If

FILTERSPEC is a cell array, the first column is used as the list of

extensions, and the second column is used as the list of descriptions.

When FILTERSPEC is a string or a cell array, "All files" is appended

to the list.

When FILTERSPEC is empty the default list of file types is used.

Parameter TITLE is a string containing the title of the dialog box.

The output variable FILENAME is a string containing the name of the

file selected in the dialog box.  If the user presses Cancel, it is set

to 0.

The output variable PATHNAME is a string containing the path of the

file selected in the dialog box.  If the user presses Cancel, it is set

to 0.

The output variable FILTERINDEX returns the index of the filter

selected in the dialog box. The indexing starts at 1. If the user

presses Cancel, it is set to 0.

[FILENAME, PATHNAME, FILTERINDEX] = uigetfile(FILTERSPEC, TITLE, FILE)

FILE is a string containing the name to use as the default selection.

[FILENAME, PATHNAME] = uigetfile(..., 'MultiSelect', SELECTMODE)

specifies if multiple file selection is enabled for the uigetfile

dialog. Valid values for SELECTMODE are 'on' and 'off'. If the value of

'MultiSelect' is set to 'on', the dialog box supports multiple file

selection. 'MultiSelect' is set to 'off' by default.

The output variable FILENAME is a cell array of strings if multiple

filenames are selected. Otherwise, it is a string representing

the selected filename.

[FILENAME, PATHNAME] = uigetfile(..., 'Location', [X Y]) places the

dialog box at screen position [X,Y] in pixel units. This option is

supported on UNIX platforms only.

NOTE: THIS SYNTAX IS OBSOLETE AND WILL BE IGNORED

[FILENAME, PATHNAME] = uigetfile(..., X, Y) places the dialog box at

screen position [X,Y] in pixel units. This option is supported on UNIX

platforms only.

NOTE: THIS SYNTAX IS OBSOLETE AND WILL BE IGNORED.

Examples:

[filename, pathname, filterindex] = uigetfile('*.m', 'Pick a MATLAB code file');

[filename, pathname, filterindex] = uigetfile( ...

{'*.m;*.fig;*.mat;*.mdl', 'All MATLAB Files (*.m, *.fig, *.mat, *.mdl)';

'*.m',  'MATLAB Code (*.m)'; ...

'*.fig','Figures (*.fig)'; ...

'*.mat','MAT-files (*.mat)'; ...

'*.mdl','Models (*.mdl)'; ...

'*.*',  'All Files (*.*)'}, ...

'Pick a file');

[filename, pathname, filterindex] = uigetfile( ...

{'*.mat','MAT-files (*.mat)'; ...

'*.mdl','Models (*.mdl)'; ...

'*.*',  'All Files (*.*)'}, ...

'Pick a file', 'Untitled.mat');

Note, multiple extensions with no descriptions must be separated by semi-

colons.

[filename, pathname] = uigetfile( ...

{'*.m';'*.mdl';'*.mat';'*.*'}, ...

'Pick a file');

Associating multiple extensions with one description:

[filename, pathname] = uigetfile( ...

{'*.m;*.fig;*.mat;*.mdl', 'All MATLAB Files (*.m, *.fig, *.mat, *.mdl)'; ...

'*.*',                   'All Files (*.*)'}, ...

'Pick a file');

Enabling multiple file selection in the dialog:

[filename, pathname, filterindex] = uigetfile( ...

{'*.mat','MAT-files (*.mat)'; ...

'*.mdl','Models (*.mdl)'; ...

'*.*',  'All Files (*.*)'}, ...

'Pick a file', ...

'MultiSelect', 'on');

This code checks if the user pressed cancel on the dialog.

[filename, pathname] = uigetfile('*.m', 'Pick a MATLAB code file');

if isequal(filename,0) || isequal(pathname,0)

disp('User pressed cancel')

else

disp(['User selected ', fullfile(pathname, filename)])

end

See also uigetdir, uiputfile.

Reference page in Help browser

doc uigetfile

一般这样多的说明,通过例子就可以大致了解这个函数的功能而不必逐行都看(从一大堆信息中提取有效信息是一种能力,我们真正的目的在于解决问题完成任务),uigetfile就是利用一个对话框选取一个文件,这个函数的返回值有两个 [filename, pathname],这里的filename就是文件名,这里的pathname是路径名。

我们可以通过将两个变量组合,变成一个全路径,也就是指向我们选取的图片文件的地址。

str = [pathname filename];

这段代码等同于:

str = srpintf('%s%s', pathname, filename);

我们可以在GUI界面里边添加一个按钮控件,试试这段代码的效果,根据个人习惯,代码部分要稍微做一点点调整:

[FileName,FilePath] = uigetfile({'*.*';'*.jpg';'*.bmp';'*.gif';'*.png';'*.tif'},'Read Pic');

PathName = sprintf('%s%s', FilePath, FileName);

为了保险起见,我们需要看看最后选取完成,文件的全路径是不是对的,需要借助fprintf函数把这个PathName字符串内容打印出来:

fprintf('%s', PathName)

点击Matlab Guide界面,或者直接运行 .m文件,它们的效果是一样的,就会弹出界面:

..

然后点击pushbutton1按钮,会弹出一个文件选取窗口:

..

选取其中一个文件:

..

这时候检查matlab的命令窗口,就会看到

E:\Courses\MatlabLearningCourse\MissionImpossible2_GuessNumber\MagicMatrix_800600.jpg>>

说明我们的代码是写对了。这里的显示有一点违和感,那是因为fprintf('%s', PathName)代码的问题,如果能够把代码改成fprintf('%s\n', PathName)添加一个换行'\n'运行的结果就更和谐了E:\Courses\MatlabLearningCourse\MissionImpossible2_GuessNumber\MagicMatrix_800600.jpg

>>

第二部分的代码的意思是读取图片文件并呈现(在Matlab GUI里,图片通常需要借助axes坐标轴控件显示出来),

src_img = imread(str);

axes(handles.axes1);

imshow(src_img);这三句话的意思分别是:将str指向的图片文件用imread函数读取出来,然后赋值给src_img;然后我们要激活axes1控件;最后是在这个激活的axes1控件里把src_img变量的内容显示出来。

步骤二能让我们更进一步地了解GUI机制,以一个按钮事件触发一个它对应的函数,在这个函数中添加代码来实现载入图片的功能。

步骤三:猜数字游戏的界面设计?

界面与我们要实现的功能有关,猜数字游戏至少需要一个输入用的文本编辑框控件,一个确定按钮控件,和一个用来显示反馈信息用的text控件。我们从百度上先找一个参照的界面过来,比如下边这个界面:

(a)我们需要一个文本控件,用来提示“请输入一个数字:”;

(b)一个编辑框控件,用来实际输入数字;

(c)一个“提交“按钮,提交这个数字,通过从编辑框控件获取字符串并转化为数字,和之前随机产生的数字进行一次比对,然后根据比对的结果来决定显示”大了“,或”小了“或”你猜对了“诸如此类的反馈;

(d)为了增加游戏性,猜数字游戏还可以有排行榜,为了满足这个条件,就需要一开始设置用户的id作为区分,它会有一个与数据库的交互的过程;

(e)除了根据用户的猜数字的次数,还可以增加倒计时功能(Timer),让游戏变得更加刺激;

(f)需要设置一个”重新开始游戏“的按钮重置游戏。

接下来,我们就逐一地在一个GUI程序中实现这些功能。将’E:\Courses\MatlabLearningCourse\MissionImpossible2_GuessNumber‘路径拷贝到Matlab的当前工作目录文本编辑框中,敲回车,正式启动这个猜数字游戏编程项目。

在Matlab的命令窗口输入 guide,新建一个空的gui

下面进入界面的布局阶段:

(a) 先加一个Static Text文本控件,

双击这个控件,或者右键点击控件选择Proper Inspector,会弹出一个属性设置对话框

默认的属性是按照字母顺序排列的,找到里边的String属性,把 'Static Text' 改成 ’请输入一个数字:‘

(b)增加一个文本编辑框,可以考虑把它的String属性里边的内容去掉

(c)加入提交按钮,把它的String属性里边的内容改成'提交'

按这个提交的按钮要完成的第一步工作是从编辑框控件中获取字符串并转化为数字

每一个控件都有一个自己的tag,我们正是通过这个tag来操作控件里边的内容,文本编辑框的tag里显示的是edit1,我们可以通过一个get函数来获取这个edit1里边的内容,具体代码如下:

这里我要停下来补充个重要的内容,关键词是'句柄'

GUI中的所有控件使用同一个handles结构体,handles结构体中保存了图形窗口中所有对象的句柄,可以使用handles获取或设置某个对象的属性。我从网上找了这个句柄图形系统的结构图,方便大家理解:

如果我们想要获取或者改变某个控件的属性,比如,我们想知道用户在文本编辑框中输入的内容,我们可以通过如下代码来实现:number_Input = get(handles.edit1, 'String')

按这个提交的按钮要完成的第二步工作是将这个数字和事先随机生成的数字进行比较

这里又涉及到另外一个重要的内容,关键词是'数据传递'

别忘记了,Matlab GUI既然用的是函数的分发机制,函数的特点就是转瞬即逝的,它所有的中间过程随着函数运行结束都会消失,包括那个number_Input 变量,虽然在运行的过程中获取了编辑文本框中的内容,等这个按钮的回调函数pushbutton1_Callback结束了,这个变量也就被清空了。所以,要想办法把这个有用的数据保留下来,在函数消失之前传递到函数的外部。方法有多种,我们一般习惯借用handles这个结构体,比如,number_Input = get(handles.edit1, 'String'),这个语句我们可以改成 handles.number_Input = get(handles.edit1, 'String'),这个时候我们就可以在其他函数里边用handles.number_Input 这个变量了。

不对不对不对!!!

到这个时候,这个变量仍然没有被传递到函数外边,数据要从函数内部传递到handles句柄,完成这个过程还需要借助一个数据交换的机制,就是这句话:

guidata(hObject, handles);

有了这个数据交换机制,我们就可以把handles.number_Input 变量和之前生成的随机数变量进行比较。

这里还要涉及到一个重要的内容,关键词是'初始化',一个程序一开始都会运行 XXX_OpeningFcn 函数,如果需要事先确定一些属性和参数,可以把代码写入到OpeningFcn 函数模块里边。

随机生成一个 1~9的数字的代码如下:

tmpRandArr = randperm(9);

handles.randomNumber = tmpRandArr(1);

注意这段代码添加的位置!

提交按钮的回调函数里边的代码最后是这样的:

handles.number_Input = str2num(cell2mat(get(handles.edit1, 'String')));

if handles.number_Input == handles.randomNumber

set(handles.result,'string','你猜对了!!');

else

if handles.number_Input > handles.randomNumber

set(handles.result,'string','大了.');

else

set(handles.result,'string','小了.');

end

end

为了保证这个代码能够正常运行,是需要在整个游戏界面的最下边再添加一个新的文本控件 Static Text,并且把它的tag改成result。我们尝试在初始化OpeningFcn函数把标记为'result'的文本控件的初始'String'属性的内容改成'程序随机生成一个1~9数字,请猜是哪个数?'

到这里为止,一个简易版的猜数字游戏就写好了。

------------- +++++++++++ --------------------

Matlab GUI 既然是一种基于图形用户界面的编程,它已经对用户体验是有要求的。

大部分用户可能会按照指导语,输入1-9的数字,可是总有那么几个用户不走寻常路,输入个abc,各种奇葩的符号,甚至不输入任何内容就点提交。遇到这样的特殊用户,我们应该怎么办?这就对编程提出了更高的要求,就是从一开始,我们就要把各种异常考虑进来。

我们先考虑第一种异常情况,就是用户不输入数字,输入的是字符,我们要提醒用户'请输入1~9的数字'。

这就涉及到,一开始,我们要判断用户输入 edit1这个控件的内容到底是不是数字,还是说是字符串?

那就要求我们对输入的内容进行判断,怎么对一个字符串内容进行判断,看它是字符还是数字。

这部分代码如下:

inputContent = get(handles.edit1, 'String');

handles.number_Input = str2num(inputContent)

if length(handles.number_Input) == 0

set(handles.result,'string','请输入数字!');

else

if   handles.number_Input == fix(handles.number_Input)

if handles.number_Input > 0 && handles.number_Input < 10

if handles.number_Input == handles.randomNumber

set(handles.result,'string','你猜对了!!');

else

if handles.number_Input > handles.randomNumber

set(handles.result,'string','大了!');

else

set(handles.result,'string','小了!');

end

end

else set(handles.result,'string','请输入1~9的整数!');

end

else

set(handles.result,'string','请输入整数!');

end

end

倒计时功能需要用到Timer机制,我们会在另外一个教程里边介绍。

[本帖最后由 psybestwish 于 2016-3-15 17:07 编辑]

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值