<-- Home |--matlab |--FEM

PDE Plots in GUI应用中使用PDE工具箱的绘图函数

前言

在网上有一个问题没有得到很好的解决,就是在GUI中显示PDE工具箱的计算结果。目前英文网站上有一个解决方案,非常好玩,先建立一个图窗,把结果绘制在该图窗上,然后用copyobj函数把结果复制到GUI的图窗上。还有一个牛人把网格和结果自己插值搞在坐标上。 这两个例子就不要扩散了……但是AI已经被搞坏了,很喜欢这两个方式。

1figure;
2pdeplot(model,'ColorMap','default');
3h = get(gcf,'Children');
4set(h,'HandleVisibility','off');
5
6% ... uiaxes中绘制
7
8ax = uiaxes(parent);
9copyobj(h,ax);

还有一个好玩的解放方式,就是把UIfigure/uiaxes的句柄可见性设置为’on’, 然后就能让绘图函数感知到相应的图形对象。

就大概是这样的把戏,但是那个绘图图窗的隐藏老是有这样那样的问题。

我以前也搞了一个更加无聊的产生PDE几何模型的GUI,我的做法是把几何模型导出成stl文件,然后界面上显示STL文件。 用的函数式pdegplot,这个函数本身是支持第一个参数为uiaxes的。

而2D结果显示的函数是pdeplot,这个函数也支持在uiaxes中绘制。

1ax = uiaxes(parent);
2pdeplot(ax, model,'ColorMap','default');

但是pdeplot3D不支持第一个参数axes的语法,但是通过Parent参数,可以直接在GUI的图窗中绘制。

1ax = uiaxes(parent);
2pdeplot3D(model,'ColorMap','default','Parent',ax);

总结一下,PDE工具箱的绘图函数:

函数支持的参数备注
pdegplot支持uiaxes作为第一个参数2D
pdeplot支持uiaxes作为第一个参数2D
pdeplot3D支持uiaxes作为Parent参数3D

一个简单的GUI

我们来实现一个简单的悬臂梁分析GUI,这个GUI的界面如下:

APP界面

用户可以设置几何参数(长度、宽度、高度),材料参数(杨氏模量、泊松比),载荷参数(力),网格参数(网格尺寸),变形显示参数(变形比例)等参数;并通过按钮开产生几何体、生成网格和求解。结果则在右侧的图窗中显示,包括几何、网格和结果。

按照默认参数,点击产生几何体按钮,几何体显示在右侧的图窗中。

几何体

再点击生成网格按钮,网格显示在右侧的图窗中。

网格

然后点击求解按钮,结果显示在右侧的图窗中。

结果

调节变形比例到2085后,悬臂梁的变形以更加夸张的方式显示在右侧的图窗中。

变形

这里所有的图片都是通过exportapp函数导出的。

1exportapp(app.UIFigure, "app.png");

GUI代码

 1classdef simplePDE < matlab.apps.AppBase
 2    % 一个简单的PDE工具箱例子的GUI App
 3    
 4    properties (Access = public)
 5        % 用户界面组件
 6        UIFigure matlab.ui.Figure
 7    end
 8    
 9    properties (Access = private)
10        
11        % 主布局
12        MainLayout matlab.ui.container.GridLayout
13        
14        % 控制面板
15        ControlPanel matlab.ui.container.Panel
16        
17        % 几何参数控制
18        LengthSpinner matlab.ui.control.Spinner      % 长度
19        WidthSpinner matlab.ui.control.Spinner       % 宽度
20        HeightSpinner matlab.ui.control.Spinner      % 高度
21        
22        % 材料参数控制
23        YoungModulusSpinner matlab.ui.control.Spinner    % 杨氏模量
24        PoissonRatioSpinner matlab.ui.control.Spinner    % 泊松比
25        
26        % 载荷参数控制
27        LoadSpinner matlab.ui.control.Spinner        % 载荷大小
28        
29        % 网格参数
30        MeshSizeSpinner matlab.ui.control.Spinner    % 网格尺寸
31        
32        % 变形显示控制
33        DeformationSlider matlab.ui.control.Slider   % 变形比例因子
34        DeformationValueLabel matlab.ui.control.Label % 变形比例值显示
35        
36        % 按钮控件
37        GeometryButton matlab.ui.control.Button    % 生成几何体按钮
38        MeshButton matlab.ui.control.Button        % 生成网格按钮
39        SolveButton matlab.ui.control.Button       % 求解按钮
40        
41        % 结果显示区域
42        ResultsPanel matlab.ui.container.Panel
43        TabGroup matlab.ui.container.TabGroup
44        GeometryTab matlab.ui.container.Tab
45        MeshTab matlab.ui.container.Tab
46        ResultsTab matlab.ui.container.Tab
47        GeometryAxes matlab.ui.control.UIAxes
48        MeshAxes matlab.ui.control.UIAxes
49        ResultsAxes matlab.ui.control.UIAxes
50        
51        % 结果类型选择
52        ResultTypeDropDown matlab.ui.control.DropDown
53        
54        % PDE模型
55        Model
56        Results
57    end

