当前位置: 首页 > article >正文

015_GUI_in_Matlab中实现GUI开发

Matlab的GUI设计

以前,Matlab有一个GUIDE工具,可以用来设计GUI,凑凑活活,要说毛病,主要是逻辑上很难写清楚,大量工具都没有提供,我记得好像数据交换通过控件的Tag属性来整(没做过几个,早就忘记)。在2019b,MathWorks声明未来会停止发布并删除GUIDE,并且提供了新的开发工具和迁移工具。从2020a开始,GUIDE ==> App Designer的迁移工具可用性大大增强,已经很好用。

目前,Matlab的GUI设计有两种方式:

  • App Designer拖放式设计
  • 编程设计GUI

App Designer

在这里插入图片描述
App Designer的入口在主界面的工具栏,在APP标签下面,第一个按钮就是App Designer。App Designer是一个拖放式设计工具,可以很方便的设计GUI。

App Designer的界面跟QtDesigner,SceneBuilder等界面设计工具类似,左边是控件列表,中间是设计区,右边是属性编辑器。设计区可以拖放控件,属性编辑器可以编辑控件的属性。

同样,App Designer有两种模式来查看GUI,一种是设计模式(GUI的显示),一种是代码模式(是一个类,可以编辑GUI的回调函数)。

在第一个模式下,可以拖放控件,设置控件属性。

在这里插入图片描述

在第二个模式下,可以编辑GUI的回调函数,这个模式下,可以看到GUI的类定义,可以编辑类的方法。

在这里插入图片描述

这种方式简单直观,封装也很不错,适合比较简单地给编的函数加个GUI。设计好的APP可以导出成一个独立的APP,可以在没有Matlab的机器上运行(需要安装对应的runtime),也能导出成一个m文件,可以在Matlab命令行运行。

整个过程很简单,按照操作一步一步尝试就行。MathWorks还提供Runtime的下载,可供客户安装。
在这里插入图片描述

编程设计GUI

GUI编程实现,可以用figure作为窗口,也可以用uifigure作为窗口,这里先介绍uifigure的方式。这个方式提供与App Designer类似的功能,但是通过一系列ui开头的函数来实现。

所涉及的函数可以做如下的总结。

容器
函数说明
uifigure创建GUI设计窗口
uigridlayout创建网格布局
uipaenl创建面板
uitabgroup创建标签页
uitab创建标签
坐标控件
函数说明
uiaxes创建画图的坐标系
axes创建笛卡尔坐标系
geoaxes创建地理坐标系
polaraxes创建极坐标系
通用控件
函数说明
uibutton创建按钮
uibuttongroup创建按钮组(特别是复选框按钮和单选按钮)
uicheckbox创建复选框
uidatepicker创建日期选择控件
uidropdown创建下拉式列表
uieditfield创建编辑框
uihyperlink创建超链控件
uiimage创建图片控件
uilabel创建文本标签控件
uilistbox创建列表控件
uiradiobutton创建单选按钮
uislider创建滑动条
uispinner创建旋转扭
uitable创建表格
uitextarea创建文本域
uitogglebutton创建状态开关按钮
uitree创建标准树和复选框树
uitreenode创建树节点
图形工具
函数说明
uicontextmenu创建上下文菜单
uimenu创建菜单
uipushtool创建工具栏按钮
uitoggletool创建工具栏开关按钮
uitoolbar创建工具栏
仪器外观控件
函数说明
uigauge创建仪表盘
uilamp创建灯泡
uiknob创建旋钮
uiswitch创建开关
可扩展控件
函数说明
uihtml创建HTML控件
控件控制与风格
函数说明
expand展开树节点
collapse折叠树节点
move移动控件
scroll滚动控件
open打开上下文相关菜单
uistyle创建控件样式
addStyle添加控件样式
removeStyle移除控件样式
对话框和消息通知
函数说明
uialert创建警告对话框
uiconfirm创建确认对话框
uiprogressdlg创建进度对话框
uisetcolor创建颜色选择对话框
uigetfile创建文件选择对话框
uigetdir创建文件夹选择对话框
uiputfile创建文件保存对话框
uiopen创建载入文件对话框
uisave创建保存mat文件对话框

总结

具体所需要的函数除了上面的,主要是各种对象的属性,这个具体查询文档即可。

一个简单的例子

我们的要求是,设计一个UI输入一组参数,这组参数每个都有一个名字,每个都有一个单位,每一个都有一个默认值,要求按照单位输入数字,最终得到一个数组,这个数组就是输入的参数。

