Simulink 自定义顶部菜单:一键显示所有 Scope 菜单栏

Simulink 自定义顶部菜单:一键显示所有 Scope 菜单栏

测试环境:MATLAB R2016b
注意:下面两个 .m 文件都需要放到 MATLAB path 中,否则 Simulink 找不到脚本。

最近在做自己的 Simulink 工具箱时,遇到一个小需求:
Scope 打开以后,有些菜单栏/工具栏默认不显示,用起来不太方便。

比如想缩放、保存图像、调整窗口这些操作,如果每次都手动处理就比较麻烦。
所以我在 Simulink 顶部菜单里加了一个自己的工具入口,用来一键打开当前模型中的所有 Scope,并把 Scope 窗口的菜单栏和工具栏显示出来。

最终效果就是:

Simulink 顶部菜单 -> TG工具箱 -> Scope Tools -> Show All Scope MenuBar


1. 创建 sl_customization.m

先创建一个 sl_customization.m 文件,放到 MATLAB path 下面。

这个文件的作用是给 Simulink 注册自定义菜单。

function sl_customization(cm)

    cm.addCustomMenuFcn('Simulink:MenuBar', @nova_ecu_f4_menu);

end


function schemaFcns = nova_ecu_f4_menu(callbackInfo)

    %#ok<INUSD>

    % 顶层菜单
    schemaFcns = { @nova_ecu_f4_top_menu };

end


function schema = nova_ecu_f4_top_menu(callbackInfo)

    %#ok<INUSD>

    schema = sl_container_schema;

    % Simulink 顶部菜单显示的名字
    schema.label = 'TG工具箱';

    % tag 尽量唯一,避免和其他工具箱冲突
    schema.tag = 'NovaECU:F4:TopMenu';

    % 顶层菜单下面挂载的子菜单
    schema.childrenFcns = { ...
        @nova_ecu_f4_scope_tools_menu ...
    };

end


function schema = nova_ecu_f4_scope_tools_menu(callbackInfo)

    %#ok<INUSD>

    schema = sl_container_schema;

    % TG工具箱 -> Scope Tools
    schema.label = 'Scope Tools';
    schema.tag   = 'NovaECU:F4:ScopeTools';

    schema.childrenFcns = { ...
        @nova_ecu_f4_show_scope_menubar_item ...
    };

end


function schema = nova_ecu_f4_show_scope_menubar_item(callbackInfo)

    %#ok<INUSD>

    schema = sl_action_schema;

    % TG工具箱 -> Scope Tools -> Show All Scope MenuBar
    schema.label = 'Show All Scope MenuBar';
    schema.tag   = 'NovaECU:F4:ShowAllScopeMenuBar';

    schema.callback = @nova_ecu_f4_show_scope_menubar_callback;

end


function nova_ecu_f4_show_scope_menubar_callback(callbackInfo)

    model = '';

    % 优先从当前菜单上下文里取模型
    try
        model = get_param(callbackInfo.model, 'Name');
    catch
        % 取不到就退回到当前模型
        try
            model = bdroot(gcs);
        catch
            model = '';
        end
    end

    try
        if isempty(model)
            nova_scope_show_menubar;
        else
            nova_scope_show_menubar(model);
        end
    catch ME
        errordlg(ME.message, 'NovaECU Scope Tool Error');
    end

end

这里的几个名字可以自己改,比如:

schema.label = 'TG工具箱';

这个就是 Simulink 顶部菜单栏里显示的名字。

但是 schema.tag 建议尽量写得唯一一点,最好带上自己的工具箱前缀。
比如我这里用的是:

NovaECU:F4:TopMenu

这样可以减少和别的工具箱菜单冲突的可能。


2. 创建 nova_scope_show_menubar.m

再创建一个 nova_scope_show_menubar.m 文件,也放到 MATLAB path 下面。

这个文件负责真正处理 Scope 窗口。