这里,大部分控件都是不可在外部访问的,access属性为private。我们把UIFigure作为public属性,这样就可以在对象中访问,便于调用exportapp函数把界面输出图片。

  1    methods (Access = private)
  2        function createComponents(app)
  3            % 创建主窗口
  4            app.UIFigure = uifigure('Name', '悬臂梁有限元分析');
  5            app.UIFigure.Position = [100 100 1200 800];
  6            movegui(app.UIFigure,'center');
  7            
  8            % 创建主网格布局
  9            app.MainLayout = uigridlayout(app.UIFigure, [1 2]);
 10            app.MainLayout.ColumnWidth = {300, '1x'};
 11            app.MainLayout.Padding = [0 0 0 0];  % 移除边距
 12            app.MainLayout.RowSpacing = 0;       % 移除行间距
 13            app.MainLayout.ColumnSpacing = 0;    % 移除列间距
 14            
 15            % 创建控制面板
 16            app.ControlPanel = uipanel(app.MainLayout);
 17            app.ControlPanel.Title = '参数控制';
 18            app.ControlPanel.Layout.Column = 1;
 19            
 20            % 修改控制面板的网格布局以容纳更多控件
 21            controlGrid = uigridlayout(app.ControlPanel, [18 2]);
 22            controlGrid.RowHeight = {25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 35, 25, 25, 25, 25, 25, 25};  % 固定每行高度
 23            controlGrid.ColumnWidth = {'1x', '1x'};  % 两列等宽
 24            controlGrid.Padding = [10 10 10 10];
 25            controlGrid.RowSpacing = 5;  % 减小行间距
 26            controlGrid.ColumnSpacing = 10;  % 设置列间距
 27            
 28            % 几何参数控件
 29            uilabel(controlGrid, 'Text', '几何参数', 'FontWeight', 'bold');
 30            uilabel(controlGrid, 'Text', '');
 31            
 32            uilabel(controlGrid, 'Text', '长度 (m)');
 33            app.LengthSpinner = uispinner(controlGrid);
 34            app.LengthSpinner.Limits = [0.1 10];
 35            app.LengthSpinner.Value = 1;
 36            
 37            uilabel(controlGrid, 'Text', '宽度 (m)');
 38            app.WidthSpinner = uispinner(controlGrid);
 39            app.WidthSpinner.Limits = [0.01 1];
 40            app.WidthSpinner.Value = 0.1;
 41            
 42            uilabel(controlGrid, 'Text', '高度 (m)');
 43            app.HeightSpinner = uispinner(controlGrid);
 44            app.HeightSpinner.Limits = [0.01 1];
 45            app.HeightSpinner.Value = 0.1;
 46            
 47            % 材料参数控件
 48            uilabel(controlGrid, 'Text', '材料参数', 'FontWeight', 'bold');
 49            emptyLabel = uilabel(controlGrid, 'Text', '');
 50            
 51            uilabel(controlGrid, 'Text', '杨氏模量 (Pa)');
 52            app.YoungModulusSpinner = uispinner(controlGrid);
 53            app.YoungModulusSpinner.Limits = [1e6 1e12];
 54            app.YoungModulusSpinner.Value = 2e11;  % 钢的杨氏模量
 55            app.YoungModulusSpinner.Step = 1e9;
 56            
 57            uilabel(controlGrid, 'Text', '泊松比');
 58            app.PoissonRatioSpinner = uispinner(controlGrid);
 59            app.PoissonRatioSpinner.Limits = [0 0.5];
 60            app.PoissonRatioSpinner.Value = 0.3;
 61            app.PoissonRatioSpinner.Step = 0.01;
 62            
 63            % 载荷参数控件
 64            uilabel(controlGrid, 'Text', '载荷参数', 'FontWeight', 'bold');
 65            uilabel(controlGrid, 'Text', '');
 66            
 67            uilabel(controlGrid, 'Text', '载荷 (N)');
 68            app.LoadSpinner = uispinner(controlGrid);
 69            app.LoadSpinner.Limits = [-1e6 1e6];
 70            app.LoadSpinner.Value = -100000;
 71            app.LoadSpinner.Step = 100;
 72            
 73            % 网格参数控件
 74            uilabel(controlGrid, 'Text', '网格参数', 'FontWeight', 'bold');
 75            uilabel(controlGrid, 'Text', '');
 76            
 77            uilabel(controlGrid, 'Text', '网格尺寸');
 78            app.MeshSizeSpinner = uispinner(controlGrid);
 79            app.MeshSizeSpinner.Limits = [0.01 0.5];
 80            app.MeshSizeSpinner.Value = 0.05;
 81            app.MeshSizeSpinner.Step = 0.01;
 82            
 83            % 变形显示控制
 84            uilabel(controlGrid, 'Text', '变形显示', 'FontWeight', 'bold');
 85            uilabel(controlGrid, 'Text', '');
 86            
 87            uilabel(controlGrid, 'Text', '变形比例');
 88            app.DeformationValueLabel = uilabel(controlGrid, 'Text', '500');  % 初始值显示
 89            
 90            sliderGrid = uigridlayout(controlGrid, [1 1]);  % 创建滑块的网格布局
 91            sliderGrid.Layout.Column = [1 2];  % 跨两列
 92            sliderGrid.Padding = [0 5 0 5];  % 添加适当的上下内边距
 93            app.DeformationSlider = uislider(sliderGrid);
 94            app.DeformationSlider.Limits = [10 10000];
 95            app.DeformationSlider.Value = 500;
 96            app.DeformationSlider.ValueChangedFcn = @(src,~) deformationValueChanged(app, src);
 97            app.DeformationSlider.Enable = 'off';  % 初始禁用
 98            app.DeformationValueLabel.Enable = 'off';  % 初始禁用
 99            
