Callbacks_in_MATLAB中APP界面回调函数
回调函数
用法
回调函数,是Matlab GUI和App设计中的一个核心的内容。通过用户注册回调函数,在界面操作时App代码会自动调用相应代码,实现各种功能。编程实现GUI App时,回调函数的注册方式有两种:
1function fig = createApp
2fig = uifigure;
3
4btn = uibutton(fig, 'push', 'ButtonPushedFcn', @buttonPushed);
5% btn = uibutton(fig, 'push', ButtonPushedFcn=@buttonPushed);
6
7btn.ButtonPushedFcn = @buttonPushed;
8
9% 或者
10fh = @(src, event) disp('Button pushed');
11btn.ButtonPushedFcn = fh;
12
13end
具体回调函数的形式请查看帮助,一般而言,函数有两个参数,第一个参数是发起回调的对象,这里就是btn
,第二个参数是事件数据,这里是ButtonPushedData
。
回调函数设定时的值可以有三种形式:
- 函数句柄,如
@buttonPushed
- 函数句柄和参数,如
{@buttonPushed, arg1, arg2}
- 匿名函数,如
@(src, event) disp('Button pushed')
全局函数、局部函数和嵌套函数
以这个函数为例:
1function buttonPushed(src, event)
2 disp('Button pushed');
3end
函数可以通过三种方式来实现:
- 函数文件形式的全局函数,单独有一个
buttonPushed.m
文件 - 放在文件最后的局部函数,在这里就是函数文件
function ...... end
之后的局部函数 - 放在函数内部的嵌套函数
嵌套函数的定义形式如下:
1function fig = createApp
2
3 fig = uifigure;
4 btn = uibutton(fig, 'push', 'ButtonPushedFcn', @buttonPushed);
5
6 function buttonPushed(src, event)
7 disp('Button pushed');
8 end
9end
这三种情况没有本质的区别,其中,嵌套函数可以共享在函数调用之前定义的所有变量,这是一个很有用的特性。
全局函数和局部函数都不能共享变量,但是可以通过global
关键字来共享变量。主要通过{@functionName, arg1, arg2}
的形式来传递参数。另外,匿名函数也捕获函数,比如:
1@(src, event) funcWithExtraArgs(args1, src, event, arg2)
这种情况下,funcWithExtraArgs
函数会被调用,参数是args1, src, event, arg2
。这个时候,整个函数的排列方式是按照用户的需要自行定义的。
内部实现
用于App响应用户的输入。在类层次结构中给出的UI相关的类结构中,实现了一个对用户提供的响应函数的支持。
matlab.ui.control.internal.controller.ComponentController
appdesservices.internal.interfaces.model.AbstractModel
其中两个重要的类是ComponentController
和AbstractModel
,这两个类是handle
的子类,构成一个MVC
模式。ComponentController
是控制器,AbstractModel
是模型。
在ComponentController
中, 有一个函数:
1function handleUserInteraction(obj, clientEventName, eventData, callbackInfo)
2 % Method to be called by the subclasses when handling a user
3 % interaction that results in either:
4 % - a user callback executing (e.g. ButtonPushedFcn)
5 % - a property update and a user callback executing (e.g.
6 % ValueChangedFcn)
7 % - any number greater than 2 of the above (e.g. 'mouseclicked'
8 % results in 2 callbacks)
9 %
10 % Typically, the subclasses would implement handleEvent, and in
11 % the case of a user interaction, call handleUserInteraction.
12 %
13 % INPUTS:
14 %
15 % - clientEventName: event name of the client side event
16 % that this is a response to
17 %
18 % - eventData: event data of the client side event that this
19 % is a response to
20 %
21 % - as many cells as the number of callbacks to execute.
22 % Minimum is 1.
23 % See executeUserCallback for the formatting of each cell.
24 %
25 % Example:
26 %
27 % obj.handleUserInteraction(...
28 % 'mousedragging', ...
29 % event.Data, ...
30 % {'ValueChanging', eventData}, ...
31 % {'ValueChanged', eventData, 'Value', newValue}, ...
32 % );
33
34 assert(nargin == 4);
35
36 obj.Model.executeUserCallback(callbackInfo{:});
37
38 if(~isvalid(obj))
39 % It is possible the user callback deleted the component
40 %
41 % If so, don't do any more
42 return;
43 end
44 % Force the view to process the value update before
45 % emitting the event.
46 % If the property is revered to its old value in a callback
47 % (its own or from another component),
48 % the visual might not update because of the peer node
49 % coalescing events from property sets.
50 % Ensure that the visual will react to a potential
51 % reversion by forcing the view to process the current
52 % value.
53 %
54 % We don't need to specify any property names in
55 % 'refreshProperties' because simply sending an event
56 % flushes the propertiesSet event queue. If we explicitly
57 % passed in the propertyName, the view would refresh
58 % twice in the case of the reversion from the callback of
59 % another component.
60 %
61 % see g1124873 and g1218934
62
63 obj.refreshProperties({});
64
65
66 % After all matlab events for this client side event have been
67 % emitted and callbacks processed, send an event to the client
68 % if the event is registered to use an event coalescing
69 % mechanism.
70 % Need to check if obj and ViewModel are valid or not because
71 % the user's callback could delete the app or the component
72 % see g1336677
73 coalescedEventIsField = isfield(eventData, 'CoalescedEvent');
74 if(isvalid(obj) && coalescedEventIsField && eventData.CoalescedEvent)
75 obj.sendFlushEventToClient(obj.Model, clientEventName);
76 end
77
78end
这里面的obj.Model
就是一个AbstractModel
的对象
1function executeUserCallback(obj, matlabEventName, matlabEventData, propertyName, propertyValue)
2 % Execute user callbacks associated with 'matlabEventName'.
3 % If a property-value pair is also provided, the property will
4 % be updated before the callbacks are executed.
5 %
6 % INPUTS:
7 %
8 % - MatlabEventName: string representing the event that the component
9 % model should emit as a result of the user interaction
10 % - MatlabEventData: eventdata associated with eventName
11 %
12 % Example: obj.executeUserCallback('ButtonPushed', 'ButtonPushed', eventData);
13 %
14 % Optional INPUTS:
15 %
16 % - propertyName: name of the property to be modified as
17 % a result of the user interaction if any
18 % - propertyValue: value to update the property to
19 %
20 % Example: obj.executeUserCallback('ValueChanged', eventData, 'Value', newValue);
21
22 assert(nargin == 3 || nargin == 5);
23
24 if(nargin == 3)
25 % There is no property to update, just emit the event
26
27 % Have the model emit the event
28 % The event handling system will execute the callbacks
29 % associated with this event.
30 notify(obj, matlabEventName, matlabEventData);
31
32 else
33 % propertyName and propertyValue were passed in as inputs
34 % The property needs to be updated before sending the event
35
36 oldValue = obj.(propertyName);
37
38 % Check that the property value has indeed changed
39 if(isequal(oldValue,propertyValue))
40 % The value has not changed, do not emit event.
41 % This check is a catch all for instances where the
42 % view does send an event even when the value didn't
43 % really change.
44 return;
45 end
46
47 % Update the property value
48 obj.(propertyName) = propertyValue;
49
50 % Mark properties dirty
51 %
52 % Usually, the property is private, such as 'PrivateFoo'
53 obj.markPropertiesDirty({propertyName});
54
55 % Have the model emit the event
56 % The event handling system will execute the callbacks
57 % associated with this event.
58 notify(obj, matlabEventName, matlabEventData);
59 end
60end
这些代码是Matlab的UI组件的回调函数的核心代码,通过这些代码,可以实现UI组件的回调函数的功能。
例子
下面我们改造一下官方帮助文件中的例子,搞一个用下拉列表来更换背景颜色的例子。运行的效果如下:
文件下载地址:
1function fig = updateDropDown
2fig = uifigure('Position', [100 100 480 300], 'Visible', 'off', Name='DropDown Example');
3fig.Icon = 'icon.jpg';
4movegui(fig, 'center');
5
6fg = uigridlayout(fig, [3 2]);
7fg.ColumnWidth = {'fit', '1x'};
8fg.RowHeight = {'1x', 'fit', '1x'};
9
10
11% color tareget
12p = uipanel(fg);
13
14% 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'black', 'white' 和 'none'
15% 有效的十六进制颜色代码由 '#' 后跟三个或六个十六进制数字组成
16p.BackgroundColor = 'white';
17
18% 'none' | 'etchedin' | 'etchedout' | 'beveledin' | 'beveledout' | 'line'。
19p.BorderType = 'line';
20
21p.Title = 'Color Target';
22p.Layout.Row = [1, 3];
23p.Layout.Column = 2;
24% add text in the center of the panel
25g = uigridlayout(p, [1 1]);
26g.RowHeight = {'1x'};
27g.ColumnWidth = {'1x'};
28t = uilabel(g, 'Text', '黑色文字',...
29 'FontSize', 48, ...
30 'HorizontalAlignment', 'center',...
31 'VerticalAlignment', 'center');
32
33% no height resize
34
35t.Tooltip = "'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'black', 'white'; 有效的十六进制颜色代码由 '#' 后跟三个或六个十六进制数字组成";
36
37
38dd = uidropdown(fg, ...
39 'Editable', 'on', ...
40 'ValueChangedFcn', {@addItems, t});
41
42dd.Tooltip = "选择或者编辑颜色";
43dd.Layout.Row = 2;
44dd.Layout.Column = 1;
45
46
47% dd.Items = ["red", "yellow", "green"];
48dd.Items = {'red','green','blue','cyan','magenta','yellow','black','white' };
49for idx = 1:numel(dd.Items)
50 setUpBg(dd, idx, dd.Items{idx});
51end
52
53fig.UserData = struct('dropdown', dd,...
54 'panel', p, ...
55 'text', t);
56
57t.BackgroundColor = dd.Value;
58
59fig.Visible = 'on';
60
61end
62
63function setUpBg(target, idx, c)
64% table, tree, list box, or drop-down UI component
65st = uistyle("BackgroundColor", c);
66addStyle(target, st, "item", idx);
67end
68
69
70function addItems(src, event, p)
71arguments
72 src (1,1) matlab.ui.control.DropDown
73 event (1,1) matlab.ui.eventdata.ValueChangedData
74 p (1,1) matlab.ui.control.Label
75end
76val = src.Value;
77
78if event.Edited
79 src.Items{end+1} = val;
80 setUpBg(src, numel(src.Items), val);
81end
82
83p.BackgroundColor = val;
84
85end
UI相关类
App Designer中的UI编程的函数和类在%MATLAB_ROOT%\toolbox\matlab\uicomponents\uicomponents
目录下,根据安装位置,%MATLAB_ROOT%
可能是C:\Program Files\MATLAB\R2023b
。在+matlab.ui
目录下有很多UI相关的类,这些类是App Designer的基础类,可以通过这些类来实现App Designer中的功能。
如果把这里面大部分类的继承关系整一个图,大概是这样的:
这个图什么都看不清,但是实际上,运行下面的代码,这个图可随便放大,也能通过鼠标在图上查看节点信息(名称、入度、出度),可以很容易地看到继承关系。
1paths = walkToTop({'matlab.ui.Figure', ...
2 'matlab.ui.container.Tree', ...
3 'matlab.ui.container.TreeNode', ...
4 'matlab.ui.container.GridLayout', ...
5 'matlab.ui.container.CheckBoxTree', ...
6 'matlab.ui.container.TabGroup', ...
7 'matlab.ui.container.Tab', ...
8 'matlab.ui.container.Panel',...
9 'matlab.ui.container.ContextMenu', ...
10 'matlab.ui.container.Menu', ...
11 'matlab.ui.control.Table', ...
12 'matlab.ui.control.AngularGauge', ...
13 'matlab.ui.control.Button', ...
14 'matlab.ui.control.CheckBox', ...
15 'matlab.ui.control.DatePicker', ...
16 'matlab.ui.control.DiscreteKnob', ...
17 'matlab.ui.control.DropDown', ...
18 'matlab.ui.control.EditField', ...
19 'matlab.ui.control.Gauge', ...
20 'matlab.ui.control.HTML', ...
21 'matlab.ui.control.Hyperlink', ...
22 'matlab.ui.control.Image', ...
23 'matlab.ui.control.Knob', ...
24 'matlab.ui.control.Label', ...
25 'matlab.ui.control.Lamp', ...
26 'matlab.ui.control.LinearGauge', ...
27 'matlab.ui.control.ListBox', ...
28 'matlab.ui.control.NinetyDegreeGauge', ...
29 'matlab.ui.control.NumericEditField', ...
30 'matlab.ui.control.RadioButton', ...
31 'matlab.ui.control.RangeSlider', ...
32 'matlab.ui.control.RockerSwitch', ...
33 'matlab.ui.control.SemicircularGauge', ...
34 'matlab.ui.control.Slider', ...
35 'matlab.ui.control.Spinner', ...
36 'matlab.ui.control.StateButton', ...
37 'matlab.ui.control.Switch', ...
38 'matlab.ui.control.TextArea', ...
39 'matlab.ui.control.ToggleButton', ...
40 'matlab.ui.control.ToggleSwitch',...
41 'matlab.ui.eventdata.ButtonPushedData', ...
42 'matlab.ui.eventdata.CheckedNodesChangedData', ...
43 'matlab.ui.eventdata.ClickedData', ...
44 'matlab.ui.eventdata.CollapsedChangedData', ...
45 'matlab.ui.eventdata.ComponentInteraction', ...
46 'matlab.ui.eventdata.ContextMenuOpeningData', ...
47 'matlab.ui.eventdata.DataChangedData', ...
48 'matlab.ui.eventdata.DoubleClickedData', ...
49 'matlab.ui.eventdata.DropDownInteraction', ...
50 'matlab.ui.eventdata.DropDownOpeningData', ...
51 'matlab.ui.eventdata.FigureInteraction', ...
52 'matlab.ui.eventdata.HTMLEventReceivedData', ...
53 'matlab.ui.eventdata.HyperlinkClickedData', ...
54 'matlab.ui.eventdata.ImageClickedData', ...
55 'matlab.ui.eventdata.ListBoxInteraction', ...
56 'matlab.ui.eventdata.MenuSelectedData', ...
57 'matlab.ui.eventdata.MutualExclusiveComponentSelectionChangeData', ...
58 'matlab.ui.eventdata.NodeCollapsedData', ...
59 'matlab.ui.eventdata.NodeExpandedData', ...
60 'matlab.ui.eventdata.NodeTextChangedData', ...
61 'matlab.ui.eventdata.PasswordEnteredData', ...
62 'matlab.ui.eventdata.SelectedNodesChangedData', ...
63 'matlab.ui.eventdata.TableInteraction', ...
64 'matlab.ui.eventdata.TreeInteraction', ...
65 'matlab.ui.eventdata.ValueChangedData', ...
66 'matlab.ui.eventdata.ValueChangingData'});
67
68
69% get all names into a set
70names = {};
71for i = 1:length(paths)
72 for j = 1:length(paths{i})
73 if ~ismember(paths{i}{j}, names)
74 names = [names, paths{i}{j}];
75 end
76 end
77end
78
79
80% initialize the digraph
81
82dgObj = digraph;
83for i = length(names):-1:1
84 dgObj = addnode(dgObj, names{i});
85end
86
87
88for i = 1:length(paths)
89 for j = 1:length(paths{i})-1
90 dgObj = addedge(dgObj, paths{i}{j+1}, paths{i}{j});
91 end
92end
93
94% visualize the digraph
95plot(dgObj, 'Layout', 'layered', 'NodeLabel', dgObj.Nodes.Name, 'NodeColor', 'r', 'EdgeColor', 'b');
96exportgraphics(gcf, 'matlab-ui-classes.png');
97
98% find the roots and write markdown for me
99parents = dgObj.Nodes{indegree(dgObj) == 0, :};
100for i = 1:numel(parents)
101 fprintf("- `%s`\n", parents{i});
102end
最后这段话找出所有的基类(也就是digraph
中入度为0
的节点),并且把下面的Markdown帮我写好。其实,最上面那段UI
对象的列表,我也是通过这段代码生成的……可以留做一个作业!
Matlab App开发的相关类的基类基本上是下面这些:
matlab.ui.eventdata.FigureInteraction
matlab.ui.eventdata.internal.Interaction
matlab.graphics.mixin.internal.GraphicsDataTypeContainer
matlab.mixin.internal.CompactDisplay
matlab.mixin.CustomDisplay
matlab.mixin.Heterogeneous
handle
其中,handle
是Matlab中的句柄类,所有的UI组件都是handle
的子类,这个类的对象都是传递引用的,而不是传递值的。这在UI组件的回调函数中是非常有用的,可以通过传递UI组件的句柄,来实现UI组件之间的交互。
参考
总结
- 回调函数是Matlab GUI和App设计中的一个核心的内容,通过用户注册回调函数,在界面操作时App代码会自动调用相应代码,实现各种功能。
- 回调函数的注册方式有两种:函数句柄和匿名函数。
- 回调函数具体实现方式有三种:全局函数、局部函数和嵌套函数。
文章标签
|-->matlab |-->gui |-->callback |-->uidropdown
- 本站总访问量:次
- 本站总访客数:人
- 可通过邮件联系作者:Email大福
- 也可以访问技术博客:大福是小强
- 也可以在知乎搞抽象:知乎-大福
- Comments, requests, and/or opinions go to: Github Repository