作为模型工程师,在Simulink中建模时,总是希望把模块排列整齐从而更加美观。但是模型的反复修改使得我们没有太多的精力去做这些整理工作。因此笔者开发了如下脚本来自动实现Simulink模块的整理工作,效果如下图所示:
大家可以自取使用,希望可以给大家的开发工作带来一点便利。使用方法是,在模型中点击想要被整理的模块(比如一个subsystem,使得gcbh为当前选中模块的句柄),再运行函数 arrangeit() 即可。
代码如下:
% 整理与所选模块的输入和输出连接的模块的位置,使之排布整齐
% 操作方法:先在simulink中点击选中目标模块,再运行该函数
function arrangeit()
lineh = get(gcbh, 'LineHandles');
porth = get(gcbh, 'PortHandles');
blkDistance = 150;
%% 设置输入接口模块
for i = 1:1:length(porth.Inport)
% 如果当前port口上悬空,没有连线,则跳过
if isequal(lineh.Inport(i), -1)
continue;
end
% 获取当前port口的位置
[portx, porty, ~, ~] = lgetpos(porth.Inport(i));
% 获取当前port口连线的源模块的句柄
curtLineSrcBlkH = get_param(lineh.Inport(i), 'SrcBlockHandle');
% 如果源模块是subsystem,则不对该subsystem的位置进行设置
if isequal('SubSystem', get_param(curtLineSrcBlkH, 'BlockType'))
continue;
end
% 获取源模块的宽度和高度值
[~, ~, width, height] = lgetpos(curtLineSrcBlkH);
% 重新设置源模块的位置,保持其宽度和高度值不变
newx = portx - blkDistance;
newy = porty - height / 2;
lsetpos(curtLineSrcBlkH, newx, newy, width, height);
end
%% 设置输出接口模块
for i = 1:1:length(porth.Outport)
% 如果当前port口上悬空,没有连线,则跳过
if isequal(lineh.Outport(i), -1)
continue;
end
% curtPortPos = get_param(porth.Outport(i), 'Position');
% 获取当前port口的位置
[portx, porty, ~, ~] = lgetpos(porth.Outport(i));
% 获取当前port口连线的目标模块的句柄
curtLineDstBlkH = get_param(lineh.Outport(i), 'DstBlockHandle');
% 如果目标模块是subsystem,则不对该subsystem的位置进行设置
if isequal('SubSystem', get_param(curtLineDstBlkH, 'BlockType'))
continue;
end
% 如果目标模块是 swithc, 则不对该subsystem的位置进行设置
if isequal('Switch', get_param(curtLineDstBlkH, 'BlockType'))
continue;
end
% 当前port连线的目标模块可能有多个模块,因此挨个处理
for j = 1:1:length(curtLineDstBlkH)
% 获取源模块的宽度和高度值
[~, ~, width, height] = lgetpos(curtLineDstBlkH(j));
% newx = curtPortPos(1) + blkDistance;
% newy = curtPortPos(2) - height / 2 + height * (j - 1);
newx = portx + blkDistance;
newy = porty - height / 2 + height * (j - 1);
lsetpos(curtLineDstBlkH(j), newx, newy, width, height);
end
end
%% 设置 enable 或 trigger 接口,两种类型的port合并后处理
EnableTriggerPortH = -1;
EnableTriggerLineH = -1;
if (~isequal(lineh.Enable, -1)) && (~isempty(lineh.Enable))
EnableTriggerPortH = porth.Enable;
EnableTriggerLineH = lineh.Enable;
end
if (~isequal(lineh.Trigger, -1)) && (~isempty(lineh.Trigger))
EnableTriggerPortH = porth.Trigger;
EnableTriggerLineH = lineh.Trigger;
end
if ~isequal(EnableTriggerPortH, -1)
% 获取当前port口的位置
[portx, porty, ~, ~] = lgetpos(EnableTriggerPortH);
% 获取当前port口连线的源模块的句柄
curtLineSrcBlkH = get_param(EnableTriggerLineH, 'SrcBlockHandle');
% 只有当enable port的源模块是 inport 类型的模块时,才对其位置进行优化
if isequal('Inport', get_param(curtLineSrcBlkH, 'BlockType'))
% 获取源模块的宽度和高度值
[~, ~, width, height] = lgetpos(curtLineSrcBlkH);
% 重新设置源模块的位置,保持其宽度和高度值不变
newx = portx - width * 3;
newy = porty - height * 2;
lsetpos(curtLineSrcBlkH, newx, newy, width, height);
end
end
end
函数 arrangeit() 使用到了位置设置和位置读取的两个函数的代码如下:
function lsetpos(blockH, x, y, width, height)
% lsetpos(blockH, x, y, width, height)
% blockH 可以是 目标模块句柄,或者是 目标模块路径
% blockH 不能是 port 或者 line 的句柄
% width 和 height 必须 ≥ 0
% 入参合法性检查
% 如果 blockH 是句柄,则维度必须是1
% 如果 blockH 是目标模块路径,则是字符串
if ((length(blockH) > 1) && (~ischar(blockH)))
errordlg('函数 lsetpos 的入参 blockH 的长度大于1','错误','creatmode');
end
if (width < 0)
errordlg('错误信息:函数 lsetpos 的入参 width 小于0 ! 应 ≥ 0 ','错误','creatmode');
end
if (height < 0)
errordlg('错误信息:函数 lsetpos 的入参 height 小于0 ! 应 ≥ 0 ','错误','creatmode');
end
set_param(blockH, 'Position', [x, y, x + width, y + height]);
end
function [x, y, width, height] = lgetpos(blockH)
% [x, y, width, height] = lgetpos(目标模块句柄)
% 如果 目标模块句柄 是一个 port 的句柄,则 width 和 height 返回-1
% 入参合法性检查
if (length(blockH) > 1)
errordlg('函数 lgetpos 的入参 blockH 的长度大于1','错误','creatmode');
end
pos = get_param(blockH, 'Position');
x = pos(1);
y = pos(2);
% 如果目标句柄是port的句柄,则pos只有两维,此时宽度和高度返回-1
if length(pos) == 4
width = pos(3) - pos(1);
height = pos(4) - pos(2);
else
width = -1;
height = -1;
end
end
如果大家觉得有点用处,还请点个赞,欢迎留言讨论~