100            % 添加结果类型选择下拉列表
101            uilabel(controlGrid, 'Text', '结果类型');
102            app.ResultTypeDropDown = uidropdown(controlGrid);
103            app.ResultTypeDropDown.Items = {'位移大小', '应力von Mises', 'x方向位移', 'y方向位移', 'z方向位移'};
104            app.ResultTypeDropDown.Value = '位移大小';
105            app.ResultTypeDropDown.ValueChangedFcn = @(~,~) updateResultsDisplay(app);
106            app.ResultTypeDropDown.Enable = 'off';  % 初始禁用
107            
108            % 添加按钮控件(在控制面板网格布局中)
109            % 生成几何体按钮
110            app.GeometryButton = uibutton(controlGrid, 'Text', '生成几何体');
111            app.GeometryButton.Layout.Column = [1 2];
112            app.GeometryButton.ButtonPushedFcn = @(~,~) generateGeometry(app);
113            
114            % 生成网格按钮
115            app.MeshButton = uibutton(controlGrid, 'Text', '生成网格');
116            app.MeshButton.Layout.Column = [1 2];
117            app.MeshButton.Enable = 'off';  % 初始禁用
118            app.MeshButton.ButtonPushedFcn = @(~,~) generateMesh(app);
119            
120            % 求解按钮
121            app.SolveButton = uibutton(controlGrid, 'Text', '求解');
122            app.SolveButton.Layout.Column = [1 2];
123            app.SolveButton.Enable = 'off';  % 初始禁用
124            app.SolveButton.ButtonPushedFcn = @(~,~) solvePDE(app);
125            
126            % 创建结果显示面板
127            app.ResultsPanel = uipanel(app.MainLayout);
128            app.ResultsPanel.Title = '计算结果';
129            app.ResultsPanel.Layout.Column = 2;
130            
131            % 创建结果区域的网格布局
132            resultsGrid = uigridlayout(app.ResultsPanel, [1 1]);
133            resultsGrid.Padding = [0 0 0 0];  % 移除内边距
134            
135            % 创建TabGroup并使其填满整个网格
136            app.TabGroup = uitabgroup(resultsGrid);
137            app.TabGroup.Layout.Row = 1;
138            app.TabGroup.Layout.Column = 1;
139            
140            % 创建各个标签页
141            app.GeometryTab = uitab(app.TabGroup, 'Title', '几何体');
142            app.MeshTab = uitab(app.TabGroup, 'Title', '网格');
143            app.ResultsTab = uitab(app.TabGroup, 'Title', '结果');
144            
145            % 为每个标签页创建网格布局
146            geomGrid = uigridlayout(app.GeometryTab, [1 1]);
147            meshGrid = uigridlayout(app.MeshTab, [1 1]);
148            resultGrid = uigridlayout(app.ResultsTab, [1 1]);
149            
150            % 设置网格布局的属性
151            grids = {geomGrid, meshGrid, resultGrid};
152            for g = grids
153                g{1}.Padding = [0 0 0 0];
154            end
155            
156            % 创建坐标轴
157            app.GeometryAxes = uiaxes(geomGrid);
158            app.MeshAxes = uiaxes(meshGrid);
159            app.ResultsAxes = uiaxes(resultGrid);
160            
161            % 设置所有坐标轴的基本属性
162            axesList = [app.GeometryAxes, app.MeshAxes, app.ResultsAxes];
163            for ax = axesList
164                ax.XGrid = 'on';
165                ax.YGrid = 'on';
166                ax.ZGrid = 'on';
167                ax.Box = 'on';
168                view(ax, 30, 30);
169            end
170            
171            % 添加标签切换回调
172            app.TabGroup.SelectionChangedFcn = @(~,~) tabChanged(app);
173        end

实际构造GUI的部分在createComponents函数中,这个函数在simplePDE函数(构造函数)中调用。

 1    methods
 2        function app = simplePDE
 3            % 构造函数
 4            createComponents(app);
 5            
 6            registerApp(app, app.UIFigure);
 7            % 显示界面
 8            app.UIFigure.Visible = 'on';
 9        end
10        
11        function delete(app)
12            % 析构函数
13            if isvalid(app.UIFigure)
14                delete(app.UIFigure);
15            end
16        end
17    end

回调函数

具体的功能在几个回调函数中实现。

 1        function tabChanged(app)
 2            % 根据当前选择的标签更新显示
 3            if isempty(app.Model)
 4                return;
 5            end
 6            
 7            switch app.TabGroup.SelectedTab.Title
 8                case '几何体'
 9                    cla(app.GeometryAxes);
