|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
) l% q/ v {$ G/ E8 z9 ?! ?2 S" W写这个帖子主要是因为之前恒润科技给单位开发一个工具软件,需要和Simulink深度结合。但是在提需求的时候,他们坚定的说不能自定义Simulink配置环境(换句话说就是不能和Simulink整合,只能提供单独的工具),且已经咨询Mathworks,说没法在Simulink中添加自定义菜单等。' {* e7 b+ ?3 J' M! v
; R' H! q: H1 A' L$ c, i6 S
先预览下效果图,给大家一个感性的认识:
1 I5 l) y$ N: p0 X
. a0 e4 I* ?) T0 T: S另外这里提供本文用到的sl_customization函数2 h8 v+ f7 u( `0 y: a
% L( W3 \ x+ Q' u8 z+ T# j: d" \. o其实本文中涉及的技术,可以在Simulink的公开文档轻易找到,只是大家没有注意而已!另外本文中讨论的技术,不仅仅可以修改Simulink的菜单,还可以修改Simulink中很多环境配置,比如对话框控制、库浏览器、Simulink首选项等很多方面。本文仅以自定义Simulink菜单做抛砖引玉的作用!( @$ F8 Y. h1 L7 r. |3 `. T8 g
- O: o. t8 Q4 D+ \" a在Simulink编辑器和Stateflow编辑中,可以在以下列表中添加菜单或命令:
4 x5 t' ~4 O) \+ O* q6 E6 S( S E2 {; \; g$ V
- 顶级菜单的尾部
- 右键菜单的首部或尾部
- 工具条/ q/ c* `8 ]3 v6 b
$ M: Z5 k0 p+ }5 c+ f, I- g+ G
1、注册用户界面自定义(Registering User InteRFace Customizations)4 w; s* g+ v% ^7 F6 j/ l% j. a! l/ x
首先在MATLAB的搜索路径下,添加一个sl_customization()函数,简单说,此函数就是告诉Simulink您希望对Simulink环境做哪些修改。function sl_customization()函数接受一个输入参数cm,cm是一个自定义管理对象( customization manager object)。cm对象中内置了一些进行环境自定义的方法。启动Simulink的时候,程序会自动加载sl_customization文件。
) m/ }, r4 p( ]1 P& @- function sl_customization(cm)
- % 用于自定义Simulink环境配置
, u3 }( [% @9 C0 j& a 7 O. ?. _; r- z
4 Z) K7 ]+ j: K/ H8 a
2、添加自定义菜单函数(addCustomMenuFcn)
3 H' x I0 R! Q! s7 U* H! F8 [定制菜单主要用到cm对象的addCustomMenuFcn和addCustomFilterFcn两个方法。0 C: E( A4 e- _7 s/ o6 Q5 p
8 ^# k; d4 K/ s% D8 |(1)addCustomMenuFcn(stdMenuTag, menuSpecsFcn)方法。此方法的主要功能是,将menuSpecsFcn中自定义的条目添加到stdMenuTag菜单项目下,主要参数意义如下:
6 s- W. P. m# \) l& n- E- menuSpecsFcn:函数句柄,返回新增菜单项的创建函数,具体请看后面的例子。
- stdMenuTag:字符串,指定需要进行修改的Simulink菜单。比如,希望在Simulink的Edit菜单下添加其它内容,那么stdMenuTag='Simulink:EditMenu',这个时候有人估计会疑问,既然Edit菜单stdMenuTag都那么复杂,那怎么知道其它菜单的stdMenuTag?其实很简单,在CommandWindows中执行如下命令
B1 ?$ q: o* p+ `# a: `
- cm = sl_customization_manager;
- cm.showWidgetIdAsToolTip=true;
1 _& H& k1 ^& _2 P& X( j" m+ I " H) l4 n2 y& H
3 o* }( x; y' Y+ l4 s) H. l: [
此时Simulink界面将变成如下样子,在原有菜单旁边会自动显示Simulink已有菜单的stdMenuTag
' c) r$ \( S7 {6 H5 N( @
: Y* B, Q; c. n1 l+ }
( P$ t" M8 e$ q/ P, o& Y1 o7 h/ ^+ a, r( C* O5 Q( b/ e- [
当您不需要显示那个stdMenuTag时,在命令窗口执行% a/ c- q' Z& p# a! I9 n
- cm.showWidgetIdAsToolTip=false;2 N5 s/ P1 H1 Z5 i
' W/ ?* j' z" ?' ~
(1)addCustomFilterFcn(stdMenuItemID, filterFcn)方法。该方法主要是用于,禁用或激活stdMenuItemID菜单。本文不大算详细介绍,感兴趣的朋友可以看看帮助文档。* k3 E# C* y/ X r; T
, r+ |. ^; p0 r A, _因此文件中的代码重新修改如下:
/ k" v5 G/ D( p: L/ K- function sl_customization(cm)
- % 用于自定义Simulink环境配置
- % 告诉Simulink根据MenuWeWantAdd()返回参数中指定的函数
- % 在'Simulink:EditMenu'菜单下添加新建菜单
- cm.addCustomMenuFcn('Simulink:EditMenu',@MenuWeWantAdd)2 S, ^* @2 [/ k2 A5 p, D* j: H
l, O3 @+ g+ ~& U/ `7 b! p. P/ {
, C# K0 z) ]. _/ N$ q
3、定义新增菜单创建函数(Define Menu Create Function)" h& b" A0 |1 U+ o) s* H) e& V) U
在第二步中,告诉Simulink根据MenuWeWantAdd()返回参数中指定的函数来创建新菜单项,但是到底是使用哪些函数以及添加哪些菜单我们还米有定义。因此在sl_customization.m文件中继续添加如下代码:
: \) z$ c0 b _- function schemaFcns=MenuWeWantAdd(callbackInfo)
- % 返回用于创建新增菜单的函数,必须接受一个callbackInfo输入参数
- % 告诉Simulink分别使用下面的函数,来创建每个新增菜单项
- schemaFcns={...
- @CreateNewMenu1 % 新增第1个菜单的函数
- @CreateNewMenu2 % 新增第2个菜单的函数
- @CreateNewMenu3}; % 新增第3个菜单的函数3 I, K" O1 K( u; Z5 l4 h
5 N- |. \: F0 t7 f) Q+ S4 N9 h9 [: T
* w" P+ G- B) W' b( x, Y/ d% `其中MenuWeWantAdd()函数就是告诉Simulink通过哪些函数来创建菜单。/ \# C6 V3 P {8 I1 q }
' f/ I2 [5 o0 j& D8 q" I其中的输入参数callbackInfo,是一个Callback Info对象,包含以下几个属性
6 P# t z# u' b+ P4 W( A$ R7 ]: w9 m' w/ a
* u( z h# u/ t, p4 W# d _- l: {
| 属性 | 说明 | | uiObject | 句柄,被点击菜单的父对象,Simulink编辑器或Stateflow编辑器 | | model | 句柄,当前编辑器中显示的Simulink模型 | | userdata | | ) Z2 a$ p* }9 C4 d$ ]
' z4 f: u' i6 m/ @! R% d
- N0 j5 E" z2 K
4、定义新增Action菜单项(Define Action Menu Items)
. Y; d2 n* ^# Z Y: n虽然现在已经知道使用CreateNewMenu1()等函数来创建自定义菜单,但到底是什么菜单呢?,因此需要继续在sl_customization.m文件中添加如下代码:/ @" b; w7 ?9 N) o1 n
- 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菜单,被临幸了!')
. H0 {. N" C5 ~% t4 K6 A+ a
( }5 \/ ^3 t2 F: p1 a, r t; W* c+ \) I+ s5 i5 p; X
Action Schema对象有以下属性0 T7 J8 x1 s* V& ]6 @
7 ~9 t5 K8 r5 z
| 属性 | 取值 | 说明 | | tag | 字符串,可选 | 菜单的唯一标识符,类似于Simulink:EditMenu的作用 | | label | 字符串,必须 | 菜单的显示名称 | | state | 字符串,可选,只能以下三个取值 'Enabled'(默认)、'Disabled'、'Hidden' | | | statustip | 字符串,可选 | 当鼠标移动到菜单上时,状态栏显示的提示文本 | | userdata | 任意数据,可选 | 用户之定义数据 | | accelerator | 字符串,可选,例如,'CTR+K' | 菜单快捷键 | | callback | 字符串或函数句柄,必须 | 点击菜单时的回调函数 | | autoDisableWhen | 字符串,可选,只能以下三个取值'Locked'(默认)、'Busy'、'Never' | 什么时候自动禁用菜单 | * R4 e; k; i7 J& B7 k, o9 P' {$ b
4 }4 c+ E4 j1 z6 Y8 w/ J5、添加Toggole菜单按钮(Toggle Schema Object)
4 A" z4 n3 U/ z其实在第4步中定义的是Action Schema Object,也是点击菜单,当点击菜单时响应相应的回调,但是菜单的形状不发生变化。而Toggle Schema Object在点击以后会发生形状变化,比如点击以后外形会凹陷下去,或者在菜单前面有一个√。
4 s( V. k# o6 S: T7 g: W, ?, c* p7 [# u
Toggle Schema Object和Action Schema Object的属性基本一致,只是Toggle对象多了一个checked属性,当摁下时,checked=='on',否则checked=='off'(默认)。另外Toggle对象是使用sl_toggle_schema创建实例的。+ k% {" `9 f6 |9 F: ^
- 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菜单,被临幸了!')$ d5 K9 k# N1 j( c
% u9 @3 t( ?( d' d+ c Y
1 v$ J) A! O- d# H
6、定义多级子菜单(Container Schema Object)' ^4 i; L+ M1 T1 O" G
第5和6步都是添加一个菜单项,下面尝试添加一个包含子菜单的项目试试。这个子菜单叫做Container Schema Object,使用sl_container_schema进行实例创建。Container Schema Object和前面两个对象属性基本相似,包含两个特殊属性:
8 G4 o) H3 ~6 B2 V2 C0 d' i
4 E! {3 k, |( J. p A2 i(1)childreNFCns
+ I- t3 v6 E# B$ }! HCell数组,指定创建子菜单函数的列表,等同于第2步中MenuWeWantAdd()的返回值。可以使用'separator'指定菜单之间的分割线。
; @3 c. r2 v+ ^+ w9 w" h- % 创建一个schema对象的实例,用于定义新增菜单的内容
- schema=sl_container_schema; % 注意这里是一个container
- % 子菜单创建函数,使用'separator'添加分割线
- schema.childrenFcns={
- @CreateSubMenu1 % 子菜单创建函数1
- 'separator' % 菜单之间的分隔符,快乐的分割线
- @CreateSubMenu2}; % 子菜单创建函数2& ^/ ^) n5 ^1 }$ k. s
+ ]" j1 \4 @* F) T
# W# C" ]% c$ }" {8 W(2)generateFcn& _$ [# U& ^. ~" g
函数句柄,相当于第2步中的@MenuWeWantAdd,该函数返回一个cell数组。
# n8 `) c( t7 M. x2 W% z5 Y- % 创建一个schema对象的实例,用于定义新增菜单的内容
- schema=sl_container_schema; % 注意这里是一个container
- % 子菜单生成函数,注意当设置了generateFcn时,自动屏蔽childrenFcns
- schema.generateFcn=@SubMenuWeWantAdd; % 返回生成子菜单的函数列表
+ S8 T( V5 C$ z0 ]% X+ e: e7 w
S# L7 M) m/ r% [ N9 b5 x) P7 L0 J. z* T' p% ?
请注意,generateFcn的优先权高于childrenFcns,当定义了generateFcn属性,那么childrenFcns属性自动被屏蔽。根据上面的说明,继续在sl_customization.m文件中添加如下代码:7 c$ e# _5 R6 l7 B0 C8 {
- 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,被临幸了!'')';2 O+ X8 Y8 O6 u" K# c% |. U, [9 X! Z
. I: R$ I( p2 l# S! m; ~$ o6 ?
! j5 Q3 U4 X4 B$ s7 D7、让自定义立即菜单生效! q- {- [! h1 u3 s% z) v- n
好不容易编写好了上面的sl_customization.m,想立即看看效果,不过很惋惜的告诉您,您必须重启MATLAB,否不会生效。不过也可以在Command Windows中执行以下命令:
; d0 ?. g9 z; U$ C- sl_refresh_customizations* U' _# a' `, |: j5 o
* ~8 _5 W, i# F! D% r3 o5 O, @- m$ `* W2 m
至于效果可以查看本文最头部的图片!+ ?8 s/ Z6 D! O5 V7 ~. x
/ z" V. N- [6 P8 ], G4 [4 t请注意,在Window系统中,在sl_customization中设置断点,那么Simulink将不会执行设置断点的函数,因此如果想调试sl_customization文件,请使用命令行的形式,比如dbstop,千万不要在编辑器中直接设置断点。
! y* Y, c. F+ w C2 k2 G& U% C8 J
& f( H4 h% s/ v/ M |
|