第一篇的基础上,本篇介绍选项卡组TabGroup、面板Panel、按钮Button、表UITable及其部分属性。

打开第一篇中的app_01可视化对象。

新建对象

新建选项卡组TabGroup

从左侧组件库中选择选项卡组并拖拽到设计视图区域,将选项卡组的大小调整到与appUIFigure一样大(手动拉即可,也可以设置Position实现)。在组件资源区会出现app.TabGroup在app.appUIFigure下面。

我们将app.TabGroup的第一个Tab改为Tab_projects(组件资源目录中双击修改),选中第一个Tab,将其Title属性改为 项目管理。第二个Tab改为Tab_datamanage,将其Title属性改为数据管理。改好后的效果如图。

 

 新建面板Panel

选中Tab_projects,将其置为当前tab,从左侧选择面板并拉入设计视图中,放在Tab_projects的最上部,我们把它作为设置区域,后续放置按钮等组件(类似于office中的Ribbon)。将其大小调整到下图所示大小。将其名称改为app.Panel_projects,删除Title属性的内容回车(Title是一种提示标签,方便我们识别panel中的对象,对他们可以取一个有共性的名字,我们这里不用,所以删除)。

新建 按钮Button

从左侧拉去按钮到app.Panel_projects左侧,将其改为app.Button_newprohject,将其Text属性改为  新建项目。在Icon属性处,为按钮选择一个图标,并将其IconAlignment 属性设置为top(将图标放在上方)。并将其Position 属性设置为 [16 23 60 60],即大小为60×60。效果如下。

右键点击图标,添加回调函数 Button_newprohjectPushed。回调函数位于methods (Access = private)下。目前函数内没有任何代码。

methods (Access = private)

        % Button pushed function: Button_newprohject
        function Button_newprohjectPushed(app, event)
            
        end
    end

 点击上方的编辑器控制页面,选择属性下方的下拉箭头,选择私有属性。系统会生成一个 properties (Access = private)...end区域,这个区域用于存放我们的app_01的属性(即对象)。在下面新建一个属性(对象)projects_info并将其初始化为空数组。

matlab中使用%来注释,注释后的文字不作为代码,是对代码的说明,便于后续阅读,建议大家多多注释。编辑器中的代码模块可以对代码进行注释,缩进管理。

properties (Access = private)
    projects_info=[]; % 存储项目信息
end

在 Button_newprohjectPushed函数下增加以下代码。该回调函数是通过matlab的编程方式建立对话框和组件,一共包括三个部分,以下分开讲解。

第一部分

效果图和源代码如下:

selection = uiconfirm(app.UIFigure,"新建项目前请确认保存的现有的项目,是否开始新建项目?","确认新建项目","Icon","warning");
    if isequal('Cancel',selection)
         return
    end

通过新建确认对话框向用户确认是否新建,该处设置是否新建的原因是后续使用过程中会有数据覆盖问题,所以要请用户确认。

使用了matlab的专用dlg(对话框)中的一种:uiconfirm(确认对话框)。函数的matlab帮助文件说明。较为常用的方式是

selection = uiconfirm(fig,message,title, "Icon","warning");

其中

selection 是用户选择的结果,返回值是字符串,默认是'OK'、'Cancel'。用户可以自定义。

fig 是目标图窗,即父对象。