10                    pdegplot(app.Model.Geometry,'FaceLabels','on','FaceAlpha',0.5,...
11                        'Parent',app.GeometryAxes);
12                    app.ResultTypeDropDown.Enable = 'off';
13                    app.DeformationSlider.Enable = 'off';
14                    app.DeformationValueLabel.Enable = 'off';
15                    
16                case '网格'
17                    cla(app.MeshAxes);
18                    pdeplot3D(app.Model.Mesh,'Parent',app.MeshAxes);
19                    app.ResultTypeDropDown.Enable = 'off';
20                    app.DeformationSlider.Enable = 'off';
21                    app.DeformationValueLabel.Enable = 'off';
22                    
23                case '结果'
24                    if ~isempty(app.Results)
25                        app.ResultTypeDropDown.Enable = 'on';
26                        app.DeformationSlider.Enable = 'on';
27                        app.DeformationValueLabel.Enable = 'on';
28                        updateResultsDisplay(app);
29                    end
30            end
31            
32        end

这个回调函数处理几何体、网格和结果的显示。

 1        function updateResultsDisplay(app)
 2            if ~isempty(app.Results)
 3                cla(app.ResultsAxes);
 4                
 5                % 预处理:根据选择确定要显示的数据
 6                switch app.ResultTypeDropDown.Value
 7                    case '位移大小'
 8                        colorData = app.Results.Displacement.Magnitude;
 9                        titleStr = '位移大小';
10                        isDisplacement = true;
11                    case '应力von Mises'
12                        colorData = app.Results.VonMisesStress;
13                        titleStr = 'von Mises应力';
14                        isDisplacement = false;
15                    case 'x方向位移'
16                        colorData = app.Results.Displacement.x;
17                        titleStr = 'X方向位移';
18                        isDisplacement = true;
19                    case 'y方向位移'
20                        colorData = app.Results.Displacement.y;
21                        titleStr = 'Y方向位移';
22                        isDisplacement = true;
23                    case 'z方向位移'
24                        colorData = app.Results.Displacement.z;
25                        titleStr = 'Z方向位移';
26                        isDisplacement = true;
27                end
28                
29                % 自动单位转换
30                if isDisplacement
31                    maxAbs = max(abs(colorData(:)));
32                    if maxAbs >= 1
33                        unitStr = 'm';
34                        factor = 1;
35                    elseif maxAbs >= 1e-3
36                        unitStr = 'mm';
37                        factor = 1e3;
38                    elseif maxAbs >= 1e-6
39                        unitStr = 'μm';
40                        factor = 1e6;
41                    else
42                        unitStr = 'nm';
43                        factor = 1e9;
44                    end
45                    colorData = colorData * factor;
46                else
47                    maxAbs = max(abs(colorData(:)));
48                    if maxAbs >= 1e9
49                        unitStr = 'GPa';
50                        factor = 1e-9;
51                    elseif maxAbs >= 1e6
52                        unitStr = 'MPa';
53                        factor = 1e-6;
54                    elseif maxAbs >= 1e3
55                        unitStr = 'kPa';
56                        factor = 1e-3;
57                    else
58                        unitStr = 'Pa';
59                        factor = 1;
60                    end
61                    colorData = colorData * factor;
62                end
63                
64                % 统一的绘图调用
65                pdeplot3D(app.Results.Mesh,...
66                    'ColorMapData', colorData,...
67                    'Deformation', app.Results.Displacement,...
68                    'DeformationScaleFactor', app.DeformationSlider.Value,...
69                    'Parent', app.ResultsAxes);
70                
71                
72                % 添加颜色条和标题
73                cb = colorbar(app.ResultsAxes);
74                title(app.ResultsAxes, titleStr);
75                
76                % 设置颜色条标签
77                if isDisplacement
78                    cb.Label.String = sprintf('位移 (%s)', unitStr);
79                else
80                    cb.Label.String = sprintf('应力 (%s)', unitStr);
81                end
82            end
83        end

这个回调函数处理结果的显示,根据一个下拉列表选择的结果类型,显示不同的结果。注意这里pdeplot3DParent参数设置为app.ResultsAxes,这样就可以在GUI的图窗中显示结果。

 1        function generateGeometry(app)
 2            % 创建有限元模型
 3            app.Model = femodel(AnalysisType="structuralStatic");
 4            
 5            % 创建几何
 6            L = app.LengthSpinner.Value;
 7            W = app.WidthSpinner.Value;
 8            H = app.HeightSpinner.Value;
 9            
10            % 定义长方体几何
11            gm = multicuboid(L,W,H);
12            
13            % 将几何导入到模型中
14            app.Model.Geometry = gm;
15            
16            % 切换到几何体标签页并显示
17            app.TabGroup.SelectedTab = app.GeometryTab;
18            tabChanged(app);
19            
20            % 启用网格生成按钮
21            app.MeshButton.Enable = 'on';
22            
23        end

这个回调函数产生几何体,就简单调用multicuboid函数产生一个长方体,这个函数会把右边的显示切换成几何显示。

 1        function generateMesh(app)
 2            % 生成网格
 3            app.Model = generateMesh(app.Model,'Hmax',app.MeshSizeSpinner.Value);
 4            
 5            % 切换到网格标签页并显示
 6            app.TabGroup.SelectedTab = app.MeshTab;
 7            tabChanged(app);
 8            
 9            % 启用求解按钮