需求

分析需求,也就是编写测试用例。

labels = {'a', 'b', 'c'};
defaultValues = [1,2,3];
units = {'m/s', 'kg', '$m^2$'};

% 测试用例一,包含单位输入,第一个参数是矿口标题,第二参数是参数名,第三个参数是默认值,第四个参数是单位
parameters = editParameters('test', labels, defaultValues, units);
disp(parameters);

% 测试用例二,不输入单位,所有参数均为无量纲,第一个参数是矿口标题,第二参数是参数名,第三个参数是默认值
parameters = editParameters('test', labels, defaultValues);
disp(parameters);

设计

确定测试用例,就能直接开始编写代码。

function parameters = editParameters('test', labels, defaultValues, units)

parameters = defaultValues;

这个函数就能让测试一无报错运行,测试二不行。那么先修理测试二。

function parameters = editParameters('test', labels, defaultValues, units)
n = length(labels);
if nargin < 4
    units = num2cell(repmat("", 1, n));
end

parameters = defaultValues;

这样可以通过两个测试,接下来就是设计UI。

首先,我们对输入参数的特性进行规范,也称为调用规范。

assert(iscell(labels) , 'labels must be a cell array');
assert(iscell(units), 'units must be a cell array');
assert(isnumeric(defaultValues), 'defaultValues must be a numeric array');
assert(n == length(defaultValues), 'labels and defaultValues must have the same length');
assert(n == length(units), 'labels and units must have the same length');    

这段代码规范输入参数类型、长度等,这样可以确保输入参数正确。

接下来,我们设计UI。

% Create a figure and axes
fig = uifigure('Position',[100 100 600 (n+1)*50]);
fig.Name = sprintf("%s (n=%d)", name, n);

layout = uigridlayout(fig);

layout.ColumnWidth = {'4x', '8x', '4x'};
layout.RowHeight = [repmat("1x", 1, n), "1.2x"];

这段代码创建窗口,标题为输入参数,位置为100,100,大小为600x(n+1)*50,其中n为参数个数。然后创建一个网格布局,设置列宽和行高的比例关系。

接下来,我们创建控件。

edits = cell(1, n);

for i = 1:n
    label = uilabel(layout);
    label.Text = labels{i}+":";
    label.Layout.Row = i;
    label.Layout.Column = 1;
    label.HorizontalAlignment = 'right';
    
    edit = uieditfield(layout, 'numeric');
    edit.Layout.Row = i;
    edit.Layout.Column = 2;
    edit.Value = defaultValues(i);
    
    edits{i} = edit;
    
    unit = uilabel(layout);
    unit.Text = units{i};
    unit.Interpreter = 'latex';
    unit.Layout.Row = i;
    unit.Layout.Column = 3;
    
    edit.ValueChangedFcn = @(~,~)editValueChangedFcn(i, edit.Value);
end

对于每一个参数,创建一个标签,一个编辑框,一个单位标签,然后设置位置,值,单位等。最后,把编辑框的句柄保存下来,并设置编辑框的值变化回调函数。

接下来是按向量方式输入的输入框,两个按钮(重置为默认值、确定)

vectorLabel = uilabel(layout);
vectorLabel.Text = "向量形式:";
vectorLabel.Layout.Row = n+1;
vectorLabel.Layout.Column = 1;
vectorLabel.HorizontalAlignment = 'right';

vectorEdit = uieditfield(layout, 'text');
vectorEdit.Layout.Row = n+1;
vectorEdit.Layout.Column = 2;
vectorEdit.Value = mat2str(reshape(defaultValues, 1, n));
vectorEdit.ValueChangedFcn = @(~,~)updateAllValues(vectorEdit.Value);

buttonLayout = uigridlayout(layout);
buttonLayout.ColumnWidth = {'1x', '1x'};
buttonLayout.RowHeight = {'1x'};
buttonLayout.Layout.Row = n+1;
buttonLayout.Layout.Column = 3;

resetBut = uibutton(buttonLayout);
resetBut.Text = "重置";
resetBut.Layout.Row = 1;
resetBut.Layout.Column = 1;

okBut = uibutton(buttonLayout);
okBut.Text = "确定";
okBut.Layout.Row = 1;
okBut.Layout.Column = 2;

