|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
6 D: a; ~4 `' S C8 p写这个帖子主要是因为之前恒润科技给单位开发一个工具软件,需要和Simulink深度结合。但是在提需求的时候,他们坚定的说不能自定义Simulink配置环境(换句话说就是不能和Simulink整合,只能提供单独的工具),且已经咨询Mathworks,说没法在Simulink中添加自定义菜单等。8 y' p+ H4 B6 @6 ?: D
% t+ ~) ]% ^% w9 m
先预览下效果图,给大家一个感性的认识:
1 _8 d6 S8 r: o: k% b
& h, }6 V. H1 w2 |' K/ h) O. ?另外这里提供本文用到的sl_customization函数2 ^1 U1 t6 Y2 ]; {) V5 T" e* c, b
; H7 G/ A, f. l0 j9 e8 A( ^ c! C
其实本文中涉及的技术,可以在Simulink的公开文档轻易找到,只是大家没有注意而已!另外本文中讨论的技术,不仅仅可以修改Simulink的菜单,还可以修改Simulink中很多环境配置,比如对话框控制、库浏览器、Simulink首选项等很多方面。本文仅以自定义Simulink菜单做抛砖引玉的作用!; l0 Y) @6 n4 j( A
5 P. V' }6 o3 n! \5 ^* S; A, C. }
在Simulink编辑器和Stateflow编辑中,可以在以下列表中添加菜单或命令:
+ p N; P! J3 \, [ Q
# o* i- U* |$ n0 w4 ~9 T% u- 顶级菜单的尾部
- 右键菜单的首部或尾部
- 工具条9 l$ V( v$ F3 n: V" _
5 O2 Z* s( L7 l' b c1、注册用户界面自定义(Registering User InteRFace Customizations)4 n3 i5 t% ~9 ]
首先在MATLAB的搜索路径下,添加一个sl_customization()函数,简单说,此函数就是告诉Simulink您希望对Simulink环境做哪些修改。function sl_customization()函数接受一个输入参数cm,cm是一个自定义管理对象( customization manager object)。cm对象中内置了一些进行环境自定义的方法。启动Simulink的时候,程序会自动加载sl_customization文件。; B% f1 i; ?9 O+ e' B) s
- function sl_customization(cm)
- % 用于自定义Simulink环境配置
1 @- x, _3 s) F; M8 `8 ^) O! O
7 w3 E2 A i9 l* n+ o, T- Y2 }! |2 N, T; u& G2 Y
2、添加自定义菜单函数(addCustomMenuFcn)" ]/ T& o, l' ?3 p
定制菜单主要用到cm对象的addCustomMenuFcn和addCustomFilterFcn两个方法。7 T! P, S7 Y. l( D. u
8 F5 R" m" O( L7 ?; C(1)addCustomMenuFcn(stdMenuTag, menuSpecsFcn)方法。此方法的主要功能是,将menuSpecsFcn中自定义的条目添加到stdMenuTag菜单项目下,主要参数意义如下:
' K+ V& x7 L+ j9 m/ k- menuSpecsFcn:函数句柄,返回新增菜单项的创建函数,具体请看后面的例子。
- stdMenuTag:字符串,指定需要进行修改的Simulink菜单。比如,希望在Simulink的Edit菜单下添加其它内容,那么stdMenuTag='Simulink:EditMenu',这个时候有人估计会疑问,既然Edit菜单stdMenuTag都那么复杂,那怎么知道其它菜单的stdMenuTag?其实很简单,在CommandWindows中执行如下命令
6 y7 i7 Z) O9 o+ K
- cm = sl_customization_manager;
- cm.showWidgetIdAsToolTip=true;
4 G. E+ r7 |7 e# q
0 f) m4 P7 f- h% ~" a p- b% |1 y$ f( L. z8 V
此时Simulink界面将变成如下样子,在原有菜单旁边会自动显示Simulink已有菜单的stdMenuTag( n. g# s% y g, }8 C" ?
1 |' F! d- ]& c5 p( L! o
2 C7 W _) U: d5 [) h2 T6 n9 l4 Q4 ^0 p; l
当您不需要显示那个stdMenuTag时,在命令窗口执行( n S* w8 E6 n! X
- cm.showWidgetIdAsToolTip=false;# _% k1 |2 F0 u0 O
5 s) x. D8 X4 z& b' l: a, l(1)addCustomFilterFcn(stdMenuItemID, filterFcn)方法。该方法主要是用于,禁用或激活stdMenuItemID菜单。本文不大算详细介绍,感兴趣的朋友可以看看帮助文档。
8 z# [% ^4 r, _9 j6 z; ?' F6 v, ^5 g% v" j, V( `- P- \1 L: }
因此文件中的代码重新修改如下: _: K, ]( |8 f/ `; W4 L
- function sl_customization(cm)
- % 用于自定义Simulink环境配置
- % 告诉Simulink根据MenuWeWantAdd()返回参数中指定的函数
- % 在'Simulink:EditMenu'菜单下添加新建菜单
- cm.addCustomMenuFcn('Simulink:EditMenu',@MenuWeWantAdd)$ p3 C) g; u9 o4 ]2 N0 s+ [
$ q J/ h/ a( @
0 j$ T7 Z" B+ U$ m. v* B- q0 B: H, X3、定义新增菜单创建函数(Define Menu Create Function)) r5 b7 k2 Z; ?+ z, } x) b2 Q
在第二步中,告诉Simulink根据MenuWeWantAdd()返回参数中指定的函数来创建新菜单项,但是到底是使用哪些函数以及添加哪些菜单我们还米有定义。因此在sl_customization.m文件中继续添加如下代码:
$ l3 ~( {- F: ^" X- ~2 U/ Y- function schemaFcns=MenuWeWantAdd(callbackInfo)
- % 返回用于创建新增菜单的函数,必须接受一个callbackInfo输入参数
- % 告诉Simulink分别使用下面的函数,来创建每个新增菜单项
- schemaFcns={...
- @CreateNewMenu1 % 新增第1个菜单的函数
- @CreateNewMenu2 % 新增第2个菜单的函数
- @CreateNewMenu3}; % 新增第3个菜单的函数/ w) x" Z0 q$ d5 U1 }3 y
# f) n* J8 {% q6 f: O
8 f! L) v: z6 R' r7 W1 a+ p/ ~( ?6 K其中MenuWeWantAdd()函数就是告诉Simulink通过哪些函数来创建菜单。
8 Z/ Y% Y/ @8 ~( ^ G" Z0 _9 F$ x& I) X: ]& y3 h
其中的输入参数callbackInfo,是一个Callback Info对象,包含以下几个属性/ E) ^! ]/ Y2 m. i
+ j& y/ x; Q# W' R$ |
, S2 p j: H4 I0 G+ L5 R属性 | 说明 | uiObject | 句柄,被点击菜单的父对象,Simulink编辑器或Stateflow编辑器 | model | 句柄,当前编辑器中显示的Simulink模型 | userdata | | 5 p& q# p+ ?8 c6 `# ]. c
2 a8 @& ?! A$ B
* r S- _ F9 m4、定义新增Action菜单项(Define Action Menu Items)9 {* O. H% a# j! u
虽然现在已经知道使用CreateNewMenu1()等函数来创建自定义菜单,但到底是什么菜单呢?,因此需要继续在sl_customization.m文件中添加如下代码:! z/ H, S% C6 z, h1 b% x) D! X+ H
- function schema=CreateNewMenu1(callbackInfo)
- % 用来创建NewMenu1菜单,必须接受一个callbackInfo输入参数
- % 创建一个schema对象的实例,用于定义新增菜单的内容
- schema=sl_action_schema; % Action Schema Object
- % 指定菜单的显示名称,必须
- schema.label = '新增Action菜单';
- % 指定菜单响应函数,必须,可以是字符串,函数句柄或者cell数组
- schema.callback = @NewMenu1Callback;
- % 设置用户数据,非必须
- schema.userdata = '';
- function NewMenu1Callback(callbackInfo)
- % 点击NewMenu1菜单时的响应函数,必须接受一个callbackInfo输入参数
- msgbox('新增Action菜单,被临幸了!') s0 N5 P5 h7 h8 c( d5 P& F
! o( O2 C0 {$ {% k, I! n5 V D5 e$ R1 |+ B" b9 E
Action Schema对象有以下属性+ @- R& S6 b3 x4 K/ u
% i& ^) Y1 V) H5 X) Y属性 | 取值 | 说明 | tag | 字符串,可选 | 菜单的唯一标识符,类似于Simulink:EditMenu的作用 | label | 字符串,必须 | 菜单的显示名称 | state | 字符串,可选,只能以下三个取值 'Enabled'(默认)、'Disabled'、'Hidden' | | statustip | 字符串,可选 | 当鼠标移动到菜单上时,状态栏显示的提示文本 | userdata | 任意数据,可选 | 用户之定义数据 | accelerator | 字符串,可选,例如,'CTR+K' | 菜单快捷键 | callback | 字符串或函数句柄,必须 | 点击菜单时的回调函数 | autoDisableWhen | 字符串,可选,只能以下三个取值'Locked'(默认)、'Busy'、'Never' | 什么时候自动禁用菜单 | + B9 z# \: I' \' f7 q% Z6 X. ?1 b( o
. y i0 T) p h
5、添加Toggole菜单按钮(Toggle Schema Object)
9 K% h% u ]8 @3 z: f其实在第4步中定义的是Action Schema Object,也是点击菜单,当点击菜单时响应相应的回调,但是菜单的形状不发生变化。而Toggle Schema Object在点击以后会发生形状变化,比如点击以后外形会凹陷下去,或者在菜单前面有一个√。
+ h C8 G8 Z+ v" G' O
5 J3 j- c0 A, v7 u |Toggle Schema Object和Action Schema Object的属性基本一致,只是Toggle对象多了一个checked属性,当摁下时,checked=='on',否则checked=='off'(默认)。另外Toggle对象是使用sl_toggle_schema创建实例的。
+ |# o# n& M0 v. P* y7 L4 E- function schema=CreateNewMenu2(callbackInfo)
- % 用来创建NewMenu2菜单,必须接受一个callbackInfo输入参数
- % 创建一个schema对象的实例,用于定义新增菜单的内容
- schema=sl_toggle_schema; % 注意这里是一个Toggle
- % 指定菜单的显示名称,必须
- schema.label = '新增Toggle菜单';
- % 指定菜单响应函数,必须
- schema.callback = @NewMenu2Callback;
- % 设置用户数据,非必须
- schema.userdata = '';
- function NewMenu2Callback(callbackInfo)
- % 点击NewMenu2菜单时的响应函数,必须接受一个callbackInfo输入参数
- msgbox('新增Toggle菜单,被临幸了!')
0 K) _" F- \$ ?3 D3 u
+ f( x( s7 ^' M
1 o& p2 N3 L! e; L( @- i6、定义多级子菜单(Container Schema Object)! a* b; l( i% `
第5和6步都是添加一个菜单项,下面尝试添加一个包含子菜单的项目试试。这个子菜单叫做Container Schema Object,使用sl_container_schema进行实例创建。Container Schema Object和前面两个对象属性基本相似,包含两个特殊属性:, k9 Q" H2 A2 N2 O4 h1 k
9 ]8 ?( g; |: z6 g3 I
(1)childreNFCns V) Y6 j- u/ Y" M9 e7 i. ~1 c
Cell数组,指定创建子菜单函数的列表,等同于第2步中MenuWeWantAdd()的返回值。可以使用'separator'指定菜单之间的分割线。/ C- ?! O2 R$ x, \
- % 创建一个schema对象的实例,用于定义新增菜单的内容
- schema=sl_container_schema; % 注意这里是一个container
- % 子菜单创建函数,使用'separator'添加分割线
- schema.childrenFcns={
- @CreateSubMenu1 % 子菜单创建函数1
- 'separator' % 菜单之间的分隔符,快乐的分割线
- @CreateSubMenu2}; % 子菜单创建函数2
0 P8 a W7 h2 h, H& T
8 h$ e* w( A9 y/ Z L2 d( Y! C b) ~4 F/ j4 ?
(2)generateFcn' ^- p# i+ ]0 l7 l$ N- v0 V
函数句柄,相当于第2步中的@MenuWeWantAdd,该函数返回一个cell数组。
: ~- p6 n# l/ G- % 创建一个schema对象的实例,用于定义新增菜单的内容
- schema=sl_container_schema; % 注意这里是一个container
- % 子菜单生成函数,注意当设置了generateFcn时,自动屏蔽childrenFcns
- schema.generateFcn=@SubMenuWeWantAdd; % 返回生成子菜单的函数列表8 Q1 d3 f8 c8 W. d( G
F5 T5 z- r6 j
$ Y3 i R8 o+ e7 {, w请注意,generateFcn的优先权高于childrenFcns,当定义了generateFcn属性,那么childrenFcns属性自动被屏蔽。根据上面的说明,继续在sl_customization.m文件中添加如下代码:- M) d( z. r& y) {) @
- function schema=CreateNewMenu3(callbackInfo)
- % 用来创建NewMenu3菜单,必须接受一个callbackInfo输入参数
- % 创建一个schema对象的实例,用于定义新增菜单的内容
- schema=sl_container_schema; % 注意这里是一个container
- % 指定菜单的显示名称,必须
- schema.label = '新增Container菜单';
- % 注意包含子菜单的项目是没有Callback属性的
- % schema.callback = @NewMenu3Callback; % 该句无效
- % 子菜单创建函数
- schema.childrenFcns={
- @CreateSubMenu1 % 子菜单创建函数1
- 'separator' % 菜单之间的分隔符,快乐的分割线
- @CreateSubMenu2}; % 子菜单创建函数2
- % 子菜单生成函数,注意当设置了generateFcn时,自动屏蔽childrenFcns
- schema.generateFcn=@SubMenuWeWantAdd; % 返回生成子菜单的函数列表
- % 设置用户数据,非必须
- schema.userdata = '';
- function schemaFcns=SubMenuWeWantAdd(callbackInfo)
- schemaFcns={
- @CreateSubMenu3 % 新增子菜单函数
- };
- function schema=CreateSubMenu1(callbackInfo)
- schema=sl_action_schema;
- schema.label='新增子菜单1';
- schema.callback='msgbox(''新增子菜单1,被临幸了!'')';
- function schema=CreateSubMenu2(callbackInfo)
- schema=sl_action_schema;
- schema.label='新增子菜单2';
- schema.callback='msgbox(''新增子菜单2,被临幸了!'')';
- function schema=CreateSubMenu3(callbackInfo)
- schema=sl_action_schema;
- schema.label='新增子菜单3';
- schema.callback='msgbox(''新增子菜单3,被临幸了!'')';
- g( N& l: H7 l! E4 o z$ `1 f- ?5 M* W& b9 b
; p7 G1 c/ y0 q! V9 G7、让自定义立即菜单生效
$ l& s9 O' \* y好不容易编写好了上面的sl_customization.m,想立即看看效果,不过很惋惜的告诉您,您必须重启MATLAB,否不会生效。不过也可以在Command Windows中执行以下命令:/ F$ l+ L& ~5 q0 r
- sl_refresh_customizations
) {+ ~9 [. F3 [' Q1 e! D4 p% `; X
2 c* ]( u* ?* Z9 x Q' v4 w0 G; m% q* \- w
至于效果可以查看本文最头部的图片!
, A- Q4 {0 v9 {) S3 `& |0 ?: Z
$ z9 A7 l" X2 {5 f" L! n请注意,在Window系统中,在sl_customization中设置断点,那么Simulink将不会执行设置断点的函数,因此如果想调试sl_customization文件,请使用命令行的形式,比如dbstop,千万不要在编辑器中直接设置断点。& `% s" |, q& }
2 w9 z" B. O2 ]7 @
@4 s; O3 h' h5 ~, r
|
|