10            app.SolveButton.Enable = 'on';
11            
12            
13        end

这个回调函数生成网格,调用generateMesh函数生成网格,并把右边的显示切换成网格显示。控制网格尺寸的参数从UI中获取。

 1        function solvePDE(app)
 2            app.SolveButton.Enable = 'off';
 3            generateGeometry(app);
 4            generateMesh(app);
 5            % 设置材料属性
 6            app.Model.MaterialProperties = materialProperties(...
 7                YoungsModulus=app.YoungModulusSpinner.Value,...
 8                PoissonsRatio=app.PoissonRatioSpinner.Value);
 9            
10            % 设置边界条件
11            % 固定端(F3面,x=0)
12            app.Model.FaceBC(3) = faceBC(Constraint="fixed");
13            
14            % 自由端载荷(F5面,x=L)
15            load = app.LoadSpinner.Value;
16            app.Model.FaceLoad(5) = faceLoad(SurfaceTraction=[0;0;load]);
17            
18            % 求解
19            app.Results = solve(app.Model);
20            
21            % 求解完成后自动切换到结果标签
22            app.TabGroup.SelectedTab = app.ResultsTab;
23            tabChanged(app);
24            app.SolveButton.Enable = 'on';
25        end

这个回调函数求解PDE,调用solve函数求解PDE,并把右边的显示切换成结果显示。这里边界条件中固定端是faceBC,载荷是faceLoad

完成计算后,还可以切换变形量的显示比例。

1        function deformationValueChanged(app, src)
2            % 更新标签显示
3            app.DeformationValueLabel.Text = sprintf('%.0f', src.Value);
4            % 更新显示
5            updateResultsDisplay(app);
6        end

大概上面的代码就能够构造一个基本的PDE求解的GUI了。当然,我们还可以增加一些优化过程的GUI控制,构成比较完整的、可以提交给用户的App。

完整代码

  1classdef simplePDE < matlab.apps.AppBase
  2    % 一个简单的PDE工具箱例子的GUI App
  3    
  4    properties (Access = public)
  5        % 用户界面组件
  6        UIFigure matlab.ui.Figure
  7    end
  8    
  9    properties (Access = private)
 10        
 11        % 主布局
 12        MainLayout matlab.ui.container.GridLayout
 13        
 14        % 控制面板
 15        ControlPanel matlab.ui.container.Panel
 16        
 17        % 几何参数控制
 18        LengthSpinner matlab.ui.control.Spinner      % 长度
 19        WidthSpinner matlab.ui.control.Spinner       % 宽度
 20        HeightSpinner matlab.ui.control.Spinner      % 高度
 21        
 22        % 材料参数控制
 23        YoungModulusSpinner matlab.ui.control.Spinner    % 杨氏模量
 24        PoissonRatioSpinner matlab.ui.control.Spinner    % 泊松比
 25        
 26        % 载荷参数控制
 27        LoadSpinner matlab.ui.control.Spinner        % 载荷大小
 28        
 29        % 网格参数
 30        MeshSizeSpinner matlab.ui.control.Spinner    % 网格尺寸
 31        
 32        % 变形显示控制
 33        DeformationSlider matlab.ui.control.Slider   % 变形比例因子
 34        DeformationValueLabel matlab.ui.control.Label % 变形比例值显示
 35        
 36        % 按钮控件
 37        GeometryButton matlab.ui.control.Button    % 生成几何体按钮
 38        MeshButton matlab.ui.control.Button        % 生成网格按钮
 39        SolveButton matlab.ui.control.Button       % 求解按钮
 40        
 41        % 结果显示区域
 42        ResultsPanel matlab.ui.container.Panel
 43        TabGroup matlab.ui.container.TabGroup
 44        GeometryTab matlab.ui.container.Tab
 45        MeshTab matlab.ui.container.Tab
 46        ResultsTab matlab.ui.container.Tab
 47        GeometryAxes matlab.ui.control.UIAxes
 48        MeshAxes matlab.ui.control.UIAxes
 49        ResultsAxes matlab.ui.control.UIAxes
 50        
 51        % 结果类型选择
 52        ResultTypeDropDown matlab.ui.control.DropDown
 53        
 54        % PDE模型
 55        Model
 56        Results
 57    end
 58    
 59    methods (Access = private)
 60        function createComponents(app)
 61            % 创建主窗口
 62            app.UIFigure = uifigure('Name', '悬臂梁有限元分析');
 63            app.UIFigure.Position = [100 100 1200 800];
 64            movegui(app.UIFigure,'center');
 65            
 66            % 创建主网格布局
 67            app.MainLayout = uigridlayout(app.UIFigure, [1 2]);
 68            app.MainLayout.ColumnWidth = {300, '1x'};
 69            app.MainLayout.Padding = [0 0 0 0];  % 移除边距
 70            app.MainLayout.RowSpacing = 0;       % 移除行间距
 71            app.MainLayout.ColumnSpacing = 0;    % 移除列间距
 72            
 73            % 创建控制面板
 74            app.ControlPanel = uipanel(app.MainLayout);
 75            app.ControlPanel.Title = '参数控制';
 76            app.ControlPanel.Layout.Column = 1;
 77            
 78            % 修改控制面板的网格布局以容纳更多控件
 79            controlGrid = uigridlayout(app.ControlPanel, [18 2]);
 80            controlGrid.RowHeight = {25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 35, 25, 25, 25, 25, 25, 25};  % 固定每行高度
 81            controlGrid.ColumnWidth = {'1x', '1x'};  % 两列等宽
 82            controlGrid.Padding = [10 10 10 10];
 83            controlGrid.RowSpacing = 5;  % 减小行间距
 84            controlGrid.ColumnSpacing = 10;  % 设置列间距
 85            
 86            % 几何参数控件
 87            uilabel(controlGrid, 'Text', '几何参数', 'FontWeight', 'bold');
 88            uilabel(controlGrid, 'Text', '');
 89            
 90            uilabel(controlGrid, 'Text', '长度 (m)');
 91            app.LengthSpinner = uispinner(controlGrid);
 92            app.LengthSpinner.Limits = [0.1 10];
 93            app.LengthSpinner.Value = 1;
 94            
 95            uilabel(controlGrid, 'Text', '宽度 (m)');
 96            app.WidthSpinner = uispinner(controlGrid);
 97            app.WidthSpinner.Limits = [0.01 1];
 98            app.WidthSpinner.Value = 0.1;
 99            