resetBut.ButtonPushedFcn = @(~,~)resetValues();
okBut.ButtonPushedFcn = @(~,~)okButFcn();
fig.KeyPressFcn = @(~,evt)keyPressFcn(evt);
fig.CloseRequestFcn = @(~,~)okButFcn();


uiwait(fig);

这段代码创建标签、编辑框来输入向量形式数据,最后两个按钮共占网格第三列,分别是重置和确定。重置按钮的回调函数是重置所有参数为默认值,确定按钮的回调函数是关闭窗口。

最后这个uiwait(fig)是阻塞窗口,直到窗口关闭才会继续执行,相当于模态对话框,否则就是无模态对话框。可以删除这句试试结果。

除两个按钮的事件、向量编辑事件,最后还给整个窗口设置键盘事件和关闭事件,回调函数如下。

    function updateAllValues(val)
        matValue = str2num(val); %#ok<ST2NM>
        if length(matValue)>= n
            arrayfun(@(i)editValueChangedFcn(i, matValue(i)), (1:n));
        end
    end

    function resetValues()
        arrayfun(@(i)editValueChangedFcn(i, defaultValues(i)), (1:n));
    end
    function editValueChangedFcn(i, value)
        if edits{i}.Value ~= value
            edits{i}.Value = value;
        end
        parameters(i) = value;
        vectorEdit.Value = mat2str(reshape(parameters, 1, n));
    end

    function okButFcn()
        delete(fig);
    end

    function keyPressFcn(evt)
        disp(evt.Source);
        if strcmp(evt.Key, 'return')
            okButFcn();
        elseif strcmp(evt.Key, 'escape')
            resetValues();
        end
    end
  • updateAllValues:根据输入的向量,更新所有的输入框,调用editValueChangedFcn
  • resetValues:调用editValueChangedFcn,重置所有的输入框为默认值。
  • editValueChangedFcn:编辑框值变化回调函数,更新参数数组,更新向量编辑框。
  • okButFcn:确定按钮回调函数,关闭窗口,对应uiwait
  • keyPressFcn:键盘事件回调函数,回车键关闭窗口,ESC键重置所有参数。

整个函数的代码可以在MathWorks的社区找到,dialog to edit parameters.

运行界面如图所示。

在这里插入图片描述

测试

应该还可以增加一些测试,比如输入非法字符,输入非法向量等。但是这么个简单东西,真没必要。

总结

  1. Matlab的GUI设计有两种方式,一种是App Designer,一种是编程设计。
  2. uifigure创建窗口及配套控件和布局,构造的应用与App Designer类似。
  3. 编写GUI的函数,可以通过调用规范来规范输入参数,确保输入参数正确。
  4. 回调函数,因为有匿名函数调用的方式,可以很方便地在运行时绑定所需值,正确实现功能。

http://www.kler.cn/a/287366.html

相关文章:

  • 【计算机网络安全】湖北大学-mysql事务隔离性实验
  • 人工智能的未来展望与挑战
  • 已有docker增加端口号,不用重新创建Docker
  • 17.100ASK_T113-PRO 配置QT运行环境(三)
  • React Native 全栈开发实战班 - 数据管理与状态之Zustand应用
  • 【快速入门】前端御三家:HTML、CSS和JS
  • 【C++】list的使用和list的模拟实现和迭代器失效问题
  • django外键表查询
  • 随手记:小程序体积超出2M包大小如何优化
  • Superset安装
  • World of Warcraft [CLASSIC][80][Grandel]Sapphire Hive Drone
  • RocketMQ高级特性二-消息重试与流控
  • Sentieon 应用教程 | 使用CNVscope进行CNV检测分析
  • 精通Redis-CLI:命令行玩转高效缓存
  • Java设计模式之外观模式详细讲解和案例示范
  • Git分支原理、操作及实际开发中如何规范使用分支
  • pycharm破解教程
  • OD C卷 - 项目排期/最少交付时间
  • java05
  • Java Web_00001
  • HarmonyOS开发实战:Node-API扩展能力接口
  • 从实验室到应用:LC-MS/MS技术与AbMole化合物共舞,揭开半胱氨酸靶向共价抑制剂的新篇章
  • Android创建自己的内容提供器(ContentProvider)
  • java面试(java基础)
  • python脚本处理---(不同文件夹中的文件对比、移动,提取指定类型文件、中文文件名转英文)
  • 年化从19.1%提升到22.5%,全球大类资产轮动,加上RSRS择时,RSRS性能优化70倍。(附策略源码)