|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
& @4 @' f L4 d. Z/ a& N& E4 b
写这个帖子主要是因为之前恒润科技给单位开发一个工具软件,需要和Simulink深度结合。但是在提需求的时候,他们坚定的说不能自定义Simulink配置环境(换句话说就是不能和Simulink整合,只能提供单独的工具),且已经咨询Mathworks,说没法在Simulink中添加自定义菜单等。( p0 ]6 j/ q9 q' z
3 G) R4 S7 c" I4 r先预览下效果图,给大家一个感性的认识:
5 M7 b" a. f, C% F4 e2 d# q
. J5 j# v% @$ T/ c- l另外这里提供本文用到的sl_customization函数- K3 @5 F% b/ w; h$ ^
2 D6 S% L% B( p% N( r a其实本文中涉及的技术,可以在Simulink的公开文档轻易找到,只是大家没有注意而已!另外本文中讨论的技术,不仅仅可以修改Simulink的菜单,还可以修改Simulink中很多环境配置,比如对话框控制、库浏览器、Simulink首选项等很多方面。本文仅以自定义Simulink菜单做抛砖引玉的作用!
* a+ E2 Y! m4 N% w5 Z0 ^
' Z& M P( Q3 \5 G/ h9 B7 b在Simulink编辑器和Stateflow编辑中,可以在以下列表中添加菜单或命令:
9 S6 Z2 {3 E1 \; D; F8 `
: F6 p* j3 F6 v) K- 顶级菜单的尾部
- 右键菜单的首部或尾部
- 工具条
) x1 Z5 w E. a0 _ & @! `3 P( \& ?# D+ Y7 {" j
1、注册用户界面自定义(Registering User InteRFace Customizations)% A/ h$ K% O- L5 l7 S
首先在MATLAB的搜索路径下,添加一个sl_customization()函数,简单说,此函数就是告诉Simulink您希望对Simulink环境做哪些修改。function sl_customization()函数接受一个输入参数cm,cm是一个自定义管理对象( customization manager object)。cm对象中内置了一些进行环境自定义的方法。启动Simulink的时候,程序会自动加载sl_customization文件。, [0 S; Q2 l- F7 C0 N; ?" B2 ^
- function sl_customization(cm)
- % 用于自定义Simulink环境配置
' m ]$ o6 G* L
8 x5 m0 t) P% w6 A* I" {5 p) P$ D3 j8 s. ]: L3 P0 ^) B
2、添加自定义菜单函数(addCustomMenuFcn)! t% ~5 T: K$ ?$ i; u9 P# r
定制菜单主要用到cm对象的addCustomMenuFcn和addCustomFilterFcn两个方法。5 `+ `3 ]9 x* I. T- c
& g) @, F4 r6 q8 k) }. r1 N(1)addCustomMenuFcn(stdMenuTag, menuSpecsFcn)方法。此方法的主要功能是,将menuSpecsFcn中自定义的条目添加到stdMenuTag菜单项目下,主要参数意义如下:
6 h N4 Y0 G: e* a! \$ N- menuSpecsFcn:函数句柄,返回新增菜单项的创建函数,具体请看后面的例子。
- stdMenuTag:字符串,指定需要进行修改的Simulink菜单。比如,希望在Simulink的Edit菜单下添加其它内容,那么stdMenuTag='Simulink:EditMenu',这个时候有人估计会疑问,既然Edit菜单stdMenuTag都那么复杂,那怎么知道其它菜单的stdMenuTag?其实很简单,在CommandWindows中执行如下命令
3 i; h; X& w; s; Q$ X1 k
- cm = sl_customization_manager;
- cm.showWidgetIdAsToolTip=true;
$ g/ u$ G2 H* G z 0 [! d) ^3 R' k3 a5 r
2 \( a9 A3 q) J* A7 g# v- M4 H) A6 x: T
此时Simulink界面将变成如下样子,在原有菜单旁边会自动显示Simulink已有菜单的stdMenuTag
" Z- j$ B" X$ [0 Y
3 o5 G2 B. \* u S+ J: R. h# f
9 `9 B( K7 U8 X1 Z* `, m: Y3 z7 e7 |( A5 o# a+ f! z+ i
当您不需要显示那个stdMenuTag时,在命令窗口执行
" }( _# ?. \3 Q1 o5 E/ x( C- cm.showWidgetIdAsToolTip=false;
/ e1 x5 s- X, d' B2 q4 c: {0 y0 [5 q' n
: m* d2 z! L: Y" Q5 N(1)addCustomFilterFcn(stdMenuItemID, filterFcn)方法。该方法主要是用于,禁用或激活stdMenuItemID菜单。本文不大算详细介绍,感兴趣的朋友可以看看帮助文档。' J2 @% D S g, @% v5 M1 T/ J
- ~' Q5 P2 x- i因此文件中的代码重新修改如下:3 w/ w* Q& Y/ _" {' J, b- j& b
- function sl_customization(cm)
- % 用于自定义Simulink环境配置
- % 告诉Simulink根据MenuWeWantAdd()返回参数中指定的函数
- % 在'Simulink:EditMenu'菜单下添加新建菜单
- cm.addCustomMenuFcn('Simulink:EditMenu',@MenuWeWantAdd), `- K; z; Y* Q8 n; |
) l, x" W; i$ N8 W* i2 n3 S1 Q0 d4 A/ w% @# ~8 |" P$ a
3、定义新增菜单创建函数(Define Menu Create Function)
$ z; C6 M7 d- p9 o3 r) t在第二步中,告诉Simulink根据MenuWeWantAdd()返回参数中指定的函数来创建新菜单项,但是到底是使用哪些函数以及添加哪些菜单我们还米有定义。因此在sl_customization.m文件中继续添加如下代码:
) [/ D8 O" S# v- function schemaFcns=MenuWeWantAdd(callbackInfo)
- % 返回用于创建新增菜单的函数,必须接受一个callbackInfo输入参数
- % 告诉Simulink分别使用下面的函数,来创建每个新增菜单项
- schemaFcns={...
- @CreateNewMenu1 % 新增第1个菜单的函数
- @CreateNewMenu2 % 新增第2个菜单的函数
- @CreateNewMenu3}; % 新增第3个菜单的函数
% R0 [# o' y2 t
* l9 I; T! v/ h$ w: j' u Z( c7 Y- X$ t" l7 x
其中MenuWeWantAdd()函数就是告诉Simulink通过哪些函数来创建菜单。
^3 A5 g5 k, X+ i; O6 d, d2 m; @3 g% ~
其中的输入参数callbackInfo,是一个Callback Info对象,包含以下几个属性
% S; w; ~7 x' {+ @ q5 j! J* t! N: ]$ T v/ y, e$ y
9 Y& F6 E7 w: |" l, ]+ ^& v8 E. F H# Z
属性 | 说明 | uiObject | 句柄,被点击菜单的父对象,Simulink编辑器或Stateflow编辑器 | model | 句柄,当前编辑器中显示的Simulink模型 | userdata | | % P" H* _+ V" \4 M
* w Z9 `! \4 \! U6 Y2 K+ K
5 r5 ?' {( Z2 Y/ V& O M4、定义新增Action菜单项(Define Action Menu Items)( K% @! g8 x! P$ I1 ?7 c2 c
虽然现在已经知道使用CreateNewMenu1()等函数来创建自定义菜单,但到底是什么菜单呢?,因此需要继续在sl_customization.m文件中添加如下代码:* f! `1 X# _4 e* X, `
- 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菜单,被临幸了!')9 }6 h- Z7 y0 R0 w, }# q3 L
9 ? B) ^1 i0 P4 p0 P, M8 g+ y- o# A) y/ Y& j' h. J4 ?
Action Schema对象有以下属性4 y7 O/ W7 R% D0 q8 z- t/ ~+ W. E
( i% ^6 e' N, `4 M" Y! H. V属性 | 取值 | 说明 | tag | 字符串,可选 | 菜单的唯一标识符,类似于Simulink:EditMenu的作用 | label | 字符串,必须 | 菜单的显示名称 | state | 字符串,可选,只能以下三个取值 'Enabled'(默认)、'Disabled'、'Hidden' | | statustip | 字符串,可选 | 当鼠标移动到菜单上时,状态栏显示的提示文本 | userdata | 任意数据,可选 | 用户之定义数据 | accelerator | 字符串,可选,例如,'CTR+K' | 菜单快捷键 | callback | 字符串或函数句柄,必须 | 点击菜单时的回调函数 | autoDisableWhen | 字符串,可选,只能以下三个取值'Locked'(默认)、'Busy'、'Never' | 什么时候自动禁用菜单 | , |) q; K# k5 e' i6 R7 j# K2 m
/ o, L& y5 p) F0 ]' |
5、添加Toggole菜单按钮(Toggle Schema Object)
# y f* u5 c+ w* Z! u! S其实在第4步中定义的是Action Schema Object,也是点击菜单,当点击菜单时响应相应的回调,但是菜单的形状不发生变化。而Toggle Schema Object在点击以后会发生形状变化,比如点击以后外形会凹陷下去,或者在菜单前面有一个√。: U0 A! ~2 @# B+ D
! V- n% P f" |" r0 j2 k
Toggle Schema Object和Action Schema Object的属性基本一致,只是Toggle对象多了一个checked属性,当摁下时,checked=='on',否则checked=='off'(默认)。另外Toggle对象是使用sl_toggle_schema创建实例的。4 }3 }( b- \' ^4 }
- 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菜单,被临幸了!')
5 f$ U$ o2 v) B A( p 2 ~/ `. H) S* L, @
9 |, R# g1 k2 c
6、定义多级子菜单(Container Schema Object) a5 D) B6 V; } a
第5和6步都是添加一个菜单项,下面尝试添加一个包含子菜单的项目试试。这个子菜单叫做Container Schema Object,使用sl_container_schema进行实例创建。Container Schema Object和前面两个对象属性基本相似,包含两个特殊属性:$ V* v6 m) L0 A. d0 Z/ ^; F
$ f2 |7 ?- q+ p5 ?; }( j+ o& D
(1)childreNFCns, c4 s6 j9 y& r- } ^, Q9 S0 k: h
Cell数组,指定创建子菜单函数的列表,等同于第2步中MenuWeWantAdd()的返回值。可以使用'separator'指定菜单之间的分割线。4 F/ t$ e/ o/ _ P
- % 创建一个schema对象的实例,用于定义新增菜单的内容
- schema=sl_container_schema; % 注意这里是一个container
- % 子菜单创建函数,使用'separator'添加分割线
- schema.childrenFcns={
- @CreateSubMenu1 % 子菜单创建函数1
- 'separator' % 菜单之间的分隔符,快乐的分割线
- @CreateSubMenu2}; % 子菜单创建函数2) Z0 F) y( ?2 A% X1 Q7 {. _
- J8 J" j; F, W4 r) c$ i
: z; Z, y$ g- o" [(2)generateFcn) F5 s; f. L: V* S# s- M
函数句柄,相当于第2步中的@MenuWeWantAdd,该函数返回一个cell数组。6 G% F" \+ u/ {/ y. f) p, f, _( I
- % 创建一个schema对象的实例,用于定义新增菜单的内容
- schema=sl_container_schema; % 注意这里是一个container
- % 子菜单生成函数,注意当设置了generateFcn时,自动屏蔽childrenFcns
- schema.generateFcn=@SubMenuWeWantAdd; % 返回生成子菜单的函数列表0 ?; c2 g4 U/ a& m+ U0 \
* I$ ?/ }6 y4 i K# d& G, f: c1 D4 ^ |4 Q" y
请注意,generateFcn的优先权高于childrenFcns,当定义了generateFcn属性,那么childrenFcns属性自动被屏蔽。根据上面的说明,继续在sl_customization.m文件中添加如下代码:7 y$ n& a% p7 N: U9 l3 Q
- 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,被临幸了!'')';
4 r# t2 t% M& f5 }
3 Y {1 o$ s! o# r9 f- ^& i$ O8 d, n+ I$ f
7、让自定义立即菜单生效
3 }. I: ?8 [# M0 D好不容易编写好了上面的sl_customization.m,想立即看看效果,不过很惋惜的告诉您,您必须重启MATLAB,否不会生效。不过也可以在Command Windows中执行以下命令:
2 _; q0 z. K& @; ~( J* ?- sl_refresh_customizations
- o2 [' F% h. b( e2 K' { f . y S: ^: V- |5 [5 H
' r9 `9 U0 U6 L& ?4 _5 j7 d
至于效果可以查看本文最头部的图片!
* |9 p8 {" \3 z3 I
" ?, A: K, V$ {9 n8 c+ j% l请注意,在Window系统中,在sl_customization中设置断点,那么Simulink将不会执行设置断点的函数,因此如果想调试sl_customization文件,请使用命令行的形式,比如dbstop,千万不要在编辑器中直接设置断点。
. H5 |5 x8 [& }8 z9 Q9 p# G
, R+ o' a" X7 z& ]1 @# c, i! D5 O
+ c l/ A7 q4 B |
|