100            uilabel(controlGrid, 'Text', '高度 (m)');
101            app.HeightSpinner = uispinner(controlGrid);
102            app.HeightSpinner.Limits = [0.01 1];
103            app.HeightSpinner.Value = 0.1;
104            
105            % 材料参数控件
106            uilabel(controlGrid, 'Text', '材料参数', 'FontWeight', 'bold');
107            emptyLabel = uilabel(controlGrid, 'Text', '');
108            
109            uilabel(controlGrid, 'Text', '杨氏模量 (Pa)');
110            app.YoungModulusSpinner = uispinner(controlGrid);
111            app.YoungModulusSpinner.Limits = [1e6 1e12];
112            app.YoungModulusSpinner.Value = 2e11;  % 钢的杨氏模量
113            app.YoungModulusSpinner.Step = 1e9;
114            
115            uilabel(controlGrid, 'Text', '泊松比');
116            app.PoissonRatioSpinner = uispinner(controlGrid);
117            app.PoissonRatioSpinner.Limits = [0 0.5];
118            app.PoissonRatioSpinner.Value = 0.3;
119            app.PoissonRatioSpinner.Step = 0.01;
120            
121            % 载荷参数控件
122            uilabel(controlGrid, 'Text', '载荷参数', 'FontWeight', 'bold');
123            uilabel(controlGrid, 'Text', '');
124            
125            uilabel(controlGrid, 'Text', '载荷 (N)');
126            app.LoadSpinner = uispinner(controlGrid);
127            app.LoadSpinner.Limits = [-1e6 1e6];
128            app.LoadSpinner.Value = -100000;
129            app.LoadSpinner.Step = 100;
130            
131            % 网格参数控件
132            uilabel(controlGrid, 'Text', '网格参数', 'FontWeight', 'bold');
133            uilabel(controlGrid, 'Text', '');
134            
135            uilabel(controlGrid, 'Text', '网格尺寸');
136            app.MeshSizeSpinner = uispinner(controlGrid);
137            app.MeshSizeSpinner.Limits = [0.01 0.5];
138            app.MeshSizeSpinner.Value = 0.05;
139            app.MeshSizeSpinner.Step = 0.01;
140            
141            % 变形显示控制
142            uilabel(controlGrid, 'Text', '变形显示', 'FontWeight', 'bold');
143            uilabel(controlGrid, 'Text', '');
144            
145            uilabel(controlGrid, 'Text', '变形比例');
146            app.DeformationValueLabel = uilabel(controlGrid, 'Text', '500');  % 初始值显示
147            
148            sliderGrid = uigridlayout(controlGrid, [1 1]);  % 创建滑块的网格布局
149            sliderGrid.Layout.Column = [1 2];  % 跨两列
150            sliderGrid.Padding = [0 5 0 5];  % 添加适当的上下内边距
151            app.DeformationSlider = uislider(sliderGrid);
152            app.DeformationSlider.Limits = [10 10000];
153            app.DeformationSlider.Value = 500;
154            app.DeformationSlider.ValueChangedFcn = @(src,~) deformationValueChanged(app, src);
155            app.DeformationSlider.Enable = 'off';  % 初始禁用
156            app.DeformationValueLabel.Enable = 'off';  % 初始禁用
157            
158            % 添加结果类型选择下拉列表
159            uilabel(controlGrid, 'Text', '结果类型');
160            app.ResultTypeDropDown = uidropdown(controlGrid);
161            app.ResultTypeDropDown.Items = {'位移大小', '应力von Mises', 'x方向位移', 'y方向位移', 'z方向位移'};
162            app.ResultTypeDropDown.Value = '位移大小';
163            app.ResultTypeDropDown.ValueChangedFcn = @(~,~) updateResultsDisplay(app);
164            app.ResultTypeDropDown.Enable = 'off';  % 初始禁用
165            
166            % 添加按钮控件(在控制面板网格布局中)
167            % 生成几何体按钮
168            app.GeometryButton = uibutton(controlGrid, 'Text', '生成几何体');
169            app.GeometryButton.Layout.Column = [1 2];
170            app.GeometryButton.ButtonPushedFcn = @(~,~) generateGeometry(app);
171            
172            % 生成网格按钮
173            app.MeshButton = uibutton(controlGrid, 'Text', '生成网格');
174            app.MeshButton.Layout.Column = [1 2];
175            app.MeshButton.Enable = 'off';  % 初始禁用
176            app.MeshButton.ButtonPushedFcn = @(~,~) generateMesh(app);
177            
178            % 求解按钮
179            app.SolveButton = uibutton(controlGrid, 'Text', '求解');
180            app.SolveButton.Layout.Column = [1 2];
181            app.SolveButton.Enable = 'off';  % 初始禁用
182            app.SolveButton.ButtonPushedFcn = @(~,~) solvePDE(app);
183            
184            % 创建结果显示面板
185            app.ResultsPanel = uipanel(app.MainLayout);
186            app.ResultsPanel.Title = '计算结果';
187            app.ResultsPanel.Layout.Column = 2;
188            
189            % 创建结果区域的网格布局
190            resultsGrid = uigridlayout(app.ResultsPanel, [1 1]);
191            resultsGrid.Padding = [0 0 0 0];  % 移除内边距
192            
193            % 创建TabGroup并使其填满整个网格
194            app.TabGroup = uitabgroup(resultsGrid);
195            app.TabGroup.Layout.Row = 1;
196            app.TabGroup.Layout.Column = 1;
197            
198            % 创建各个标签页
199            app.GeometryTab = uitab(app.TabGroup, 'Title', '几何体');
200            app.MeshTab = uitab(app.TabGroup, 'Title', '网格');
201            app.ResultsTab = uitab(app.TabGroup, 'Title', '结果');
202            
203            % 为每个标签页创建网格布局
204            geomGrid = uigridlayout(app.GeometryTab, [1 1]);
205            meshGrid = uigridlayout(app.MeshTab, [1 1]);
206            resultGrid = uigridlayout(app.ResultsTab, [1 1]);
207            
208            % 设置网格布局的属性
209            grids = {geomGrid, meshGrid, resultGrid};
210            for g = grids
211                g{1}.Padding = [0 0 0 0];
212            end
213            
214            % 创建坐标轴
215            app.GeometryAxes = uiaxes(geomGrid);
216            app.MeshAxes = uiaxes(meshGrid);
217            app.ResultsAxes = uiaxes(resultGrid);
218            
219            % 设置所有坐标轴的基本属性
220            axesList = [app.GeometryAxes, app.MeshAxes, app.ResultsAxes];
221            for ax = axesList
222                ax.XGrid = 'on';
223                ax.YGrid = 'on';
224                ax.ZGrid = 'on';
225                ax.Box = 'on';
226                view(ax, 30, 30);
227            end
228            
229            % 添加标签切换回调
230            app.TabGroup.SelectionChangedFcn = @(~,~) tabChanged(app);
231        end
232        
233        function tabChanged(app)
234            % 根据当前选择的标签更新显示
235            if isempty(app.Model)
236                return;
237            end
238            
239            switch app.TabGroup.SelectedTab.Title
240                case '几何体'
241                    cla(app.GeometryAxes);
242                    pdegplot(app.Model.Geometry,'FaceLabels','on','FaceAlpha',0.5,...
243                        'Parent',app.GeometryAxes);
244                    app.ResultTypeDropDown.Enable = 'off';
245                    app.DeformationSlider.Enable = 'off';
246                    app.DeformationValueLabel.Enable = 'off';
247                    
248                case '网格'
249                    cla(app.MeshAxes);
250                    pdeplot3D(app.Model.Mesh,'Parent',app.MeshAxes);
251                    app.ResultTypeDropDown.Enable = 'off';
252                    app.DeformationSlider.Enable = 'off';
253                    app.DeformationValueLabel.Enable = 'off';
254                    
255                case '结果'
256                    if ~isempty(app.Results)
257                        app.ResultTypeDropDown.Enable = 'on';
258                        app.DeformationSlider.Enable = 'on';
259                        app.DeformationValueLabel.Enable = 'on';
260                        updateResultsDisplay(app);
261                    end
262            end
263            
264        end
265        
266        function updateResultsDisplay(app)
267            if ~isempty(app.Results)
268                cla(app.ResultsAxes);
269                
270                % 预处理:根据选择确定要显示的数据
271                switch app.ResultTypeDropDown.Value
272                    case '位移大小'
273                        colorData = app.Results.Displacement.Magnitude;
274                        titleStr = '位移大小';
275                        isDisplacement = true;
276                    case '应力von Mises'
277                        colorData = app.Results.VonMisesStress;
278                        titleStr = 'von Mises应力';
279                        isDisplacement = false;
280                    case 'x方向位移'
281                        colorData = app.Results.Displacement.x;
282                        titleStr = 'X方向位移';
283                        isDisplacement = true;
284                    case 'y方向位移'
285                        colorData = app.Results.Displacement.y;
286                        titleStr = 'Y方向位移';
287                        isDisplacement = true;
288                    case 'z方向位移'
289                        colorData = app.Results.Displacement.z;
290                        titleStr = 'Z方向位移';
291                        isDisplacement = true;
292                end
293                
294                % 自动单位转换
295                if isDisplacement
296                    maxAbs = max(abs(colorData(:)));
297                    if maxAbs >= 1
298                        unitStr = 'm';
299                        factor = 1;
300                    elseif maxAbs >= 1e-3
301                        unitStr = 'mm';
302                        factor = 1e3;
303                    elseif maxAbs >= 1e-6
304                        unitStr = 'μm';
305                        factor = 1e6;
306                    else
307                        unitStr = 'nm';
308                        factor = 1e9;
309                    end
310                    colorData = colorData * factor;
311                else
312                    maxAbs = max(abs(colorData(:)));
313                    if maxAbs >= 1e9
314                        unitStr = 'GPa';
315                        factor = 1e-9;
316                    elseif maxAbs >= 1e6
317                        unitStr = 'MPa';
318                        factor = 1e-6;
319                    elseif maxAbs >= 1e3
320                        unitStr = 'kPa';
321                        factor = 1e-3;
322                    else
323                        unitStr = 'Pa';
324                        factor = 1;
325                    end
326                    colorData = colorData * factor;
327                end
328                
329                % 统一的绘图调用
330                pdeplot3D(app.Results.Mesh,...
331                    'ColorMapData', colorData,...
332                    'Deformation', app.Results.Displacement,...
333                    'DeformationScaleFactor', app.DeformationSlider.Value,...
334                    'Parent', app.ResultsAxes);
335                
336                
337                % 添加颜色条和标题
338                cb = colorbar(app.ResultsAxes);
339                title(app.ResultsAxes, titleStr);
340                
341                % 设置颜色条标签
342                if isDisplacement
343                    cb.Label.String = sprintf('位移 (%s)', unitStr);
344                else
345                    cb.Label.String = sprintf('应力 (%s)', unitStr);
346                end
347            end
348        end
349        
350        function generateGeometry(app)
351            % 创建有限元模型
352            app.Model = femodel(AnalysisType="structuralStatic");
353            
354            % 创建几何
355            L = app.LengthSpinner.Value;
356            W = app.WidthSpinner.Value;
357            H = app.HeightSpinner.Value;
358            
359            % 定义长方体几何
360            gm = multicuboid(L,W,H);
361            
362            % 将几何导入到模型中
363            app.Model.Geometry = gm;
364            
365            % 切换到几何体标签页并显示
366            app.TabGroup.SelectedTab = app.GeometryTab;
367            tabChanged(app);
368            
369            % 启用网格生成按钮
370            app.MeshButton.Enable = 'on';
371            
372        end
373        
374        function generateMesh(app)
375            % 生成网格
376            app.Model = generateMesh(app.Model,'Hmax',app.MeshSizeSpinner.Value);
377            
378            % 切换到网格标签页并显示
379            app.TabGroup.SelectedTab = app.MeshTab;
380            tabChanged(app);
381            
382            % 启用求解按钮
383            app.SolveButton.Enable = 'on';
384            
385            
386        end
387        
388        function solvePDE(app)
389            app.SolveButton.Enable = 'off';
390            generateGeometry(app);
391            generateMesh(app);
392            % 设置材料属性
393            app.Model.MaterialProperties = materialProperties(...
394                YoungsModulus=app.YoungModulusSpinner.Value,...
395                PoissonsRatio=app.PoissonRatioSpinner.Value);
396            
397            % 设置边界条件
398            % 固定端(F3面,x=0)
399            app.Model.FaceBC(3) = faceBC(Constraint="fixed");
400            
401            % 自由端载荷(F5面,x=L)
402            load = app.LoadSpinner.Value;
403            app.Model.FaceLoad(5) = faceLoad(SurfaceTraction=[0;0;load]);
404            
405            % 求解
406            app.Results = solve(app.Model);
407            
408            % 求解完成后自动切换到结果标签
409            app.TabGroup.SelectedTab = app.ResultsTab;
410            tabChanged(app);
411            app.SolveButton.Enable = 'on';
412        end
413        
414        function deformationValueChanged(app, src)
415            % 更新标签显示
416            app.DeformationValueLabel.Text = sprintf('%.0f', src.Value);
417            % 更新显示
418            updateResultsDisplay(app);
419        end
420    end
421    
422    methods
423        function app = simplePDE
424            % 构造函数
425            createComponents(app);
426            
427            registerApp(app, app.UIFigure);
428            % 显示界面
429            app.UIFigure.Visible = 'on';
430        end
431        
432        function delete(app)
433            % 析构函数
434            if isvalid(app.UIFigure)
435                delete(app.UIFigure);
436            end
437        end
438    end
439end

文章标签

|-->matlab |-->pdegplot |-->pdeplot |-->pdeplot3D |-->GUI |-->appdesigner


GitHub