2016-03-17 2 views
1

Я работаю над графическим интерфейсом (без GUIDE) в Matlab. Графический интерфейс в конечном итоге будет скомпилирован в приложение и выпущен моим сотрудникам.Matlab - Есть ли способ захватить сообщения, отправленные в рабочее пространство?

Мой GUI вызывает некоторые пользовательские функции, которые я написал много лет назад. Эти пользовательские функции отображают сообщения о статусе/ходе выполнения в окне рабочей области.

Как я понимаю, я мог иметь мой исполняемый писать эти сообщения в лог-файл, а затем, что оставляет пользователю без каких-либо обновлений статуса на GUI в то время как программа работает.

Я работаю над довольно интенсивными 3D-манипуляциями с данными, которые могут работать в течение 5-10 минут между вызовами функций, поэтому, хотя я мог бы предоставлять обновления статуса между вызовами функций, он все равно оставляет конечного пользователя без идея, что происходит и/или внешний вид, который программа заблокировала.

Что бы я хотел сделать, это иметь что-то похожее на метод 'try-catch', где я могу выполнить функцию и захватить сообщения, предназначенные для рабочей области, и перенаправить их в текстовое поле uicontrol.

: EDIT:

Я добавляю это для потомков, в том случае, кто хочет использовать его. Это функциональная демонстрация, которая показывает, как выполнить ответ Питера ниже.

Во-первых, создать и сохранить функцию под названием «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:

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. Я просто добавляю переменные в структуру ручек для wh навсегда мне нужно. В этом случае это было бы handles.abortRequest = false;. Однако оказывается, что когда я передаю handles функции EndlessLoop, он становится устаревшим - ручки никогда не обновляются снова. Чтобы обойти это, мне пришлось хранить то, что я хотел в разделе UserData abortButton. Я думаю, что это потому, что дескриптор abortButton по-прежнему действителен, потому что он не изменился, и я получаю свежие UserData, потому что я могу опросить действительный дескриптор. Я не могу получить доступ к handles.abortRequest, потому что это не объект - я не могу опросить его; он просто существует и существует в «снимке», который был, когда я отправил дескрипторы в EndlessLoop. По крайней мере, это мое понимание.

  2. мне нужно установить 'Interruptible' свойство обратного вызова goButton к 'on', чтобы для abortButton функционировать в то время как процесс «висел» на EndlessLoop. Если Interruptible отключен, другие обратные вызовы могут быть обработаны до тех пор, пока этот конкретный обратный вызов не завершится, что никогда не произойдет с бесконечным циклом.

Итак, в заключение, это полный функциональный пример, который отвечает на мой вопрос. Спасибо Питеру за ответ - это также включает в себя его ProTip возможность передать вариант прерывания обратно в процесс, который занимает много времени. Я никогда не использовал обратные вызовы, подобные этому, поэтому, надеюсь, другие найдут это полезным в будущем.

+0

['evalc'] (http://es.mathworks.com/help/matlab/ref/evalc.html)? –

+1

Если вы можете изменить старые подпрограммы, вы можете разрешить им принимать функцию обратного вызова для обновлений состояния, которая по умолчанию будет отображаться в консоли, но может быть переопределена, чтобы быть функцией в вашем графическом интерфейсе. Если ваши подпрограммы могут определить приблизительный процентный коэффициент, поставьте его непосредственно на обратный вызов, чтобы графический интерфейс пользователя мог создать 'waitbar' – Peter

+0

@Peter. Не могли бы вы рассказать подробнее? Я не совсем понимаю, что вы имеете в виду. Как мне настроить это? Я никогда не использовал обратные вызовы для функции, только для объектов uicontrol. – Chuck

ответ

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 

Вы можете сделать функцию вывода настраивается путем передачи функции ручки. Здесь я делаю это по желанию путем тестирования Наргин:

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, вы можете определять и передавать в другой выходной обратный вызов. Это может жить прямо в вашем графическом интерфейсе, чтобы функция имела доступ к объектам графического интерфейса, которые вам нужны.

ProTip: Кроме того, эта функция обратного вызова возвращает true или false, чтобы сигнализировать о желании пользователя прекратить выполнение. Затем GUI может отображать кнопку отмены, чтобы вернуть это возвращаемое значение, а долговременный код может периодически отправлять сообщение о состоянии и проверять, было ли запрошено прерывание.

+0

Я редактировал вопрос, чтобы включить функциональный пример вашего метода. Это все, что я надеялся сделать, и многое другое - возможность передать вариант прерывания обратно в длительные процессы - это огромная помощь! – Chuck

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 
+0

Включается ли таймер как прерывание? Я имею в виду, что проблема, с которой я сталкиваюсь с 'evalc', что @LuisMendo упоминает в своем комментарии, заключается в том, что я не думаю, что получаю вывод evalc до завершения функции, а это значит, что динамическое обновление отсутствует, а функция Бег. Я не использовал таймеры вне tic/toc, поэтому я не знаком с их работой. – Chuck

+0

@Chuck. Они сами не вызывают прерывания, поэтому, если у вас есть долго работающая задача, вы, скорее всего, захотите разместить «оттянуть» после вывода печати в командную строку, которая будет скрывать очередь событий MATLAB и разрешить обратный вызов таймера (и графическое обновление). – Suever

Смежные вопросы