我在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,因为我能够使用有效句柄进行轮询。我无法访问handle.abortRequest,因为它不是一个对象 - 我无法轮询它;它只是存在,它存在于&#34;快照&#34;就在我向EndlessLoop发送句柄的时候。至少,这是我的理解。
我需要将goButton回调的'Interruptible'
属性设置为'on'
,以便abortButton在进程&#34;挂起&#34;在EndlessLoop上。在Interruptible设置为off的情况下,在特定回调完成之前,不会处理任何其他回调,这在无限循环中永远不会发生。
因此,总而言之,这是一个完整的功能性示例,可以回答我的问题。感谢Peter的回答 - 这也包括他的ProTip能够将中止选项传回给需要很长时间才能完成的过程。我以前从未使用过像这样的回调,所以希望其他人会在将来发现它有用。
答案 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可以提供取消按钮来驱动该返回值,长时间运行的代码可以定期发送状态消息并检查是否已请求中止。