【发布时间】:2016-03-17 13:20:06
【问题描述】:
我正在使用 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;
我在尝试让它工作时发现了几件事:
我只是将变量添加到句柄结构中以满足我的需要。在这种情况下,它将是
handles.abortRequest = false;。但是,当我将handles传递给 EndlessLoop 函数时,它似乎变得陈旧 - 句柄不再更新。为了解决这个问题,我必须将我想要的内容存储在 abortButton 的 UserData 部分中。我认为这是因为 abortButton 的句柄仍然有效,因为它没有改变,并且我得到了新的 UserData 因为我能够使用有效的句柄进行轮询。我不能访问handles.abortRequest,因为它不是一个对象——我不能轮询它;它只是存在,它存在于我将句柄发送到 EndlessLoop 时的“快照”中。至少,这是我的理解。我需要将 goButton 回调的
'Interruptible'属性设置为'on',以便 abortButton 在进程“挂起”在 EndlessLoop 上时起作用。将 Interruptible 设置为 off 后,在该特定回调完成之前,不会处理其他回调,而无限循环永远不会发生这种情况。
因此,总而言之,这是一个完整的功能示例,可以回答我的问题。感谢 Peter 的回答 - 这还包括他的 ProTip,即能够将中止选项传递回需要很长时间才能完成的过程。我以前从未使用过这样的回调,所以希望其他人将来会发现这很有用。
【问题讨论】:
-
如果您可以修改旧例程,您可以允许它们接受状态更新的回调函数,这将默认为控制台显示,但可以被覆盖为您的 GUI 中的函数。如果您的例程可以计算出大致的完成百分比,请将其直接提供给回调,以便 GUI 可以创建
waitbar -
@Peter - 你能详细说明一下吗?我不完全确定你的意思。我将如何配置它?我从来没有对函数使用回调,只对 uicontrol 对象使用。
-
@LuisMendo -
evalc不会等到函数完成后再向我提供文本吗?在函数完成之前,我不知道如何使用它。 -
@Chuck 是的,一直等到函数完成
标签: matlab