Matlab App:教你制作PlotHelper

《MATLAB基础及图形用户界面设计》
课程设计

题目: Matlab PlotHelper:plot绘图辅助器

日期: 2021年04月27日

目 录
1 摘 要 3
2 功能描述 3
2.1 功能1:图窗实时编辑绘制功能 4
2.2 功能2:图窗保存功能 5
2.3 功能3:一些绘图有关小工具 10
2.4 功能4:代码生成 11
3 软硬件环境 13
3.1 运行环境 13
3.1.1 硬件环境 13
3.1.2 软件环境 13
3.2 开发环境 13
3.2.1 硬件环境 13
3.2.2 软件环境 13
4 主要功能实现方法简介 14
4.1 图窗属性初始化 14
4.2 图窗实时编辑绘制功能 14
4.3 图窗保存功能 17
4.4 一些与绘图有关的小工具 19
4.5 代码生成 20
5 实现效果展示 24
6 实践总结及感想 25
7 课程建议 25

1摘 要
平常使用Matlab进行数值计算总会绘制各种各样的图像,绘图中最常用的就是用plot函数进行二维图像的绘制。绘制中要使用legend、title、xlabel、ylabel、grid等函数对图像进行美化,增强可读性,也会使用print函数等保存图片,以便插入自己的文章等。
而当大量使用plot函数时,每次输代码看图改图就不太方便,基于此,笔者设计了一款Plot Helper,可以实现对原始图窗的实时编辑,实时修改标题,坐标轴名称,图例等,并很方便的进行高清晰度输出。
使用本App,可以帮你方便进行图片的完善:实时编辑标题、坐标轴名称、图例等;便捷输出:一键选择png、jpg、eps、pdf、ps格式,四种清晰度供你选择;提供并提供代码生成功能,可直接复制图窗修改的代码,并粘贴到源代码。
2功能描述
本次课程设计基于Matlab App Designer GUI开发平台,设计一个Plot Helper图窗实时编辑器,实现图窗修改完善中的各项功能。
最终实现设计的Plot Helper使用界面如图21、图22及图 2-3所示。

图21 Plot Helper整体界面

(a)网格线 (b)坐标区设置 (c)文件格式 (d)清晰度
图22 Plot Helper其他设置

图 2-3 代码生成界面
2.1功能1:图窗实时编辑绘制功能
实现图窗的实时编辑功能,实现当前图窗各参数实时更改,实时编辑标题、坐标轴名称、图例等功能。点击【生成测试图】按钮可以生成一张测试图(实际使用时直接修改当前图窗),接下来可以点击【绘制】按钮对当前图窗进行初步完善如图24所示,并进行实时编辑,如图25所示。

图24 对当前图窗进行完善

图25 当前图窗实时编辑
2.2功能2:图窗保存功能
实现音乐播放器中的图窗保存、复制等功能,并便捷打开文件夹,如图25所示。

图26 图窗保存区选项
(1)文件名:默认格式:“未命名加当前时间”,也可以自己添加文件名。
(2)文件路径: 默认当前Matlab工作文件夹,也可以点击【浏览】按钮选择文件夹,如图2-7所示。
图2-7 路径选择界面
当你点击浏览但并没有选择文件夹时,会跳出弹窗提示,如图 2-8所示。

图 2-8存储文件夹提示
(3)文件格式选择功能:选择想要输出的格式,如图 2-9所示。
图 2-9文件格式选择
(4)清晰度选择功能:选择想要输出的清晰度,如图 2-10所示。
图 2-10清晰度选择
(5)复制功能:点击【复制】按钮,可以直接把当前图窗粘贴至剪切板,可直接粘贴到word文档中,如图 2-11所示。
图 2-11复制功能展示

