2

i am using GUI to call a terminal command. By using dos(my_command, '-echo') i can get the command output in Matlab's Command Window. Is there anyway to replicate that -echo in a static text in my GUI?

Currently, with my_command, I write the output to a log file, and update the static text's String by this log file after process finishes, but what I want is a live-view like in Command Window: output is displayed line by line in real-time. Thanks.

Update:

@Hoki: another question is: when the console commands are being executed, if I close the GUI then Matlab returns errors unstopable, how can I freeze the whole GUI until the commands are finished?

enter image description here

jarnosz
  • 243
  • 1
  • 18
scmg
  • 1,904
  • 1
  • 15
  • 24

2 Answers2

2

Matlab does not offer a native way to fetch results continuously during the execution of dos, unix and system. Nevertheless there is a possibility to achieve the desired behaviour.

This is the proposed work-around:

  • Execute your command and pipe the output to a file.
  • Use the & character to continue execution of the Matlab-code.
  • Read the contents of this file continuously and update the GUI accordingly.
  • Introduce a second temporary file indicating the end of the command to stop the update-process.

Here is the code:

% create figure and uicontrol
fh = figure;
txtbox = uicontrol(fh,'Style','edit','Max',30,'Min',0,...
                   'HorizontalAlignment','left',...
                   'FontName','FixedWidth',...
                   'Position',[30 30 450 200]);

% store current path
path = cd;

% delete token to make sure the loop continues as expected
warnstate = warning('off','MATLAB:DELETE:FileNotFound');
delete(fullfile(path,'temp_cmdlog_done'));
warning(warnstate); % restore original warning state

% execute dos-command in background
cmd = 'ping -c 5 192.168.200.1';
dos([cmd,' > temp_cmdlog && echo > temp_cmdlog_done &']);

% refresh text in uicontrol until dos-command is done
while 1
    out = fileread('temp_cmdlog');
    set(txtbox,'String',out);
    if exist(fullfile(path,'temp_cmdlog_done'),'file')
        break;
    end
    pause(0.2); % adjust to suit your needs
end

% delete the temporary files
delete(fullfile(path,'temp_cmdlog'));
delete(fullfile(path,'temp_cmdlog_done'));

% indicate that process finished
set(txtbox,'ForegroundColor',[0,0.5,0]);
Matt
  • 12,848
  • 2
  • 31
  • 53
  • but using `&` at the end will keep cmd-Window open in the background, is there anyway to avoid it? – scmg Jul 09 '15 at 12:26
  • btw, i think it should be a single `&` in `> temp_cmdlog && echo > temp_cmdlog_done` – scmg Jul 09 '15 at 12:28
  • @scmg To test the above script I used OSX and the `system`-command. This is the reason I used `&&` because the other versions did not work for me. If you confirm it works with a single `&`, I'll change it. I don't know a direct solution on how to close the window. – Matt Jul 09 '15 at 14:37
2

Thanks to Yair Altman info in this article, I got something to work but it involves hacking into Matlab Java base objects (namely the command window).

commandWindowGui

It involves attaching a listener to the Matlab command window. Now be careful, save your work often and be prepared to kill Matlab process quite a few times until you get it right ... because every time you have an error in the code you are stuck in a kind of infinite loop (the error/warning is sent to the command window, which triggers your listener, which re-trigger the error etc ...). I had to restart Matlab a good dozen of times just to get the example below to work in a stable way.

That's also the reason why I only attach the listener temporarily. just before sending the dos command and I remove the listener directly after. You can leave the listener permanently or adjust that to your needs but remember the advice from just above. Also consider that the command window can hold a massive amount of character, which you may not want all in your textbox, so you have to manage the text that you get from it (take a subset as in the example), and consider if you want to append or simply refresh the text in the textbox.

The example below seems to be stable as it is, any modification at your own risks ;-)

After request in comment I added 3 functions:

  • An onCleanup. This is a Matlab functionality to allow last resort action in case something goes wrong (a kind of "catch all" mechanism). Heavily recommended for this kind of program using undocumented functions.

  • A myCloseRequestFcn which intercept the closing action of the window to remove the listener and avoid error loops.

  • A scroll_to_bottom function. This one allows to move the text box caret to the end of the text (= to scroll to the bottom in case there is more text than visible space).

Warning: The last functionality could deserve a separate question and again call for undocumented functionality (so the compatibility is never guaranteed). To be able to implement it you need to have the findjobj function available in your Matlab path. If you do not want to download external component, then delete/comment the part of code that uses it and the subfunction scroll_to_bottom (and forget about scrolling the text box, there is no way to do that in pure Matlab). Or you can pick the previous version of the code by looking at the edit history of the post.


function h = gui_MirrorCmdWindow

%% // just to remove the listener in case something goes wrong
closeup = onCleanup(@() cleanup);

%% // create figure and uicontrol
h.f = figure;
h.txtOut = uicontrol(h.f,'Style','edit','Max',30,'Min',0,...
                   'HorizontalAlignment','left',...
                   'FontName','FixedWidth',...
                   'Units','Normalized',...
                   'Enable','On',...
                   'Position',[.05 .2 .9 .75]);

