根据目前所了解到的资料,MATLAB调用多线程较为麻烦,并且类似parfor等语法只适用于大规模运算,而不适合两个独立的、需要并行的任务。这时,我们就需要使用Timer变量来实现多线程的效果。在这一小节中,我们以最基本的功能——实时显示当前时间——为例,介绍Timer变量的使用。显而易见,我们需要显示时间的功能代码自主运行,隔一定时间获取当前时间并显示,且不干扰其他任务的运行。
1.首先,在properties中添加一个定时器变量:
properties (Access = public)
ser; % Serial
serialname;
model; % To store the model data
baud = 115200; % baud rate
datetimer; % To show time
SerialData;
end
我们使用datetimer创建一个Timer变量,来实现实时显示时间的功能。
2.在常用控件中找到“标签”控件,将其拖拽进画布,并在控件树中将其命名为“ShowTimeLabel”:
注意,蓝色框框出的Label的属性“Text”即为该标签显示在画布上的内容。在Timer回调函数中,我们需要调用这一属性并给它赋值,实现显示时间的效果。
3.关于Timer变量的创建和回调函数的编写,我将放在下一节StartupFcn回调进行说明。
有些功能我们需要在App启动的时候就将其开启,例如实时显示时间的功能。这时,我们需要使用StartupFcn回调函数来实现。
1.添加StartupFcn回调:在控件树中找到最高层级(也就是我们的.mlapp工程),右键单击,找到“回调”,点击“添加StartupFcn回调”,如下图所示:
2.转到代码视图:
在这个白色代码块里,我们就可以添加我们的功能代码了。
3.创建定时器变量并开启定时器
首先,我们需要创建定时器变量,并配置它的一些基本属性。在StartupFcn回调函数中,添加如下代码:
% Code that executes after component creation
function startupFcn(app)
% 删除现存的定时器变量。这个函数也可以用来关闭定时器
delete(timerfind);
% 配置并开启显示时间的定时器
app.datetimer = timer;
% 开启定时器代码运行后,延时1s再调用回调函数
app.datetimer.StartDelay = 1;
% 定时器每隔1s调用一次回调函数
app.datetimer.Period = 1;
% 定时器的工作模式,读者可参考官方文档查阅不同模式的区别。此模式下,定时器会循环运行
app.datetimer.ExecutionMode = 'fixedRate';
app.datetimer.BusyMode = 'queue';
app.datetimer.TimerFcn = @(~,~) app.datetimer_handler;
start(app.datetimer);
end
4.然后在MATLAB Appdesigner开发独立桌面App全流程(一)中第7节我们所添加的methods块中,添加一个公共函数:
function datetimer_handler(app)
CT = clock; % Current Time 当前时间
set(app.ShowTimeLabel,"Text",['当前时间:',...
num2str(CT(1)),'年',num2str(CT(2),"%02d"),'月',num2str(CT(3),"%02d"),'日 ',...
num2str(CT(4),"%02d"),': ',num2str(CT(5),"%02d"),': ',num2str(CT(6),"%02.f")]);
end
这个函数就是定时器变量需要调用的回调函数了。在上面的代码块中,关于时间调用使用了clock函数,并将其转化成字符串显示在我们在第1节创建的标签控件当中。num2str函数的第二个输入参数规定了显示形式和位数。关于这两个函数的使用,读者可以参考官方文档。
5.运行App,效果如下:
同理,以此类推,我们可以使用定时器实现更加复杂的功能,比如多个后台任务的调度等。读者可以根据自己的需要,添加相应的定时器变量和对应的回调函数,以实现自己的项目需求。
考虑这样一种情况:当我们的App打包好了并正常运行起来之后,在使用过程中,因为操作失误而出现报错;或者,干脆就是因为代码存在一些在测试环境中没有检查到的bug。这个时候,我们又该如何debug呢?毕竟这时不会再有MATLAB命令行来帮助我们了。这个时候,我们就需要引入try catch来将这个错误抛出来供我们判断。
1.将MATLAB Appdesigner开发独立桌面App全流程(一)中第8节“打开串口”按钮的回调函数修改为如下的结构:
% Button pushed function: OpenSerialButton
function OpenSerialButtonPushed(app, event)
try
serName = app.serialname(app.SeriallistDropDown.Value);
app.baud = app.BaudRateDropDown.Items{app.BaudRateDropDown.Value};
app.ser = serialport(serName,str2double(app.baud));
%% 清除缓冲区
flush(app.ser);
%% 设置中断回调
configureTerminator(app.ser,"CR/LF");
configureCallback(app.ser,"terminator",@app.SerialReceive_Callback);
catch ME
report = getReport(ME);
uialert(app.UIFigure,report,'Error Message','Interpreter','html');
end
end
这样,我们就可以将错误信息以弹窗的形式显示在图窗上。运行App,效果如下所示: