|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
+ A" @7 A$ S( n6 }/ ]7 F1 C* j
写这个帖子主要是因为之前恒润科技给单位开发一个工具软件,需要和Simulink深度结合。但是在提需求的时候,他们坚定的说不能自定义Simulink配置环境(换句话说就是不能和Simulink整合,只能提供单独的工具),且已经咨询Mathworks,说没法在Simulink中添加自定义菜单等。
g0 N |( a1 \4 j! B, M! I. @4 r8 ^; {$ `( `$ G9 \3 j
先预览下效果图,给大家一个感性的认识:
; i1 X2 k$ ~: k, I1 |
$ O6 E, C7 U: L M5 J0 D4 E) A( i另外这里提供本文用到的sl_customization函数
! Y& V# n+ S* u, V3 D4 I; X8 F2 C8 r3 C+ U$ e. H f
其实本文中涉及的技术,可以在Simulink的公开文档轻易找到,只是大家没有注意而已!另外本文中讨论的技术,不仅仅可以修改Simulink的菜单,还可以修改Simulink中很多环境配置,比如对话框控制、库浏览器、Simulink首选项等很多方面。本文仅以自定义Simulink菜单做抛砖引玉的作用!/ r. E1 P, ~3 d, d; z& Y
4 u0 q F: f2 |3 G$ `% N6 f( B在Simulink编辑器和Stateflow编辑中,可以在以下列表中添加菜单或命令:
' W8 O- `+ Q/ j5 B% S" Z$ P8 }8 C5 }5 f/ d0 h- y! r( N
- 顶级菜单的尾部
- 右键菜单的首部或尾部
- 工具条
2 @0 q/ ^2 C1 r' T7 Q& Y+ e" _
3 D$ {# a3 a4 s1、注册用户界面自定义(Registering User InteRFace Customizations)
0 n2 l; R' S" X4 I) I" d首先在MATLAB的搜索路径下,添加一个sl_customization()函数,简单说,此函数就是告诉Simulink您希望对Simulink环境做哪些修改。function sl_customization()函数接受一个输入参数cm,cm是一个自定义管理对象( customization manager object)。cm对象中内置了一些进行环境自定义的方法。启动Simulink的时候,程序会自动加载sl_customization文件。 J& o' g* F) E+ `" m5 h/ z0 M& i
- function sl_customization(cm)
- % 用于自定义Simulink环境配置) |8 V3 T6 i3 m
. S3 S3 }& O% |2 P$ h) G
9 B* Q, v7 _( P1 Y3 K+ g2 {& O2、添加自定义菜单函数(addCustomMenuFcn)' w0 e( d" @3 p) G9 L
定制菜单主要用到cm对象的addCustomMenuFcn和addCustomFilterFcn两个方法。) F/ j7 |+ \ @) T" i8 S5 ~# N
& @# n$ |& {3 ?: I# ?! i6 T(1)addCustomMenuFcn(stdMenuTag, menuSpecsFcn)方法。此方法的主要功能是,将menuSpecsFcn中自定义的条目添加到stdMenuTag菜单项目下,主要参数意义如下:
n2 C) x- a, S7 h- menuSpecsFcn:函数句柄,返回新增菜单项的创建函数,具体请看后面的例子。
- stdMenuTag:字符串,指定需要进行修改的Simulink菜单。比如,希望在Simulink的Edit菜单下添加其它内容,那么stdMenuTag='Simulink:EditMenu',这个时候有人估计会疑问,既然Edit菜单stdMenuTag都那么复杂,那怎么知道其它菜单的stdMenuTag?其实很简单,在CommandWindows中执行如下命令
1 t# Q/ r6 J$ n: t' |
- cm = sl_customization_manager;
- cm.showWidgetIdAsToolTip=true;
& P5 ^" O3 h; v$ @2 z9 }+ _
$ s E; o6 d9 W: i ?
/ J7 d; {9 J6 f/ O5 ^此时Simulink界面将变成如下样子,在原有菜单旁边会自动显示Simulink已有菜单的stdMenuTag
! F9 P3 A! s( l. T9 T6 _9 u7 S6 L+ h8 r/ ~8 N1 Q7 d- w8 P; X
/ M5 M2 t3 \$ A+ t6 ?, J c
. C: [! W1 }' [/ U) S: z6 e3 k$ O当您不需要显示那个stdMenuTag时,在命令窗口执行( b+ r5 ]1 L4 `- Z: D/ ]
- cm.showWidgetIdAsToolTip=false;
4 w/ K/ G4 k$ i- C
8 ^/ P* E4 k3 c9 @' t* [# [(1)addCustomFilterFcn(stdMenuItemID, filterFcn)方法。该方法主要是用于,禁用或激活stdMenuItemID菜单。本文不大算详细介绍,感兴趣的朋友可以看看帮助文档。
( H" l' K! A9 x z
: M4 f# h' p [9 K R因此文件中的代码重新修改如下:
# e8 {! N" y; L: l0 t% @( d% b- function sl_customization(cm)
- % 用于自定义Simulink环境配置
- % 告诉Simulink根据MenuWeWantAdd()返回参数中指定的函数
- % 在'Simulink:EditMenu'菜单下添加新建菜单
- cm.addCustomMenuFcn('Simulink:EditMenu',@MenuWeWantAdd)! [9 a7 P4 r! z
" g3 w0 `6 w3 W0 k) J; h" K r# T, h
3、定义新增菜单创建函数(Define Menu Create Function)
7 u( N: T) ]6 B" E在第二步中,告诉Simulink根据MenuWeWantAdd()返回参数中指定的函数来创建新菜单项,但是到底是使用哪些函数以及添加哪些菜单我们还米有定义。因此在sl_customization.m文件中继续添加如下代码:' o8 O8 Q8 `4 c6 f: [
- function schemaFcns=MenuWeWantAdd(callbackInfo)
- % 返回用于创建新增菜单的函数,必须接受一个callbackInfo输入参数
- % 告诉Simulink分别使用下面的函数,来创建每个新增菜单项
- schemaFcns={...
- @CreateNewMenu1 % 新增第1个菜单的函数
- @CreateNewMenu2 % 新增第2个菜单的函数
- @CreateNewMenu3}; % 新增第3个菜单的函数
$ g5 A7 J! |# C- n5 { 3 E) V8 O( e; F' Z0 P9 l# D. u
( _7 L6 B3 L; y$ _( B; T& Q
其中MenuWeWantAdd()函数就是告诉Simulink通过哪些函数来创建菜单。
7 j3 T* ` g2 G7 t c
0 I w( v6 I8 R$ Y+ W. L其中的输入参数callbackInfo,是一个Callback Info对象,包含以下几个属性$ `! M- I1 A S( b6 T' X
& v5 S+ B/ ]' q* G: e3 o7 n a0 g1 i. T- N9 O* J$ i- k* t, w7 X
属性 | 说明 | uiObject | 句柄,被点击菜单的父对象,Simulink编辑器或Stateflow编辑器 | model | 句柄,当前编辑器中显示的Simulink模型 | userdata | |
/ ^& t$ F% X$ G3 G
) d* |! b! j/ r : ^! ?+ S0 _5 S. U
4、定义新增Action菜单项(Define Action Menu Items)
/ U0 ?: t" \7 ]+ V( y虽然现在已经知道使用CreateNewMenu1()等函数来创建自定义菜单,但到底是什么菜单呢?,因此需要继续在sl_customization.m文件中添加如下代码:6 W' F2 i: Z0 F8 B; ^4 i
- 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菜单,被临幸了!')& e" c0 \; r) v, H# s" I1 n
% i) y! a% ?9 l# D# U% T4 Z6 ~$ c
Action Schema对象有以下属性
+ j" @* |; n' {+ X4 P( W, V) e/ ?- o# L& N
属性 | 取值 | 说明 | tag | 字符串,可选 | 菜单的唯一标识符,类似于Simulink:EditMenu的作用 | label | 字符串,必须 | 菜单的显示名称 | state | 字符串,可选,只能以下三个取值 'Enabled'(默认)、'Disabled'、'Hidden' | | statustip | 字符串,可选 | 当鼠标移动到菜单上时,状态栏显示的提示文本 | userdata | 任意数据,可选 | 用户之定义数据 | accelerator | 字符串,可选,例如,'CTR+K' | 菜单快捷键 | callback | 字符串或函数句柄,必须 | 点击菜单时的回调函数 | autoDisableWhen | 字符串,可选,只能以下三个取值'Locked'(默认)、'Busy'、'Never' | 什么时候自动禁用菜单 | 8 q$ b% [" _9 `
- p9 N( d5 a' q) w8 P. p
5、添加Toggole菜单按钮(Toggle Schema Object)
! s+ F6 B* Q5 B3 y( }1 n! i其实在第4步中定义的是Action Schema Object,也是点击菜单,当点击菜单时响应相应的回调,但是菜单的形状不发生变化。而Toggle Schema Object在点击以后会发生形状变化,比如点击以后外形会凹陷下去,或者在菜单前面有一个√。9 p+ {! n6 C$ i5 a
0 Z+ {- L& x4 Z+ t
Toggle Schema Object和Action Schema Object的属性基本一致,只是Toggle对象多了一个checked属性,当摁下时,checked=='on',否则checked=='off'(默认)。另外Toggle对象是使用sl_toggle_schema创建实例的。
' M3 c. ]' E. S9 F! 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菜单,被临幸了!')
7 h4 I4 C0 n& T e* p+ y! ^$ Q , Z @$ Q, J7 f
B% @4 Q* H2 ?1 S) t6、定义多级子菜单(Container Schema Object)
# T# d/ ~2 q6 R: n6 Q第5和6步都是添加一个菜单项,下面尝试添加一个包含子菜单的项目试试。这个子菜单叫做Container Schema Object,使用sl_container_schema进行实例创建。Container Schema Object和前面两个对象属性基本相似,包含两个特殊属性:
0 H4 K% s& A. E, R5 j
( W$ c5 X7 Z$ t(1)childreNFCns2 @' a- s7 f# _5 m2 ~$ N2 E
Cell数组,指定创建子菜单函数的列表,等同于第2步中MenuWeWantAdd()的返回值。可以使用'separator'指定菜单之间的分割线。
$ N2 F" u8 z! _' w- % 创建一个schema对象的实例,用于定义新增菜单的内容
- schema=sl_container_schema; % 注意这里是一个container
- % 子菜单创建函数,使用'separator'添加分割线
- schema.childrenFcns={
- @CreateSubMenu1 % 子菜单创建函数1
- 'separator' % 菜单之间的分隔符,快乐的分割线
- @CreateSubMenu2}; % 子菜单创建函数2 }- i m. F( [, A( ?! @
' T6 \, X! g/ F3 U% N; m
7 y9 y/ O8 f, v5 O% k(2)generateFcn
/ L8 C/ Y8 g5 A# R函数句柄,相当于第2步中的@MenuWeWantAdd,该函数返回一个cell数组。/ h# b, X# b* {2 k
- % 创建一个schema对象的实例,用于定义新增菜单的内容
- schema=sl_container_schema; % 注意这里是一个container
- % 子菜单生成函数,注意当设置了generateFcn时,自动屏蔽childrenFcns
- schema.generateFcn=@SubMenuWeWantAdd; % 返回生成子菜单的函数列表
0 r6 V. c! I# d, r& ]: d ' |+ }- _4 Z6 W* d1 W# v
3 Y# y* I3 n* U- w; N Q. g请注意,generateFcn的优先权高于childrenFcns,当定义了generateFcn属性,那么childrenFcns属性自动被屏蔽。根据上面的说明,继续在sl_customization.m文件中添加如下代码:$ ^# D2 ~3 G- l) R
- 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,被临幸了!'')';) K/ R1 V4 N2 O" U
) L* E( F0 w$ g
, y3 t. w Q N- d& v7、让自定义立即菜单生效" N% r" j8 W/ ]" v8 N; A+ k
好不容易编写好了上面的sl_customization.m,想立即看看效果,不过很惋惜的告诉您,您必须重启MATLAB,否不会生效。不过也可以在Command Windows中执行以下命令:2 H0 s% i9 L. e
- sl_refresh_customizations
9 C: B4 S# {4 Y6 ~; W ) q. e% } W$ l( [
/ k6 r# \6 \" i3 `8 U9 M* }至于效果可以查看本文最头部的图片!
2 v ^+ N5 S) Q' y) @: ^ K; O
" C& G7 v6 T2 G: P$ Y k5 x请注意,在Window系统中,在sl_customization中设置断点,那么Simulink将不会执行设置断点的函数,因此如果想调试sl_customization文件,请使用命令行的形式,比如dbstop,千万不要在编辑器中直接设置断点。
" D' S: q6 C# z+ Z+ B
0 M6 H% R- H1 {( j8 p: p" k! U) n1 R
|
|