h.btnPing = uicontrol(h.f,'Style','Pushbutton',...
                   'String','Ping',...
                   'Units','Normalized',...
                   'Position',[.05 .05 .9 .1],...
                   'Callback',@btnPing_callback);

guidata(h.f,h)

%// intercept close request function to cleanup before close
set(gcf,'CloseRequestFcn',@myCloseRequestFcn) 

%% // Get the handle of the Matlab control window
jDesktop = com.mathworks.mde.desk.MLDesktop.getInstance;
jCmdWin = jDesktop.getClient('Command Window');
jTextArea = jCmdWin.getComponent(0).getViewport.getView;

%% // Get the handle of the jave edit box panel component
jtxtBox = findjobj(h.txtOut) ;
jTxtPane = jtxtBox.getComponent(0).getComponent(0) ;

%// Save these handles
setappdata( h.f , 'jTextArea', jTextArea ) ;
setappdata( h.f , 'jTxtPane',  jTxtPane ) ;


function btnPing_callback(hobj,~)
    h = guidata(hobj) ;
    jTextArea = getappdata( h.f , 'jTextArea' ) ;

    my_command = 'ping google.com -n 10' ;
    startPos = jTextArea.getCaretPosition ;
    set(jTextArea,'CaretUpdateCallback',{@commandWindowMirror,h.f,startPos}) ;
    dos( my_command , '-echo' ) ;
    pause(1) %// just to make sure we catch the last ECHO before we kill the callback
    set(jTextArea,'CaretUpdateCallback',[]) ;
    scroll_to_bottom(h.f)


function commandWindowMirror(~,~,hf,startPos)
    h = guidata(hf) ;
    jTextArea = getappdata( h.f , 'jTextArea' ) ;

    %// retrieve the text since the start position
    txtLength = jTextArea.getCaretPosition-startPos ;
    if txtLength > 0 %// in case a smart bugger pulled a 'clc' between calls
        cwText = char(jTextArea.getText(startPos-1,txtLength) ) ; 
    end
    %// display it in the gui textbox
    set( h.txtOut, 'String',cwText ) ; 
    scroll_to_bottom(h.f)


function scroll_to_bottom(hf)
    %// place caret at the end of the texbox (=scroll to bottom)
    jTxtPane  = getappdata( hf , 'jTxtPane' ) ;
    jTxtPane.setCaretPosition(jTxtPane.getDocument.getLength)


function myCloseRequestFcn(hobj,~)
    cleanup ;       %// make sure we remove the listener
    delete(hobj) ;  %// delete the figure


function cleanup
    jDesktop = com.mathworks.mde.desk.MLDesktop.getInstance;
    jCmdWin = jDesktop.getClient('Command Window');
    jTextArea = jCmdWin.getComponent(0).getViewport.getView;
    set(jTextArea,'CaretUpdateCallback',[]) ;
Hoki
  • 11,637
  • 1
  • 24
  • 43
  • i will try to mix this into my GUI first and come back – scmg Jul 09 '15 at 12:54
  • i did try and still have a question: if the log is too long, how can i set the text position at the end? – scmg Jul 09 '15 at 16:32
  • @scmg. I don't understand your question. Do you mean set the beginning position of the copy at the end of the command window ? – Hoki Jul 09 '15 at 16:35
  • no, i mean: in case the output in DOS is too long, then the textbox always show the first part of the log, how can i make it show the last part of the log, like in the pic i updated in question? – scmg Jul 09 '15 at 17:31
  • i used your example and call the DOS 3 times, so the output exceeds the height of textbox, a scroll bar then shows up, but it is always at the top position and i cannot have a full `live-view` – scmg Jul 09 '15 at 17:35
  • @scmg. Answer edited to protect against figure closing and to scroll textbox – Hoki Jul 09 '15 at 19:07
  • Thank you so much for your addendum, I would vote 3 times if I could! And sorry that i didn't split into 2 questions, since they seem close relevant. For the `scroll_to_bottom` part, i also just found on *undocumentedmatlab.com*, however for the freezing part, i would use the `enableDisableFig` of same author on *Matlab fileexchange*, since with just the `onCleanup` part, user still can close GUI before all parts after DOS commands (DOS commands are not everything in my button's callback) are not yet finished, which also results in errors. Thanks again for everything. – scmg Jul 09 '15 at 21:19
  • @scmg, for the freeze, you don't need to resort to external function. You can set a flag before you start the `dos` command, then in the `CloseRequestFcn`, you just do nothing as long as the flag is not cleared (don't forget to clear the flag when the `dos` command is finished though). – Hoki Jul 10 '15 at 08:55
  • sorry, i meant not only close GUI but also interaction with other components, any other simpler way than that external function? – scmg Jul 10 '15 at 09:04
  • 1
    Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/82911/discussion-between-hoki-and-scmg). – Hoki Jul 10 '15 at 09:19