图 2-12输出功能展示
(6)输出功能:点击【输出】按钮,就讲图片按照上面选择的格式、路径、清晰度输出,并生成代码,如图 2-12输出功能展示所示。
(7)打开输出文件夹功能:点击【打开输出文件夹】按钮,可直接打开输出文件夹,如图 2-13所示。
图 2-13打开输出文件夹功能展示
(8)快速看图功能:点击【快速看图】按钮,可直接打开最近生成的图片如图 2-14所示。
图 2-14快速看图功能展示
2.3功能3:一些绘图有关小工具
小工具栏,集成了一些与绘图有关的小工具,如生成测试图、当前图窗显示、关闭所有图窗、关闭当前图窗、Plot函数自查表、背景一键变白功能。
(1)生成测试图:生成一张正余弦函数曲线作为测试功能用图,如图 2-15所示。

图 2-15生成测试图
(2)当前图窗显示:当当前图窗被遮挡后,点击【当前图窗显示】按钮显示当前图窗。
(3)关闭所有图窗:关闭所有的图窗。
(4)关闭当前图窗:关闭当前处理的图窗。
(5)Plot函数自查表:打开plot函数自查表Matlab Plot Cheatsheet
(源:https://github.com/Pjer-zhang/matlabPlotCheatsheet),如图 2-16所示。

图 2-16Matlab Plot Cheatsheet
(6)背景一键变白:将当前图窗背景变白,如图 2-17所示。

图 2-17图窗背景变白
2.4功能4:代码生成
在图窗编辑时实时生成代码,并在代码生成界面显示,点击【复制】按钮,会提示你选中文字,并2s后自动复制(control+c),如图 2-18、图 2-19所示。

图 2-18复制按钮点击

图 2-19复制效果
3软硬件环境
3.1运行环境
3.1.1硬件环境
处理器型号:Intel® Core™ i5-8265U CPU @ 1.60GHz 1.80 GHz及以上
内存容量:8GB及以上
外存容量:1G及以上
3.1.2软件环境
操作系统:64位Windows7或Windows10
应用软件:安装有Matlab R2020a或以上版本,或安装有Matlab R2020a对应的Matlab Runtime,版本号9.8.0.1323502。
3.2开发环境
3.2.1硬件环境
设备名称 LAPTOP-MTB43AUP
处理器 Intel® Core™ i5-8265U CPU @ 1.60GHz 1.80 GHz
机带 RAM 16.0 GB (15.8 GB 可用)
设备 ID C271EDB0-7F8D-4CE1-ADDE-A78FE92D6980
产品 ID 00342-35646-76079-AAOEM
系统类型 64 位操作系统, 基于 x64 的处理器
笔和触控 没有可用于此显示器的笔或触控输入

3.2.2软件环境
Windows规格
版本 Windows 10 家庭中文版
版本号 20H2
安装日期 ‎2021/‎3/‎18
操作系统内部版本 19042.928
体验 Windows Feature Experience Pack 120.2212.551.0

4主要功能实现方法简介
4.1图窗属性初始化
编写initPlotHelper(app)函数通过各个Flag的值来判断此属性是否变化,Flag值的变化手段为对应回调是否被调用,若被调用,则属性值变化,初始Flag值均设为0。若Flag值无变化,则对应属性值为原始属性值。
function initPlotHelper(app)
if app.PicXlabelFlag ~= 1
app.PH.PicXlabel = ‘x’;%Xlabel初始值为x
end
if app.PicTitleFlag ~= 1
app.PH.PicTitle = ‘Title’;%title初始值为Title
end
if app.PicYlabelFlag ~= 1
app.PH.PicYlabel = ‘y’;%ylabel初始值为y
end
if app.PicLegendFlag ~= 1
app.PH.PicLegend = {‘a’,‘b’};%legend初始值a,b
end
if app.PicFileNameFlag ~= 1
app.PH.FileName =strcat(‘未命名’,strrep(char(datetime(‘now’)),’:’,’-’));
app.EditField_Filename.Value = app.PH.FileName;%默认输出’未命名+时间’
end
if isempty(app.EditField_FilePath.Value)
app.EditField_FilePath.Value = pwd;
end
if isempty(app.EditField_Filename.Value)
msgbox(‘请输入文件名,默认未命名加当前时间’,‘FileName’);
end
end
4.2图窗实时编辑绘制功能
通过各个编辑区中Value值的更改,调用PHplot(app)函数,实现实时编辑功能,即每改变一次编辑框中的值,就回调用一次PHplot(app)函数,就会进行图像的实时绘制,相应代码如下所示:
function PHplot(app)% 绘制回调
initPlotHelper(app);
if app.InterpreterMode == 0%编译器模式选择0为tex1为latex
%对xlabel、ylabel、title、legend等进行绘制
xlabel(app.PH.PicXlabel,‘interpreter’,‘tex’);
ylabel(app.PH.PicYlabel,‘interpreter’,‘tex’);
title(app.PH.PicTitle,‘interpreter’,‘tex’);
if app.LegendMode == 0
legend off
else
legend(app.PH.PicLegend,‘interpreter’,‘tex’);
end
else
%对xlabel、ylabel、title、legend等进行绘制
xlabel(app.PH.PicXlabel,‘interpreter’,‘latex’);
ylabel(app.PH.PicYlabel,‘interpreter’,‘latex’);
title(app.PH.PicTitle,‘interpreter’,‘latex’); if app.LegendMode == 0
legend off
else
legend(app.PH.PicLegend,‘interpreter’,‘latex’);
end
end
end
网格线设置与坐标区设置通过各自的回调函数实现相应功能,如下所示:
% Value changed function: DropDown_grid
function DropDown_gridValueChanged(app, event)
value = app.DropDown_grid.Value;
if strcmp(value,‘关闭’)
grid off;
app.Code_Grid = ‘grid off; %不显示网格线’;
elseif strcmp(value,‘主网格线’)
grid on;
app.Code_Grid = ‘grid on; %显示主网格线’;
else
grid minor;
app.Code_Grid = [‘grid on; %显示主网格线’ newline ‘grid minor; %显示次网格线’];
end
PHplot(app);
end

    % Value changed function: DropDown_axis
    function DropDown_axisValueChanged(app, event)
        value = app.DropDown_axis.Value;
        switch value
            case '普通'
                axis normal;
                app.Code_Axis = 'axis normal;    %坐标轴普通模式';
            case '自动'
                axis auto;
                app.Code_Axis = 'axis normal;    %坐标轴自动模式';
            case '单位长度相同'
                axis equal
                app.Code_Axis = 'axis normal;    %坐标轴单位长度相同模式';
            case '紧凑'
                axis tight;
                app.Code_Axis = 'axis normal;    %坐标轴紧凑模式';
            case '紧绕数据'
                axis image;
                app.Code_Axis = 'axis normal;    %坐标轴紧绕数据模式';
            case '方形'
                axis square;
                app.Code_Axis = 'axis normal;    %坐标轴方形模式';
        end
        PHplot(app);
    end

4.3图窗保存功能
对图窗的保存的核心为print函数,通过得到文件路径,文件名,文件格式,清晰度等并在print函数中组合起来实现图窗的保存。
文件路径选择按钮回调核心为uigetdir函数,通过app.EditField_FilePath.Value = uigetdir(pwd,‘Select a Folder’),将浏览的路径存到app.EditField_FilePath.Value值中,实现属性改变,代码如下:
% Button pushed function: Button_PathSelect
function Button_PathSelectPushed(app, event)
if app.EditField_FilePath.Value == 0
msgbox(‘请选择一个文件夹,默认存储当前文件夹下’);
app.EditField_FilePath.Value = pwd;
else
try
app.EditField_FilePath.Value = uigetdir(pwd,‘Select a Folder’);
catch
msgbox(‘请选择一个文件夹,默认存储当前文件夹下’,‘Attention’);
app.EditField_FilePath.Value = pwd;
end
end
end
文件名若没有更改,即app.PicFileNameFlag值为0,则调用datetime(now),默认‘未命名+当前时间作为文件名’,代码如下:
If app.PicFileNameFlag ~= 1
app.PH.FileName =strcat(‘未命名’,strrep(char(datetime(‘now’)),’:’,’-’));
app.EditField_Filename.Value = app.PH.FileName;%默认输出’未命名+时间’
end
文件格式通过switch对应每种选择的格式,得到app.FormatValue1与app.FormatValue2。
function DropDown_FileformatValueChanged(app, event)
value = app.DropDown_Fileformat.Value;
switch value
case ‘.png’
app.FormatValue1 = ‘-dpng’;
app.FormatValue2 = ‘.png’;
case ‘.jpg’
app.FormatValue1 = ‘-djpeg’;
app.FormatValue2 = ‘.jpg’;
case ‘.eps’
app.FormatValue1 = ‘-depsc’;
app.FormatValue2 = ‘.eps’;
case ‘.ps’
app.FormatValue1 = ‘-dpsc’;
app.FormatValue2 = ‘.ps’;
case ‘.pdf’
app.FormatValue1 = ‘-dpdf’;
app.FormatValue2 = ‘.pdf’;
end
end
清晰度通过switch对应每种选择的格式,得到app.Difinition。
% Value changed function: DropDown_DefinitionChoose
function DropDown_DefinitionChooseValueChanged(app, event)
value = app.DropDown_DefinitionChoose.Value;
switch value
case ‘VIP至尊清晰’
app.Difinition = ‘-r1200’;
case ‘超清’
app.Difinition = ‘-r900’;
case ‘高清’
app.Difinition = ‘-r600’;
case ‘标清’
app.Difinition = ‘-r300’;
end
end
最终,通过【输出】按钮的回调将上述参数整合起来,进行输出,代码如下:
% Button pushed function: Button_FilePrint
function Button_FilePrintPushed(app, event)
app.FinalPath = strcat(app.EditField_FilePath.Value,’’,app.PH.FileName,app.FormatValue2);
print(gcf,app.FormatValue1,app.Difinition,app.FinalPath);
app.Code_Print = strcat(‘print(gcf,’’’,app.FormatValue1,’’’,’’’,app.Difinition,’’’,’’’,app.FinalPath,’’’);%图片保存’);
end
4.4一些与绘图有关的小工具
与绘图有关小工具的背后其实还是对函数的调用,下面小工具的实现依次如下:
4.4.1 生成测试图
点击【生成测试图】按钮,调用画正余弦曲线的代码:
% Button pushed function: Button_testpic
function Button_testpicPushed(app, event)
x = linspace(-2pi,2pi);
y1 = sin(x);
y2 = cos(x);
plot(x,y1,x,y2);
figure(gcf);
end
4.4.2 当前图窗显示
点击【当前图窗显示】按钮,通过gcf找到当前图窗,figure(gcf)显示出来:
% Button pushed function: Button_findcurrentgcf
function Button_findcurrentgcfPushed(app, event)
figure(gcf);
end
4.4.3 关闭所有图窗
点击【关闭所有图窗】按钮,调用close all:
% Button pushed function: Button_deleteallpic
function Button_deleteallpicPushed(app, event)
close all;
end
4.4.4 关闭当前图窗
点击【关闭当前图窗】按钮,调用close,关闭当前图窗:
% Button pushed function: Button_deletenowpic
function Button_deletenowpicPushed(app, event)
close;
end

4.4.5 Plot函数自查表
点击【Plot函数自查表】按钮,调用画Plot函数自查表的代码,此代码源于https://github.com/Pjer-zhang/matlabPlotCheatsheet,存为名为matlabplotsheet.m的函数,放在与.mlapp同路径的文件夹内:
% Button pushed function: Button_plotsheet
function Button_plotsheetPushed(app, event)
matlabplotsheet;
end
4.4.6 背景一键变白
点击【背景一键变白】按钮,通过set函数对当前gcf的属性进行更改:
% Button pushed function: Button_backgroundwhite
function Button_backgroundwhitePushed(app, event)
set(gcf,‘Color’,‘w’);
end
4.5代码生成
在每一个绘图函数调用下面再生成一段字符串,内容与代码一致,通过strcat函数字符串拼接的方式拼起来,最终整合成app.Final_Code元胞数组,存储所有的代码。
下面是总代码生成,分为Code_Title、Code_Xlabel、Code_Ylabel、Code_Legend、Code_Grid、Code_Axis、Code_Print七个部分,用newline实现换行功能:
function Codegenerate(app)
app.Final_Code = [app.Code_Title newline app.Code_Xlabel newline app.Code_Ylabel newline…
app.Code_Legend newline app.Code_Grid newline app.Code_Axis newline app.Code_Print];
app.Text_Code.Value = app.Final_Code;
end
分别的,在各自绘图代码实现下面加一行字符串的生成,增加后核心代码如下所示:
function PHplot(app)% 绘制回调
initPlotHelper(app);
if app.InterpreterMode == 0
xlabel(app.PH.PicXlabel,‘interpreter’,‘tex’);
app.Code_Xlabel = strcat(‘xlabel(’’’,app.PH.PicXlabel,’’’,’‘interpreter’’,’‘tex’’);%横轴显示’);
ylabel(app.PH.PicYlabel,‘interpreter’,‘tex’);
app.Code_Ylabel = strcat(‘ylabel(’’’,app.PH.PicYlabel,’’’,’‘interpreter’’,’‘tex’’);%纵轴显示’);
title(app.PH.PicTitle,‘interpreter’,‘tex’);
app.Code_Title = strcat(‘title(’’’,app.PH.PicTitle,’’’,’‘interpreter’’,’‘tex’’);%标题显示’);
if app.LegendMode == 0
legend off
app.Code_Legend = ‘legend off %不显示图例’;
else
legend(app.PH.PicLegend,‘interpreter’,‘tex’);
app.Code_Legend = strcat(‘legend(’’’,app.PH.PicLegend,’’’,’‘interpreter’’,’‘tex’’);%图例显示’);
end
else
xlabel(app.PH.PicXlabel,‘interpreter’,‘latex’);
app.Code_Xlabel = strcat(‘xlabel(’’’,app.PH.PicXlabel,’’’,’‘interpreter’’,’‘latex’’);%横轴显示’);
ylabel(app.PH.PicYlabel,‘interpreter’,‘latex’);
app.Code_Ylabel = strcat(‘ylabel(’’’,app.PH.PicYlabel,’’’,’‘interpreter’’,’‘latex’’);%纵轴显示’);
title(app.PH.PicTitle,‘interpreter’,‘latex’);
app.Code_Title = strcat(‘title(’’’,app.PH.PicTitle,’’’,’‘interpreter’’,’‘latex’’);%标题显示’);
if app.LegendMode == 0
legend off
app.Code_Legend = ‘legend off %不显示图例’;
else
legend(app.PH.PicLegend,‘interpreter’,‘latex’);
app.Code_Legend = strcat(‘legend(’’’,app.PH.PicLegend,’’’,’‘interpreter’’,’‘latex’’);%图例显示’);
end
end
Codegenerate(app);
end

    % Value changed function: DropDown_grid
    function DropDown_gridValueChanged(app, event)
        value = app.DropDown_grid.Value;
        if strcmp(value,'关闭')
            grid off;
            app.Code_Grid = 'grid off; %不显示网格线';
        elseif strcmp(value,'主网格线')
            grid on;
            app.Code_Grid = 'grid on; %显示主网格线';
        else
            grid minor;
            app.Code_Grid = ['grid on;   %显示主网格线' newline 'grid minor;   %显示次网格线'];
        end
        PHplot(app);
    end

    % Value changed function: Switch_axis
    function Switch_axisValueChanged(app, event)
        value = app.Switch_axis.Value;
        if strcmp(value,'On')
            axis on;
            app.Code_Axis = 'axis on;   %坐标轴显示';
        else
            axis off;
            app.Code_Axis = 'axis off;   %坐标轴不显示';
        end
        PHplot(app);
    end

    % Value changed function: DropDown_axis
    function DropDown_axisValueChanged(app, event)
        value = app.DropDown_axis.Value;
        switch value
            case '普通'
                axis normal;
                app.Code_Axis = 'axis normal;    %坐标轴普通模式';
            case '自动'
                axis auto;
                app.Code_Axis = 'axis normal;    %坐标轴自动模式';
            case '单位长度相同'
                axis equal
                app.Code_Axis = 'axis normal;    %坐标轴单位长度相同模式';
            case '紧凑'
                axis tight;
                app.Code_Axis = 'axis normal;    %坐标轴紧凑模式';
            case '紧绕数据'
                axis image;
                app.Code_Axis = 'axis normal;    %坐标轴紧绕数据模式';
            case '方形'
                axis square;
                app.Code_Axis = 'axis normal;    %坐标轴方形模式';
        end
        PHplot(app);
    end

通过上述代码生成最终的代码后,在代码生成编辑框中显示,并通过键鼠模拟调用键盘的control+v实现代码的复制功能。代码如下:
% Button pushed function: Button_Copy
function Button_CopyPushed(app, event)
%提示对话框
msgbox(‘请先选中文字哦,2s后自动复制’,‘Please’);
pause(2)%等待2s
% 创建鼠标键盘模拟代码
import java.awt.Robot;
import java.awt.event.*;
vb=actxserver(‘wscript.shell’);
robot=Robot;
% 快捷键操作: control+c
vb.SendKeys(’^c’);
end

5实现效果展示
最终,PlotHelper设计完成,可以实时进行图片编辑与代码生成,如图 5-1,并快捷复制粘贴图片与代码,如图 5-2,很方便快捷。

图 5-1 实时编辑与代码生成

图 5-2快捷复制粘贴

6实践总结及感想
课程感想:为期八周的Matlab GUI设计课程就到此结束了,自己其实打了一年数模还是有一定Matlab基础的,但就是不太系统,因为是用一块学一块的。上课时老师系统的输出总能让我学到一些新的东西,还学会了app的制作,做了音乐播放器和这个PlotHelper,收获蛮大的。
实践总结:其实app的制作还是要耗费不少心血的,从最开始有这个想法,到中间制作时不断试错尝试新方法,到最终app功能实现完毕,设计了个app专属图标,图标也花了半小时,如图 6-1,并写完整的实践报告,这其中前前后后总共花的时间加起来也不少于24小时了。

图 6-1自己设计的PlotHelper图标
之前自己用matlab做数模时,最后一步可视化都是要画图的,但一步很难画出满意的图,就要不断地调,要么重新运行,要么存数据一遍遍跑,看图怎么样,好看与否,调各种线型啊颜色啊标题啊等等,最后再输出,一遍遍重复的工作令我感觉不免有点枯燥,于是便诞生了制作Plot Helper的想法。从想法诞生到最终开花结果,现在想来还是很感动的。
不过自己这个功能有限,没有实现线型选择,粗细调整等等功能,最近关注了个up主“图通道”,做了个FigureBest软件,能美化各种图,二维三维图,还能一键设置线型等等,感觉自己就小巫见大巫了,要学的东西还是有很多的,可能以后还能增加新的功能。
7课程建议
最开始上这个课的时候其实有点疑惑,就是我觉得学appdesigner默认是有一定matlab基础的,没想到最开始前四周还是学基础的操作等等。
如果再来一次,我想可不可以在课程说明上注明有一定Matlab基础者可选,这样就会有更多时间可以用来学appdesign和实操制作了。


版权声明:本文为m0_46581972原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。