function nova_scope_show_menubar(model)

    if nargin < 1 || isempty(model)
        try
            model = bdroot(gcs);
        catch
            model = '';
        end
    end

    oldHidden = get(0, 'ShowHiddenHandles');
    set(0, 'ShowHiddenHandles', 'On');

    cleanupObj = onCleanup(@() set(0, 'ShowHiddenHandles', oldHidden)); %#ok<NASGU>

    scopes = {};

    % 找当前模型里的 Scope 模块
    if ~isempty(model)
        try
            scopes = find_system(model, ...
                'LookUnderMasks', 'all', ...
                'FollowLinks', 'on', ...
                'BlockType', 'Scope');
        catch
            scopes = {};
        end
    end

    % 先把 Scope 全部打开
    for i = 1:numel(scopes)
        try
            open_system(scopes{i});
        catch
        end
    end

    drawnow;

    % Scope 本质上也是 figure 窗口
    figs = findall(0, 'Type', 'figure');

    count = 0;

    for i = 1:numel(figs)
        if local_is_scope_figure(figs(i), scopes)
            try
                set(figs(i), 'MenuBar', 'figure');
                set(figs(i), 'ToolBar', 'figure');
                count = count + 1;
            catch
            end
        end
    end

    fprintf('[NovaECU] Scope windows updated: %d\n', count);

end


function tf = local_is_scope_figure(hFig, scopes)

    tf = false;

    try
        tag = get(hFig, 'Tag');
    catch
        tag = '';
    end

    try
        name = get(hFig, 'Name');
    catch
        name = '';
    end

    % R2016b 以及一些老版本 Scope 常见的 Tag
    scopeTags = { ...
        'SIMULINK_SIMSCOPE_FIGURE', ...
        'Simulink_Scope_Figure', ...
        'Scope' ...
    };

    for k = 1:numel(scopeTags)
        if strcmpi(tag, scopeTags{k})
            tf = true;
            return;
        end
    end

    % 兜底:根据 Scope block 的路径或者名称判断
    for k = 1:numel(scopes)
        blk = scopes{k};

        try
            blkName = get_param(blk, 'Name');
        catch
            blkName = '';
        end

        if isempty(name)
            continue;
        end

        if strcmp(name, blk)
            tf = true;
            return;
        end

        if ~isempty(strfind(name, blk))
            tf = true;
            return;
        end

        if ~isempty(blkName) && strcmp(name, blkName)
            tf = true;
            return;
        end

        if ~isempty(blkName) && ~isempty(strfind(name, blkName))
            tf = true;
            return;
        end
    end

end

这里需要注意一点:

set(0, 'ShowHiddenHandles', 'On');

Scope 窗口有时候不是普通方式能直接找到的,所以这里临时打开 ShowHiddenHandles
为了不影响 MATLAB 后续环境,脚本里用了 onCleanup,执行结束后会自动恢复原来的设置。


两个文件都放好以后,在 MATLAB 命令行执行:

sl_refresh_customizations

然后重新打开 Simulink,顶部菜单里应该就能看到:

TG工具箱

点进去以后:

TG工具箱 -> Scope Tools -> Show All Scope MenuBar

点击后,当前模型里的 Scope 会被打开,并且菜单栏、工具栏会显示出来。


4. 常见问题

如果菜单没有出现,可以检查这几项:

  1. sl_customization.m 是否在 MATLAB path 中;
  2. 修改完 sl_customization.m 后,是否执行了 sl_refresh_customizations
  3. 文件名是否写错,必须叫 sl_customization.m
  4. 如果还不行,可以重启 MATLAB 再试一次。

这个功能本身不复杂,但是放到自定义工具箱里还是挺方便的。
尤其是自己做 Simulink 工具箱时,把一些常用的小工具挂到菜单里,用起来会比每次手敲命令舒服很多。

posted @ 2026-06-05 08:40  顾率  阅读(16)  评论(0)    收藏  举报