Matlab - 有没有办法捕获发送到工作区的消息?

时间:2016-03-17 13:20:07

标签: matlab

我在Matlab中使用GUI(没有GUIDE)。 GUI最终将被编译到应用程序并发布给我的同事。

我的GUI调用了我很久以前写过的一些自定义函数。这些自定义函数将状态/进度消息显示到工作区窗口。

根据我的理解,我可以让我的可执行文件将这些消息写入日志文件,但是这会让用户在GUI上没有任何状态更新 程序正在运行。

我正在进行一些非常密集的3D数据操作,它有可能在函数调用之间运行5-10分钟,所以虽然我可以在函数调用之间提供状态更新,但它仍然留给最终用户不知道发生了什么和/或程序锁定的外观。

我想做的是做一些类似于' try-catch'方法,我可以通过某种方式执行函数并捕获用于工作空间的消息,并将它们重定向到uicontrol文本框。

:编辑:

如果有人想要使用它,我会为后代添加这个。这是一个功能演示,展示了如何在下面实现彼得的答案。

首先,创建并保存一个名为" EndlessLoop":

的函数
function EndlessLoop(handles,loopCallback)
if nargin<1
    handles = [];
    loopCallback = @loop_Callback;
else
    disp('Callback already set!');
end
tic;
abort = false;
while true
    statusText = sprintf('Current Elapsed Time:\n%.2f',toc);
    abort = loopCallback(handles,statusText);
    if abort
        statusText = sprintf('Abort request processed.\nEnding now.');
        [~] = loopCallback(handles,statusText);
        break;
    end
    pause(0.1);
end
return;

function abort = loop_Callback(~,myText)
clc;
abort = false;
disp(myText)
return;

然后,创建一个调用EndlessLoop的GUI:

function TestGUI
close all;
myTest = figure('Visible','on','Units','normalized','Position',[0.1 0.1 0.8 0.8],'Name','Test GUI','NumberTitle','off');
set(myTest,'menubar','none');
handles = guihandles(myTest);

handles.goButton = uicontrol('Style','pushbutton','Units','normalized','Position',[0 0.5 0.5 0.5],'String','Go');
handles.abortButton = uicontrol('Style','pushbutton','Units','normalized','Position',[0 0 0.5 0.5],'String','Abort','Enable','off');
handles.statusText = uicontrol('Style','text','Units','normalized','Position',[0.5 0 0.5 1],'String','Press Go when ready.');

set(handles.goButton,'Callback',@goButton_Callback,'interruptible','on');
set(handles.abortButton,'Callback',@abortButton_Callback,'interruptible','on');
handles.abortButton.UserData = false;
guidata(myTest,handles);
return;

function goButton_Callback(hObject,~)
handles = guidata(gcbo);
hObject.Enable = 'off';
handles.abortButton.Enable = 'on';
EndlessLoop(handles,@StatusUpdate)
handles.abortButton.String = 'Abort';
handles.abortButton.Enable = 'off';
hObject.Enable = 'on';
pause(0.5);
handles.statusText.String = 'Press Go to start again.';
handles.abortButton.UserData = false;
guidata(gcbo,handles);
return;

function abortButton_Callback(hObject,~)
handles = guidata(gcbo);
if handles.abortButton.UserData
    handles.abortButton.UserData = false;
    hObject.String = 'Abort';
else
    handles.abortButton.UserData = true;
    hObject.String = sprintf('Abort pending...');
end
guidata(gcbo,handles);
return;

function abort = StatusUpdate(handles,statusText)
clc;
abort = handles.abortButton.UserData;
disp(handles.abortButton.UserData)
handles.statusText.String = statusText;
return;

在尝试使用它时,我发现了几件事情:

  1. 我一直在为句柄结构添加变量以满足我的需要。在这种情况下,它应该是handles.abortRequest = false;。但是,当我将handles传递给EndlessLoop函数时,它变得陈旧 - 句柄永远不会再次更新。为了解决这个问题,我必须在abortButton的UserData部分存储我想要的内容。我认为这是因为abortButton的句柄仍然有效,因为它没有改变,我获得了新的UserData,因为我能够使用有效句柄进行轮询。我无法访问handle.abortRequest,因为它不是一个对象 - 我无法轮询它;它只是存在,它存在于&#34;快照&#34;就在我向EndlessLoop发送句柄的时候。至少,这是我的理解。

  2. 我需要将goButton回调的'Interruptible'属性设置为'on',以便abortButton在进程&#34;挂起&#34;在EndlessLoop上。在Interruptible设置为off的情况下,在特定回调完成之前,不会处理任何其他回调,这在无限循环中永远不会发生。

  3. 因此,总而言之,这是一个完整的功能性示例,可以回答我的问题。感谢Peter的回答 - 这也包括他的ProTip能够将中止选项传回给需要很长时间才能完成的过程。我以前从未使用过像这样的回调,所以希望其他人会在将来发现它有用。

2 个答案:

答案 0 :(得分:1)

您可以使用内置的diary功能来完成类似于您尝试的操作。

diary on               % Uses "diary" for filename
diary('logfile.log')   % Custom filename

这会将所有命令行输出写入指定的文件。然后,您可以定期轮询此文件并使用内容更新uicontrol(如果需要,还可以更新最后几行)。

logfile = 'logfile.log';
diary(logfile);

u = uicontrol('style', 'text');

% Check every 5 seconds
t = timer('Period', 5, ...
          'ExecutionMode', 'FixedRate', ...
          'TimerFcn', @(s,e)populate(s));

start(t);

function populate(src)
    fid = fopen(logfile, 'rb');
    contents = fread(fid, '*char').';
    set(src, 'String', contents);
    fclose(fid);
end

答案 1 :(得分:1)

这个答案阐述了我对这个问题的评论。

假设您有将东西输出到命令窗口的功能:

function out = longcomputation(param1, param2)
%
while(for_a_long_time)
   process_one_step();
   percent_complete = something;
   fprintf('Step result: %f   Complete: %f', stuff, percent_complete);
end

您可以通过传递函数句柄来配置输出功能。在这里,我通过测试nargin来使它成为可选项:

function out = longcomputation(param1, param2, outputfcn)

function display_progress_to_console(msg, percentage)
  sprintf('%s, Complete %f', msg, percentage);
end

if(nargin < 3)  % Supply a default output function if none passed in
  outputfcn = @display_progress_to_console;
end

while(for_a_long_time)
  process_one_step();
  percent_complete = something;
  msg = sprintf('Step result: %f', stuff);
  outputfcn(msg, percent_complete);
end

现在,在GUI中,您可以定义并传入不同的输出回调。这可以直接存储在您的GUI代码中,以便该函数可以访问您需要的GUI对象。

ProTip:此外,让此回调函数返回true或false以表示用户希望中止。然后,GUI可以提供取消按钮来驱动该返回值,长时间运行的代码可以定期发送状态消息并检查是否已请求中止。