message 是显示在对话框中的提示信息,是编程时给的,格式是char(字符)或者string(字符串。

title 对话框的标题,是编程时给的,格式是char(字符)或者string(字符串。

Icon 用于设置图标,可以设置也可以不设置,值是字符串表达的图片名称(系统自带),也可以自定义给文件地址。

if isequal('Cancel',selection)用于判断用户的选择,如果选择'Cancel'(取消)则直接返回(return),不执行后续代码。

isequal函数用于判断内部的对象是否一致。此处判断选择结果selection与'Cancel'是否一致,从而做出后续的判断。 见matlab帮助

if 语句是matlab中常用判断语句,见matlab帮助

第二部分

效果图和源代码如下:

            %第二部分:通过模态对话框的方式建立新的新建对话框
            d = dialog('Position',[500 400 500 250],'Name','新建项目');
            %项目名称
            name_label = uicontrol('Parent',d,...
                'Style','text',...
                'Position',[30 210 100 20],...
                'String','项目名称');
            name_txt=uicontrol('Parent',d,...
                'Style','edit',...
                'Position',[120 210 300 20],...
                HorizontalAlignment='left');
            %项目说明
            info_label = uicontrol('Parent',d,...
                'Style','text',...
                'Position',[30 150 100 20],...
                'String','项目说明');
            info_txt=uicontrol('Parent',d,...
                'Style','edit',...
                'Position',[120 130 300 60],...
                'Min',0,'Max',1000,...
                'String','',...
                HorizontalAlignment='left');
            %保存地址
            foldernow=pwd;%获取当前文件地址
            folder_label = uicontrol('Parent',d,...
                'Style','text',...
                'Position',[30 100 100 20],...
                'String','项目地址');
            folder_txt=uicontrol('Parent',d,...
                'Style','edit',...
                'Position',[120 100 300 20],...
                'String',foldernow,...
                HorizontalAlignment='left');
            button_folder = uicontrol('Parent',d,...
                'Position',[420 97 50 25],...
                'String','选择',...
                Callback=@button_folder_Pushed);
            %确定按钮
            button_done = uicontrol('Parent',d,...
                'Position',[350 20 70 25],...
                'String','确定',...
                Callback=@button_done_Pushed);

MATLAB中的模态对话框是一种特殊的交互窗口,具有以下核心特点和功能:‌

  • 会阻止用户与其他MATLAB窗口交互,直到对话框关闭‌
  • 强制保持焦点在当前窗口,形成操作阻断效果‌
  • 典型应用包括关键操作确认、错误提示等场景‌

第二部分为以下几步

  1. 通过dialog创建对话框。
  2. 通过uicontrol创建text、edit、pushbutton等组件。

dialog是创建模态对话框的方式,其使用方式请查看帮助。比较重要的且常用的属性是Position和Name,用于约束位置和标题栏内容。如:

 d = dialog('Position',[500 400 500 250],'Name','新建项目');

uicontrol用于定义模态对话框中的组件,定义的方式是属性名称和属性值作为一组出现,有两种属性的表达方式,如:

'Style','text',  %用单引号括起来的属性及其取值
 Style='text',   %用等号表达的属性及其取值,两者的作用是一样的

常用的属性包括:

  1. Position,位置,值为数组,如 [500 400 500 250];
  2. Style,样式,即组件类型,有:'pushbutton'、'togglebutton'、'checkbox'、'radiobutton'、'edit'、'text'、'slider'、'listbox'、'popupmenu';
  3. Value ,值,可以用于设置默认值,也可以用于获取用户值;
  4. String,控件显示的文字名称;
  5. Callback回调函数;
  6. 其他属性请查看帮助

Callback=@button_folder_Pushed的作用是,定义当前pushbutton的回调函数,目前用的内联回调函数,函数button_folder_Pushed只能在上级回调函数Button_newprohjectPushed内部调用,且不用app.的前缀,输入参数也不用app。

如果将button_folder_Pushed设置为app_01的函数,则调用时要改为Callback=@app.button_folder_Pushed,相应的button_folder_Pushed应该放置到methods (Access = private)或者methods (Access = public)下面,button_folder_Pushed的输入值需要添加app,即button_folder_Pushed(app,src,event),如果此时button_folder_Pushed没有调用app的任何信息,可以省略为button_folder_Pushed(~,src,event),其中的~不可省略,否则系统识别错误。

第三部分

包括两个回调函数,button_folder_Pushed和button_done_Pushed,分别响应用户点击按钮button_folder(选择文件夹)和button_done(完成)

回调函数button_folder_Pushed
            function button_folder_Pushed(src,event)%按钮选择btn_folder(选择)的回调函数
                foldernow=pwd;%获取当前文件地址
                try
                    newdir=uigetdir(foldernow,'选择文件夹');
                    folder_txt.String=newdir;
                catch ME
                    errordlg(sprintf('失败  %s',ME.message));
                end
            end

两个输入参数,src和event

src表示触发回调事件的源对象,通常是用户交互的UI控件(如按钮、滑块等)或图形对象(如坐标轴、线条等)‌。例如点击按钮时,src会指向该按钮的实例对象。可通过src.PropertyName访问控件的所有属性(如StringPositionColor等)‌,如

src.Text = 'Clicked';  % 修改按钮文本

event参数(全称eventdata)是记录事件触发时相关数据的对象,其内容根据不同的控件和事件类型而变化。event用于传递与事件相关的附加信息,例如:

  • 鼠标点击的坐标位置
  • 键盘按键的字符
  • 滑块移动的数值变化量
  • 表格单元格的修改内容
function sliderMoved(src, event)
    event.Value      % 滑块当前值
    event.PreviousValue % 滑块之前的值
    event.Source     % 触发事件的控件对象(同src)
end

回调函数button_folder_Pushed,首先通过 pwd 函数获取当前文件夹,matlab中的当前文件夹指matlab主程序当前的文件夹,即主程序地址栏显示的地址,如:

然后通过try...catch...end语句处理后续代码, try...catch...end语句能够有效处理错误,代码运行时先执行try和catch之间的语句,发生错误后执行catch和end之间的语句。

在button_folder_Pushed回调中,try和catch之间的语句用于获取,用户选择的文件夹地址newdir,并将新的地址赋值给folder_txt的String属性,让用户能够看到自己选择的地址。

获取地址用到了函数uigetdir,用于打开一个对话框供用户选择文件夹,使用格式如下,selpath是用户选择的路径,path是我们设置的默认路径,也就是用户打开对话框是显示的路径,title是对话框的标题,path和title输入可以省略:

selpath = uigetdir%不设置默认地址和标题,此时的地址是matlab主程序当前的文件夹
selpath = uigetdir(path)%设置默认地址
selpath = uigetdir(path,title)%设置默认地址和标题

如果try和catch之间的语句运行发生错误,则执行catch和end之间的语句。此处catch 后面有一个参数ME,用于获取错误的信息,ME(MException)的参数信息见帮助,我们常用的是message属性,用于返回错误信息的文字说明。

在catch和end之间,使用errordlg对话框处理错误信息。errordlg最核心的输入参数就是msg,即错误信息,是'字符向量'(char)或者"字符串"(string)。

回调函数button_done_Pushed
            function button_done_Pushed(src,event)%按钮选择button_done(确定)的回调函数
                name=name_txt.get('String');
                info=info_txt.get('String');
                projectdir=folder_txt.get('string');
                if ~isempty(name)
                    timenow=datetime('now', 'Format', 'yyyy-MM-dd HH:mm:ss', 'TimeZone', 'local');
                    col1=name;
                    col2=char(timenow);
                    col3=char(timenow);
                    col4=projectdir;
                    col5=info;
                    row_data={col1,col2,col3,col4,col5};
                    %新建文件夹
                    try
                        newdir=char(sprintf('%s/%s',projectdir,name));
                        if ~exist(projectdir,'dir')
                            mkdir(projectdir);
                        end
                        mkdir(newdir);

                        %赋初值
                        app.project_name=name;%项目名称
                        app.project_dir=projectdir;%项目地址
                        app.create_time=col2;%创建时间
                        app.last_modefied_time=col3;%最后修改时间

                        app.projects_info=[app.projects_info;row_data];
                        app.UITable_projectlist.Data=app.projects_info;
                        app.save_projects_info;
                        delete(gcf);
                    catch ME
                        rmdir(newdir, 's');
                        errordlg(ME.message);
                        errordlg(sprintf('失败  %s',ME.message));
                        delete(gcf);
                    end
                end

button_done_Pushed回调函数的第一部分是获取用户设置的项目名称(name)、项目介绍(info)、项目所在地址(projectdir),通过 .get('String')的方式分别获取name_txt、info_txt、folder_txt三个组件的值,得到的都是字符串。

name=name_txt.get('String');
info=info_txt.get('String');
projectdir=folder_txt.get('string');

 然后通过~isempty(name)判断name是不是空数组,不是空数组的话就继续执行后续代码,即将获得的name、info、projectdir信息作为新的项目信息建立新项目,建立新项目包括以下几个部分

1、构建新项目信息

新项目信息包括前面提到的name、info、projectdir,为了便于识别项目,我们增加项目的创建时间和最后更新时间的信息,即col2,col3,通过datetime函数获取按格式的时间。

'now' 代表获取当前时间;

'Format', 'yyyy-MM-dd HH:mm:ss' 时间的格式定义为“年-月-日 小时-分钟-秒”

'TimeZone', 'local' 时区选择本地时间

由于datatime格式不能直接赋值给UITable所以这里直接转化为字符向量char,即char(timenow)。

将col1~col5通过大括号{}转化为元胞数组,元胞数组和普通数组的区别是可以容纳不同类型的元素,同时可以直接给表UITable赋值。

timenow=datetime('now', 'Format', 'yyyy-MM-dd HH:mm:ss', 'TimeZone', 'local');
col1=name;
col2=char(timenow);
col3=char(timenow);
col4=projectdir;
col5=info;
row_data={col1,col2,col3,col4,col5};

在开始后续代码前为app_01新建几个属性,如下:

    properties (Access = private)
        projects_info=[]; % 存储项目信息

        project_name;%项目名称 ,新建的属性
        project_dir;%项目地址 ,新建的属性
        create_time;%创建时间 ,新建的属性
        last_modefied_time;%最后修改时间 ,新建的属性
    end

2、接下来使用try...catch...end处理后续代码

将前面获取的用户选择文件夹和文件名合并为新的文件地址newdir。

使用~exist(projectdir,'dir')判断用户选择的文件夹是否存在;

如果不存在则新建这个文件夹地址mkdir(projectdir);

然后建立项目所在文件夹地址mkdir(newdir);

此处使用了函数mkdir,其使用语法有以下几种:

mkdir newdir %当前目录下直接新建名为newdir的文件夹
[status, msg, msgID] = mkdir('newFolder')%在当前文件夹下创建newFolder的文件夹,返回值status为1标识成功,0表示失败,msg返回错误信息,msgID错误信息的ID,如status = logical 1,msg = 'Directory already exists.',msgID = 'MATLAB:MKDIR:DirectoryExists'
mkdir('newFolder','parentdir')%输入文件夹可以有两个参数,要建立的文件夹newFolder和父文件夹parentdir,也可以使用案例中的方式将newFolder设置为完整的文件夹路径

 将新建的项目赋值给app_01,作为当前项目,调用app_01的所有属性和函数,都要在前面加上  app.  。这是matlab规定的app类调用的方法。

 %赋初值
app.project_name=name;%项目名称
app.project_dir=projectdir;%项目地址
app.create_time=col2;%创建时间
app.last_modefied_time=col3;%最后修改时间

将新的项目信息添加到 app.projects_info的下一行

app.projects_info=[app.projects_info;row_data];

 将projects_info赋值给UITable_projectlist显示给用户看。

app.UITable_projectlist.Data=app.projects_info;
                    try
                        newdir=char(sprintf('%s/%s',projectdir,name));
                        if ~exist(projectdir,'dir')
                            mkdir(projectdir);
                        end
                        mkdir(newdir);

                        %赋初值
                        app.project_name=name;%项目名称
                        app.project_dir=projectdir;%项目地址
                        app.create_time=col2;%创建时间
                        app.last_modefied_time=col3;%最后修改时间

                        app.projects_info=[app.projects_info;row_data];
                        app.UITable_projectlist.Data=app.projects_info;
                        app.save_projects_info;
                        delete(gcf);
                    catch ME
                        rmdir(newdir, 's');
                        errordlg(ME.message);
                        errordlg(sprintf('失败  %s',ME.message));
                        delete(gcf);
                    end

delete(gcf)用于删除当前对话框,更准确的方式是delete(d),删除我们建立的对话框d。

如果过程出现了问题,则删除新建的文件夹。 

 最后给app_01新建一个函数save_projects_info,用于保存projects_info;

save函数,的输入参数包括保存的文件名、要保存的对象名(可以是多个对象),不设置地址默认保存在当前文件夹。

    methods (Access = private)

        function save_projects_info(app)%将projects info保存到本地
            projects_info=app.projects_info;
            save("projects_info.mat","projects_info");
        end
    end

同时,为了在项目起动时就能够显示我们保存的项目,需要为app_01添加回调函数startupFcn(app), 这是系统自带的回调函数。在组件浏览器中选择app_01,点击编辑器的回调,然后选择startupFcn

 新建并添加以下代码:首先判断projects_info.mat文件是否存在,如果存在则加载(load),并将数据赋值给app.projects_info;在赋值给UITable_projectlist。

        function startupFcn(app)
            %导入项目清单
            try
                folder=pwd;
                fulefilename=fullfile(folder, 'projects_info.mat');
                if exist(fulefilename,"file")
                    projectsinfo=load("projects_info.mat");
                    app.projects_info=projectsinfo.projects_info;
                    app.UITable_projectlist.Data=app.projects_info;
                end
            catch ME
                errordlg(sprintf('错误  %s',ME.message));
            end
        end

本章完整代码如下 

        function Button_newprohjectPushed(app, event)
            %第一部分:提示是否新建项目
            selection = uiconfirm(app.UIFigure,"新建项目前请确认保存的现有的项目,是否开始新建项目?","确认新建项目", ...
                "Icon","warning");
            if isequal('Cancel',selection)
                return
            end

            %第二部分:通过编程代码的方式建立新的新建对话框
            d = dialog('Position',[500 400 500 250],'Name','新建项目');
            %项目名称
            name_label = uicontrol('Parent',d,...
                'Style','text',...
                'Position',[30 210 100 20],...
                'String','项目名称');
            name_txt=uicontrol('Parent',d,...
                'Style','edit',...
                'Position',[120 210 300 20],...
                HorizontalAlignment='left');
            %项目说明
            info_label = uicontrol('Parent',d,...
                'Style','text',...
                'Position',[30 150 100 20],...
                'String','项目说明');
            info_txt=uicontrol('Parent',d,...
                'Style','edit',...
                'Position',[120 130 300 60],...
                'Min',0,'Max',1000,...
                'String','',...
                HorizontalAlignment='left');
            %保存地址
            foldernow=pwd;%获取当前文件地址
            folder_label = uicontrol('Parent',d,...
                'Style','text',...
                'Position',[30 100 100 20],...
                'String','项目地址');
            folder_txt=uicontrol('Parent',d,...
                'Style','edit',...
                'Position',[120 100 300 20],...
                'String',foldernow,...
                HorizontalAlignment='left');
            button_folder = uicontrol('Parent',d,...
                'Style','pushbutton',...
                'Position',[420 97 50 25],...
                'String','选择',...
                Callback=@button_folder_Pushed);
            %确定按钮
            button_done = uicontrol('Parent',d,...
                'Style','pushbutton',...
                'Position',[350 20 70 25],...
                'String','确定',...
                Callback=@button_done_Pushed);

            %第三部分:新建对话框中回调函数
            function button_folder_Pushed(src,event)%按钮选择btn_folder(选择)的回调函数
                foldernow=pwd;%获取当前文件地址
                try
                    newdir=uigetdir(foldernow,'选择文件夹');
                    folder_txt.String=newdir;
                catch ME
                    errordlg(sprintf('失败  %s',ME.message));
                end
            end


            function button_done_Pushed(src,event)%按钮选择button_done(确定)的回调函数
                name=name_txt.get('String');
                info=info_txt.get('String');
                projectdir=folder_txt.get('string');
                if ~isempty(name)
                    timenow=datetime('now', 'Format', 'yyyy-MM-dd HH:mm:ss', 'TimeZone', 'local');
                    col1=name;
                    col2=char(timenow);
                    col3=char(timenow);
                    col4=projectdir;
                    col5=info;
                    row_data={col1,col2,col3,col4,col5};
                    %新建文件夹
                    try
                        newdir=char(sprintf('%s/%s',projectdir,name));
                        if ~exist(projectdir,'dir')
                            mkdir(projectdir);
                        end
                        mkdir(newdir);

                        %赋初值
                        app.project_name=name;%项目名称
                        app.project_dir=projectdir;%项目地址
                        app.create_time=col2;%创建时间
                        app.last_modefied_time=col3;%最后修改时间

                        app.projects_info=[app.projects_info;row_data];
                        app.UITable_projectlist.Data=app.projects_info;
                        app.save_projects_info;
                        delete(gcf);
                    catch ME
                        rmdir(newdir, 's');
                        errordlg(ME.message);
                        errordlg(sprintf('失败  %s',ME.message));
                        delete(gcf);
                    end
                end

            end
        end
Logo

「智能机器人开发者大赛」官方平台,致力于为开发者和参赛选手提供赛事技术指导、行业标准解读及团队实战案例解析;聚焦智能机器人开发全栈技术闭环,助力开发者攻克技术瓶颈,促进软硬件集成、场景应用及商业化落地的深度研讨。 加入智能机器人开发者社区iRobot Developer,与全球极客并肩突破技术边界,定义机器人开发的未来范式!

更多推荐