|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
- d9 f" i4 h0 h+ F5 o. i写这个帖子主要是因为之前恒润科技给单位开发一个工具软件,需要和Simulink深度结合。但是在提需求的时候,他们坚定的说不能自定义Simulink配置环境(换句话说就是不能和Simulink整合,只能提供单独的工具),且已经咨询Mathworks,说没法在Simulink中添加自定义菜单等。) F7 z% R% v5 _* l
7 _4 g, F6 g) x8 n
先预览下效果图,给大家一个感性的认识:5 P: R4 T% o5 q- j4 O8 J
) ^* `4 \$ O; ^6 p另外这里提供本文用到的sl_customization函数( G5 D) D: m) a4 ~! ~( g3 D( W0 d
+ g ~: D7 [. T% [3 M& S6 p5 G其实本文中涉及的技术,可以在Simulink的公开文档轻易找到,只是大家没有注意而已!另外本文中讨论的技术,不仅仅可以修改Simulink的菜单,还可以修改Simulink中很多环境配置,比如对话框控制、库浏览器、Simulink首选项等很多方面。本文仅以自定义Simulink菜单做抛砖引玉的作用!' B2 U6 q/ x# d1 X- X
# Y" e, s5 m" A3 k+ ~6 T在Simulink编辑器和Stateflow编辑中,可以在以下列表中添加菜单或命令:# s& l( a/ n- l* s+ ~# G$ t
' _" r% W' [# Z2 H- 顶级菜单的尾部
- 右键菜单的首部或尾部
- 工具条3 S* V5 [& M3 f4 l9 j# k7 ?
8 V9 d1 E4 a" E1、注册用户界面自定义(Registering User InteRFace Customizations)
c$ w a/ v X3 G/ l+ ^首先在MATLAB的搜索路径下,添加一个sl_customization()函数,简单说,此函数就是告诉Simulink您希望对Simulink环境做哪些修改。function sl_customization()函数接受一个输入参数cm,cm是一个自定义管理对象( customization manager object)。cm对象中内置了一些进行环境自定义的方法。启动Simulink的时候,程序会自动加载sl_customization文件。
* ?0 Y3 w2 ^1 ~" {- j) ~# {4 R! w- function sl_customization(cm)
- % 用于自定义Simulink环境配置
6 H) V" R( ?' ?0 p/ N7 _$ U1 K
; ]! j$ @2 [2 d5 `5 \; p! ]1 w. L! i) y( ^
2、添加自定义菜单函数(addCustomMenuFcn)4 N* s2 O" }* {& F0 W3 ^0 y
定制菜单主要用到cm对象的addCustomMenuFcn和addCustomFilterFcn两个方法。0 R4 s2 n& n$ k W$ A z
: [/ c& }! k4 ^0 @- I(1)addCustomMenuFcn(stdMenuTag, menuSpecsFcn)方法。此方法的主要功能是,将menuSpecsFcn中自定义的条目添加到stdMenuTag菜单项目下,主要参数意义如下:% e$ `6 y) e* D8 M2 V; J |2 E) P
- menuSpecsFcn:函数句柄,返回新增菜单项的创建函数,具体请看后面的例子。
- stdMenuTag:字符串,指定需要进行修改的Simulink菜单。比如,希望在Simulink的Edit菜单下添加其它内容,那么stdMenuTag='Simulink:EditMenu',这个时候有人估计会疑问,既然Edit菜单stdMenuTag都那么复杂,那怎么知道其它菜单的stdMenuTag?其实很简单,在CommandWindows中执行如下命令* B9 v8 F, P% Y v6 n
- cm = sl_customization_manager;
- cm.showWidgetIdAsToolTip=true;
% H, ?) w# R% T
' R3 r8 I5 E* ^9 P5 K4 J8 R% b6 {2 K, i. d( o
此时Simulink界面将变成如下样子,在原有菜单旁边会自动显示Simulink已有菜单的stdMenuTag
) u6 I' q' W# e& S: f) {
* G: f( W$ |) j; o
8 I$ y$ f- M6 i! ~# g4 Y( P! Q; M
* O8 ]7 O$ s1 Y* `; \2 p5 N0 g* b5 |, N
当您不需要显示那个stdMenuTag时,在命令窗口执行4 u; e( ?( V, T4 e b4 q* S) r
- cm.showWidgetIdAsToolTip=false;; r, C8 a3 k# k+ x9 s' x6 C- C
9 }& U& r7 [$ A(1)addCustomFilterFcn(stdMenuItemID, filterFcn)方法。该方法主要是用于,禁用或激活stdMenuItemID菜单。本文不大算详细介绍,感兴趣的朋友可以看看帮助文档。 G# H, K w# e+ v5 j% y5 v
4 Z% I* Z. Q9 m, D! c) T
因此文件中的代码重新修改如下:
! Y$ f! u( U" m5 z* N [! {$ H1 b' j- function sl_customization(cm)
- % 用于自定义Simulink环境配置
- % 告诉Simulink根据MenuWeWantAdd()返回参数中指定的函数
- % 在'Simulink:EditMenu'菜单下添加新建菜单
- cm.addCustomMenuFcn('Simulink:EditMenu',@MenuWeWantAdd) S+ J) X8 n% |/ c* k8 H
% R( N6 e5 M: s f! J' ^' Q e" H6 Y' m/ u/ O: B
3、定义新增菜单创建函数(Define Menu Create Function)) k+ k' L2 J. n: r- F/ S3 A# k
在第二步中,告诉Simulink根据MenuWeWantAdd()返回参数中指定的函数来创建新菜单项,但是到底是使用哪些函数以及添加哪些菜单我们还米有定义。因此在sl_customization.m文件中继续添加如下代码:
: q% L" |/ H- v$ P2 ?+ f6 I- function schemaFcns=MenuWeWantAdd(callbackInfo)
- % 返回用于创建新增菜单的函数,必须接受一个callbackInfo输入参数
- % 告诉Simulink分别使用下面的函数,来创建每个新增菜单项
- schemaFcns={...
- @CreateNewMenu1 % 新增第1个菜单的函数
- @CreateNewMenu2 % 新增第2个菜单的函数
- @CreateNewMenu3}; % 新增第3个菜单的函数9 D: b2 C+ f: a$ V
% W2 [( G) J* @0 d; r6 f0 A: \7 ?
2 _/ M3 i" ^2 J' P; i0 W
其中MenuWeWantAdd()函数就是告诉Simulink通过哪些函数来创建菜单。6 O0 o: K) x0 W2 u/ p5 o9 C9 X
3 W# T' J! ~% M" j) N! J1 i4 U
其中的输入参数callbackInfo,是一个Callback Info对象,包含以下几个属性1 j: P: F: X1 F1 A9 f0 A( a
5 H; k+ P% y7 _: y9 \1 j; @' m: Z* p6 |$ D. `# v
属性 | 说明 | uiObject | 句柄,被点击菜单的父对象,Simulink编辑器或Stateflow编辑器 | model | 句柄,当前编辑器中显示的Simulink模型 | userdata | | 8 j# O5 D" V% J3 O+ k, ~
# P: M3 l' {. x2 Z; _0 J0 r
5 b- ?) ~$ ~/ _* ?5 X4、定义新增Action菜单项(Define Action Menu Items)
. \7 |* P; j* ]+ ]$ w虽然现在已经知道使用CreateNewMenu1()等函数来创建自定义菜单,但到底是什么菜单呢?,因此需要继续在sl_customization.m文件中添加如下代码:, J9 b/ Y7 c+ { r
- 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* z! f; Q+ a8 W9 v ( U' }7 R/ Q1 {+ S/ ^, H
3 M0 z# J, M/ g$ ?( l3 X& O
Action Schema对象有以下属性4 l# |1 y8 C- W( s; D" f0 r6 U
* O, i+ {- I9 l$ S% c# O属性 | 取值 | 说明 | tag | 字符串,可选 | 菜单的唯一标识符,类似于Simulink:EditMenu的作用 | label | 字符串,必须 | 菜单的显示名称 | state | 字符串,可选,只能以下三个取值 'Enabled'(默认)、'Disabled'、'Hidden' | | statustip | 字符串,可选 | 当鼠标移动到菜单上时,状态栏显示的提示文本 | userdata | 任意数据,可选 | 用户之定义数据 | accelerator | 字符串,可选,例如,'CTR+K' | 菜单快捷键 | callback | 字符串或函数句柄,必须 | 点击菜单时的回调函数 | autoDisableWhen | 字符串,可选,只能以下三个取值'Locked'(默认)、'Busy'、'Never' | 什么时候自动禁用菜单 | 6 \) i- h3 o( @5 o
5 a. D) B! e- O! Y
5、添加Toggole菜单按钮(Toggle Schema Object). K/ I9 }2 y/ V$ T% U
其实在第4步中定义的是Action Schema Object,也是点击菜单,当点击菜单时响应相应的回调,但是菜单的形状不发生变化。而Toggle Schema Object在点击以后会发生形状变化,比如点击以后外形会凹陷下去,或者在菜单前面有一个√。; g5 p- d' `5 j% E" s
1 t; ?! S* d4 T4 g! m' ?* r7 R8 ]
Toggle Schema Object和Action Schema Object的属性基本一致,只是Toggle对象多了一个checked属性,当摁下时,checked=='on',否则checked=='off'(默认)。另外Toggle对象是使用sl_toggle_schema创建实例的。6 X& N, ^( Q. ]7 _
- 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菜单,被临幸了!'), ^( ?3 S' F- L8 `8 Y' i
: j( y( y1 Y* B9 a# X
+ B- ^/ Q ?, W0 S: \$ x2 @- J6、定义多级子菜单(Container Schema Object)
3 }: Z# s" r/ D0 q; a& S第5和6步都是添加一个菜单项,下面尝试添加一个包含子菜单的项目试试。这个子菜单叫做Container Schema Object,使用sl_container_schema进行实例创建。Container Schema Object和前面两个对象属性基本相似,包含两个特殊属性:3 | C* j9 F: n* t
# O) y% q/ l; A# S" V' J" m
(1)childreNFCns- z" ~: _4 I+ B- {! a
Cell数组,指定创建子菜单函数的列表,等同于第2步中MenuWeWantAdd()的返回值。可以使用'separator'指定菜单之间的分割线。
4 Y3 l Q$ I* K3 e1 v8 M- % 创建一个schema对象的实例,用于定义新增菜单的内容
- schema=sl_container_schema; % 注意这里是一个container
- % 子菜单创建函数,使用'separator'添加分割线
- schema.childrenFcns={
- @CreateSubMenu1 % 子菜单创建函数1
- 'separator' % 菜单之间的分隔符,快乐的分割线
- @CreateSubMenu2}; % 子菜单创建函数2
5 \0 j0 ` p$ ? a
0 D# H! C2 d0 X/ N3 h3 b! A
7 P8 g- p2 C; U( }: X& w(2)generateFcn8 k& i5 {9 L9 F# _ t c+ r4 x
函数句柄,相当于第2步中的@MenuWeWantAdd,该函数返回一个cell数组。
9 r: I, ?) Z1 p3 _5 C5 E2 X- % 创建一个schema对象的实例,用于定义新增菜单的内容
- schema=sl_container_schema; % 注意这里是一个container
- % 子菜单生成函数,注意当设置了generateFcn时,自动屏蔽childrenFcns
- schema.generateFcn=@SubMenuWeWantAdd; % 返回生成子菜单的函数列表
' k u. Q3 k( m/ b( o3 Q2 O+ L, J' @ 5 z- _! Y1 S( ]$ @8 B" `' x
$ O; x# B) Y/ }
请注意,generateFcn的优先权高于childrenFcns,当定义了generateFcn属性,那么childrenFcns属性自动被屏蔽。根据上面的说明,继续在sl_customization.m文件中添加如下代码:
. L% c F3 l" u- 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,被临幸了!'')';
- z6 F5 [- F- m: ? : M: Y5 r+ o( E' j' K* o
: h; i' N: I$ Z" d0 U7、让自定义立即菜单生效
' \: j0 c9 P _+ ^. n% _好不容易编写好了上面的sl_customization.m,想立即看看效果,不过很惋惜的告诉您,您必须重启MATLAB,否不会生效。不过也可以在Command Windows中执行以下命令:
+ H8 A T; E. \2 Z+ G# z' v- sl_refresh_customizations/ h0 j h' c1 [/ ]
; ?, U/ Q# E: ~/ c
: M/ s1 M9 L# n* V
至于效果可以查看本文最头部的图片!) F5 J1 l7 f% J, d
U$ L0 w/ P' C: ]- ]! ?9 a
请注意,在Window系统中,在sl_customization中设置断点,那么Simulink将不会执行设置断点的函数,因此如果想调试sl_customization文件,请使用命令行的形式,比如dbstop,千万不要在编辑器中直接设置断点。
7 _% x3 J |5 h' v' C' \3 g) i1 X( v4 W1 y1 S2 F
- |) l) K4 ~! ]" M; H/ c
|
|