Undocumented Matlab
  • SERVICES
    • Consulting
    • Development
    • Training
    • Gallery
    • Testimonials
  • PRODUCTS
    • IQML: IQFeed-Matlab connector
    • IB-Matlab: InteractiveBrokers-Matlab connector
    • EODML: EODHistoricalData-Matlab connector
    • Webinars
  • BOOKS
    • Secrets of MATLAB-Java Programming
    • Accelerating MATLAB Performance
    • MATLAB Succinctly
  • ARTICLES
  • ABOUT
    • Policies
  • CONTACT
  • SERVICES
    • Consulting
    • Development
    • Training
    • Gallery
    • Testimonials
  • PRODUCTS
    • IQML: IQFeed-Matlab connector
    • IB-Matlab: InteractiveBrokers-Matlab connector
    • EODML: EODHistoricalData-Matlab connector
    • Webinars
  • BOOKS
    • Secrets of MATLAB-Java Programming
    • Accelerating MATLAB Performance
    • MATLAB Succinctly
  • ARTICLES
  • ABOUT
    • Policies
  • CONTACT

setPrompt – Setting the Matlab Desktop prompt

January 25, 2010 15 Comments

A few days ago, a reader emailed me with a challenge to modify the standard matlab Command-Window prompt from “>> ” to some other string, preferably a dynamic prompt with the current timestamp. At first thought this cannot be done: The Command-Window prompts are hard-coded and to the best of my knowledge cannot be modified via properties or system preferences.
So the prompt can (probably) not be modified in advance, but what if it could be modified after being displayed? It is true that my cprintf utility modifies the Command-Window contents in order to display formatted text in a variety of font colors. But this case is different since cprintf runs once synchronously (user-invoked), whereas the prompt appears asynchronously multiple times.
There are two methods of handling multiple asynchronous events in Matlab: setting a callback on the object, and setting a PostSet handle.listener (or schema.listener) on the relevant object property. The first of these methods is a well-known Matlab practice, although we shall see that it uses an undocumented callback and functionality; the PostSet method is entirely undocumented and not well-known and shall be described in some later article. I decided to use the callback method to set the prompt – interested readers can try the PostSet method.

Setting the Command Window’s callback

The solution involved finding the Command-Window reference handle, and setting one of its many callbacks, in our case CaretUpdateCallback. This callback is fired whenever the desktop text is modified, which is an event we trap to replace the displayed prompt:

% Get the reference handle to the Command Window text area
jDesktop = com.mathworks.mde.desk.MLDesktop.getInstance;
try
  cmdWin = jDesktop.getClient('Command Window');
  jTextArea = cmdWin.getComponent(0).getViewport.getComponent(0);
catch
  commandwindow;
  jTextArea = jDesktop.getMainFrame.getFocusOwner;
end
% Instrument the text area's callback
if nargin && ~isempty(newPrompt) && ~strcmp(newPrompt,'>> ')
  set(jTextArea,'CaretUpdateCallback',{@setPromptFcn,newPrompt});
else
  set(jTextArea,'CaretUpdateCallback',[]);
end

% Get the reference handle to the Command Window text area jDesktop = com.mathworks.mde.desk.MLDesktop.getInstance; try cmdWin = jDesktop.getClient('Command Window'); jTextArea = cmdWin.getComponent(0).getViewport.getComponent(0); catch commandwindow; jTextArea = jDesktop.getMainFrame.getFocusOwner; end % Instrument the text area's callback if nargin && ~isempty(newPrompt) && ~strcmp(newPrompt,'>> ') set(jTextArea,'CaretUpdateCallback',{@setPromptFcn,newPrompt}); else set(jTextArea,'CaretUpdateCallback',[]); end

Now that we have the Command-Window object callback set, we need to set the logic of prompt replacement – this is done in the internal Matlab function setPromptFcn. Here is its core code:

% Does the displayed text end with the default prompt?
% Note: catch a possible trailing newline
try jTextArea = jTextArea.java;  catch,  end  %#ok
cwText = get(jTextArea,'Text');
pos = strfind(cwText(max(1,end-3):end),'>> ');
if ~isempty(pos)
  % Short prompts need to be space-padded
  newLen = jTextArea.getCaretPosition;
  if length(newPrompt)<3
    newPrompt(end+1:3) = ' ';
  elseif length(newPrompt)>3
    fprintfStr = newPrompt(1:end-3);
    fprintf(fprintfStr);
    newLen = newLen + length(fprintfStr);
  end
  % The Command-Window text should be modified on the EDT
  awtinvoke(jTextArea,'replaceRange(Ljava.lang.String;II)',...
            newPrompt(end-2:end), newLen-3, newLen);
  awtinvoke(jTextArea,'repaint()');
end

% Does the displayed text end with the default prompt? % Note: catch a possible trailing newline try jTextArea = jTextArea.java; catch, end %#ok cwText = get(jTextArea,'Text'); pos = strfind(cwText(max(1,end-3):end),'>> '); if ~isempty(pos) % Short prompts need to be space-padded newLen = jTextArea.getCaretPosition; if length(newPrompt)<3 newPrompt(end+1:3) = ' '; elseif length(newPrompt)>3 fprintfStr = newPrompt(1:end-3); fprintf(fprintfStr); newLen = newLen + length(fprintfStr); end % The Command-Window text should be modified on the EDT awtinvoke(jTextArea,'replaceRange(Ljava.lang.String;II)',... newPrompt(end-2:end), newLen-3, newLen); awtinvoke(jTextArea,'repaint()'); end

In this code snippet, note that we space-pad prompt string that are shorter than 3 characters: this is done to prevent an internal-Matlab mixup when displaying additional text – Matlab “knows” the Command-Window’s text position and it gets mixed up if it turns out to be shorter than expected.
Also note that I use the semi-documented awtinvoke function to replace the default prompt (and an automatically-appended space) on the Event-Dispatch Thread (more on this in a future article). Since Matlab R2008a, I could use the more convenient javaMethodEDT function, but I wanted my code to work on all prior Matlab 7 versions, where javaMethodEDT was not yet available.

Preventing callback re-entry

The callback snippet above would enter an endless loop if not changed: whenever the prompt is modified the callback would have been re-fired, the prompt re-modified and so on endlessly. There are many methods of preventing callback re-entry – here’s the one I chose:

function setPromptFcn(jTextArea,eventData,newPrompt)
  % Prevent overlapping reentry due to prompt replacement
  persistent inProgress
  if isempty(inProgress)
    inProgress = 1;  %#ok unused
  else
    return;
  end
  try
    % *** Prompt modification code goes here ***
    % force prompt-change callback to fizzle-out...
    pause(0.02);
  catch
    % Never mind - ignore errors...
  end
  % Enable new callbacks now that the prompt has been modified
  inProgress = [];
end  % setPromptFcn

function setPromptFcn(jTextArea,eventData,newPrompt) % Prevent overlapping reentry due to prompt replacement persistent inProgress if isempty(inProgress) inProgress = 1; %#ok unused else return; end try % *** Prompt modification code goes here *** % force prompt-change callback to fizzle-out... pause(0.02); catch % Never mind - ignore errors... end % Enable new callbacks now that the prompt has been modified inProgress = []; end % setPromptFcn

Handling multiple prompt types

I now wanted my function to handle both static prompt strings (like: ‘[Yair] ‘) and dynamic prompts (like: ‘[25-Jan-2010 01:00:51] ‘). This is done by accepting string-evaluable strings/functions:

% Try to evaluate the new prompt as a function
try
  origNewPrompt = newPrompt;
  newPrompt = feval(newPrompt);
catch
  try
    newPrompt = eval(newPrompt);
  catch
    % Never mind - probably a string...
  end
end
if ~ischar(newPrompt) && ischar(origNewPrompt)
  newPrompt = origNewPrompt;
end

% Try to evaluate the new prompt as a function try origNewPrompt = newPrompt; newPrompt = feval(newPrompt); catch try newPrompt = eval(newPrompt); catch % Never mind - probably a string... end end if ~ischar(newPrompt) && ischar(origNewPrompt) newPrompt = origNewPrompt; end

File Exchange submission

I then added some edge-case error handling and wrapped everything in a single utility called setPrompt that is now available on the File Exchange.
In the future, if I find time, energy and interest, maybe I’ll combine cprintf‘s font-styling capabilities, to enable setting colored prompts.

Setting a continuously-updated timestamp prompt

Using the code above, we can now display a dynamic timestamp prompt, as follows:

setPrompt usage examples
setPrompt usage examples

However, the displayed timestamp is somewhat problematic in the sense that it indicates the time of prompt creation rather than the time that the associated Command-Window command was executed. In the screenshot above, [25-Jan-2010 01:29:42] is the time that the 234 command was executed, not the time that the setPrompt command was executed. This is somewhat misleading. It would be better if the last (current) timestamp was continuously updated and would therefore always display the latest command’s execution time. This can be done using a Matlab timer as follows:

% This is entered in the main function before setting the prompt:
stopPromptTimers;
if nargin && strcmpi(newPrompt,'timestamp')
  % Update initial prompt & prepare a timer to continuously update it
  newPrompt = @()(['[',datestr(now),'] ']);
  start(timer('Tag','setPromptTimer', 'Name','setPromptTimer', ...
              'ExecutionMode','fixedDelay', 'ObjectVisibility','off', ...
              'Period',0.99, 'StartDelay',0.5, ...
              'TimerFcn',{@setPromptTimerFcn,jTextArea}));
end
% Stop & delete any existing prompt timer(s)
function stopPromptTimers
  try
    timers = timerfindall('tag','setPromptTimer');
    if ~isempty(timers)
      stop(timers);
      delete(timers);
    end
  catch
    % Never mind...
  end
end  % stopPromptTimers
% Internal timer callback function
function setPromptTimerFcn(timerObj,eventData,jTextArea)
  try
    try jTextArea = jTextArea.java;  catch,  end  %#ok
    pos = getappdata(jTextArea,'setPromptPos');
    newPrompt = datestr(now);
    awtinvoke(jTextArea,'replaceRange(Ljava.lang.String;II)',...
              newPrompt, pos, pos+length(newPrompt));
    awtinvoke(jTextArea,'repaint()');
  catch
    % Never mind...
  end
end  % setPromptTimerFcn

% This is entered in the main function before setting the prompt: stopPromptTimers; if nargin && strcmpi(newPrompt,'timestamp') % Update initial prompt & prepare a timer to continuously update it newPrompt = @()(['[',datestr(now),'] ']); start(timer('Tag','setPromptTimer', 'Name','setPromptTimer', ... 'ExecutionMode','fixedDelay', 'ObjectVisibility','off', ... 'Period',0.99, 'StartDelay',0.5, ... 'TimerFcn',{@setPromptTimerFcn,jTextArea})); end % Stop & delete any existing prompt timer(s) function stopPromptTimers try timers = timerfindall('tag','setPromptTimer'); if ~isempty(timers) stop(timers); delete(timers); end catch % Never mind... end end % stopPromptTimers % Internal timer callback function function setPromptTimerFcn(timerObj,eventData,jTextArea) try try jTextArea = jTextArea.java; catch, end %#ok pos = getappdata(jTextArea,'setPromptPos'); newPrompt = datestr(now); awtinvoke(jTextArea,'replaceRange(Ljava.lang.String;II)',... newPrompt, pos, pos+length(newPrompt)); awtinvoke(jTextArea,'repaint()'); catch % Never mind... end end % setPromptTimerFcn

Can you come up with some innovative prompts? If so, please share them in a comment below.
Update 2010-Jan-26: The code in this article was updated since it was first published yesterday.

Related posts:

  1. Setting the Matlab desktop layout programmatically – The Matlab desktop enables saving and switching layouts using the main menu. This post shows how to do so programmatically....
  2. Setting desktop tab completions – The Matlab desktop's Command-Window tab-completion can be customized for user-defined functions...
  3. Setting status-bar text – The Matlab desktop and figure windows have a usable statusbar which can only be set using undocumented methods. This post shows how to set the status-bar text....
  4. EditorMacro v2 – setting Command Window key-bindings – The EditorMacro utility was extended to support built-in Matlab Editor and Command-Window actions and key-bindings. This post describes the changes and the implementation details....
  5. Setting listbox mouse actions – Matlab listbox uicontrol can be modified to detect mouse events for right-click context menus, dynamic tooltips etc....
  6. Setting system tray icons – System-tray icons can be programmatically set and controlled from within Matlab, using new functionality available since R2007b....
Callbacks Desktop Java Listener Semi-documented function Undocumented feature
Print Print
« Previous
Next »
15 Responses
  1. Mike January 25, 2010 at 19:18 Reply

    Very Interesting. Have you found any issues with operations that make assumptions about the prompt, such as tab completion, or copying/pasting to the Editor?

    • Yair Altman January 26, 2010 at 01:55 Reply

      Hi Mike – Since the original post I have discovered two minor edge cases – when clearing the window (clc) and when invoking other windows (e.g., edit). These issues have since been fixed in the posted code as well as on FEX (should be available later today I guess).

      The only unsolved issue I have seen is that when selecting desktop text and pasting to the Editor, the prompt is not stripped as it is with the default prompt.

      Otherwise, the new prompt appears to work exactly as expected, with copy/paste, selection/F9, tab completion, help popups, history/editing etc. The Editor-Desktop interaction also works exactly as expected (aside from the known issue above).

      I haven’t tested thoroughly on non-Windows platforms and/or multiple Matlab releases, but I expect the changes to be minor at worst.

      Regarding assumptions, there are two inherent: that the default prompt ends with ‘>> ‘ and that the desktop TextArea can be accessed as described (this depends on the undocumented internal hierarchy, which may change between releases). For the moment these assumptions seem valid.

  2. Yair Altman January 26, 2010 at 16:20 Reply

    Update: I have received a report that setPrompt fouls up the Command Window (CW) on Mac platforms. Since the utility’s code and the CW is Java-based, I have a feeling that the problems are due to bugs in the Java used on MacOS (there are numerous other problems with Java on Mac that affect Matlab). I can’t test this since I have no Mac.

    Oh well – I guess we’re left with using setPrompt on non-Mac platforms.

    I would love to hear specific details about similar compatibility issues.

    • Akhwand Abdul Kareem April 25, 2016 at 23:12 Reply

      do you have video of above for matlab16a on winwows 10
      if yes reply me on email p126060@nu.edu.pk

  3. Bass February 1, 2010 at 20:27 Reply

    Hey Yair, Great article on setPrompt

    I would like to catch everything that will be printed in the command window and be able to send it to a remote PC via udp.

    Can you suggest what callbacks/ approach may be useful in doing this?

    thanks!

    • Yair Altman February 2, 2010 at 00:16 Reply

      @Bassam – you can use a similar approach to setPrompt (you can use setPrompt‘s source code as the baseline) – set the command-window’s CaretUpdateCallback to send the new text to the remote PC and then store the existing text in a persistent variable. Your callback function will look something like this:

      function setPromptFcn(jTextArea,eventData)
       
        % Callback reentry-prevention
        persistent inProgress
        if isempty(inProgress)
          inProgress = 1;  %#ok unused
        else
          return;
        end
       
        % Preserve the last-processed text position
        persistent previousPos
        if isempty previousPos,  previousPos=0;  end
       
        % Process new text
        cwText = get(jTextArea,'Text');
       
        % Handle clear-window actions
        if length(cwText) < previousPos
          previousPos=0;
        end
       
        % Send the new text to the remote PC
        sendRemote(cwText(previousPos+1:end));
        previousPos = length(cwText);
       
        % Re-enable additional callback events
        inProgress = [];

      function setPromptFcn(jTextArea,eventData) % Callback reentry-prevention persistent inProgress if isempty(inProgress) inProgress = 1; %#ok unused else return; end % Preserve the last-processed text position persistent previousPos if isempty previousPos, previousPos=0; end % Process new text cwText = get(jTextArea,'Text'); % Handle clear-window actions if length(cwText) < previousPos previousPos=0; end % Send the new text to the remote PC sendRemote(cwText(previousPos+1:end)); previousPos = length(cwText); % Re-enable additional callback events inProgress = [];

  4. junziyang March 30, 2010 at 23:10 Reply

    Another GREAT work!
    Can this function be improved to make the user difined prompt as the default one instead of calling setPrompt to manually set on each MATLAB session?

    • Yair Altman March 31, 2010 at 08:21 Reply

      @junziyang – yes: you can add the setPrompt command to your startup.m function and it will be used in each Matlab session automatically.

      If you don’t have a startup.m file, simply create one in your Matlab startup folder – it’s a simple Matlab script file that contains commands that run automatically whenever Matlab starts in that folder.

  5. Rudolph June 23, 2010 at 08:18 Reply

    I’ve been looking for a decent prompt control/replacement for a *very* long time! This is fantastic!

    I do have one question/request. Mostly I run MatLAB in a term (gnome-terminal/konsole/whatever) and not from the MatLAB GUI (matlab -nosplahs -nodesktop).

    Would your approach work for modifying the prompt in a console? Maybe something like setPromptCon..?

    • Yair Altman June 23, 2010 at 09:18 Reply

      @Rudolph – thanks.
      Re the console, I don’t know. You’re welcome to try and possibly modify the source code. If you find anything useful, please send me

  6. Steven Baete August 29, 2014 at 13:11 Reply

    Great post!
    It helped me to show the command line output in my own GUI.

    % to read the matlab output into our GUI
    jDesktop = com.mathworks.mde.desk.MLDesktop.getInstance;
    jCmdWin = jDesktop.getClient('Command Window');
    jTextArea = jCmdWin.getComponent(0).getViewport.getView;
    set(jTextArea,'CaretUpdateCallback',{@cwtext_Call,handles});

    % to read the matlab output into our GUI jDesktop = com.mathworks.mde.desk.MLDesktop.getInstance; jCmdWin = jDesktop.getClient('Command Window'); jTextArea = jCmdWin.getComponent(0).getViewport.getView; set(jTextArea,'CaretUpdateCallback',{@cwtext_Call,handles});

    This worked fine in linux (Matlab R2013b,mint 17), however on the computer of a colleague of mine (a mac), I get the following error:

    The name 'CaretUpdateCallback' is not an accessible property for an instance of class 'com.mathworks.mde.cmdwin.XCmdWndView'.

    The name 'CaretUpdateCallback' is not an accessible property for an instance of class 'com.mathworks.mde.cmdwin.XCmdWndView'.

    It seems as the CaretUpdateCallback is not available on the mac (Matlab R2014a, Os X, v10.9.4), would you have a workaround for this?

    • Yair Altman August 30, 2014 at 10:05 Reply

      @Steven – read here: http://undocumentedmatlab.com/blog/matlab-callbacks-for-java-events-in-r2014a

      In short, add the following before calling the set command:

      jTextArea = handle(jTextArea,'CallbackProperties');

      jTextArea = handle(jTextArea,'CallbackProperties');

  7. How do you retrieve the selected text in MATLAB? - DexPage July 9, 2015 at 15:05 Reply

    […] thanks to @Yair Altman‘s undocumented Matlab, I was able to figure out the java commands to make this […]

  8. Steven Hunsinger March 15, 2019 at 01:52 Reply

    Does this work in Windows MATLAB R2018b? Or would someone mind demonstrating how to change the prompt in R2018b via command?

    • Yair Altman March 15, 2019 at 11:22 Reply

      @Steven – did you even bother to download and try setPrompt() before posting your comment?!
      It still works to this day (duh!)

Leave a Reply
HTML tags such as <b> or <i> are accepted.
Wrap code fragments inside <pre lang="matlab"> tags, like this:
<pre lang="matlab">
a = magic(3);
disp(sum(a))
</pre>
I reserve the right to edit/delete comments (read the site policies).
Not all comments will be answered. You can always email me (altmany at gmail) for private consulting.

Click here to cancel reply.

Useful links
  •  Email Yair Altman
  •  Subscribe to new posts (feed)
  •  Subscribe to new posts (reader)
  •  Subscribe to comments (feed)
 
Accelerating MATLAB Performance book
Recent Posts

Speeding-up builtin Matlab functions – part 3

Improving graphics interactivity

Interesting Matlab puzzle – analysis

Interesting Matlab puzzle

Undocumented plot marker types

Matlab toolstrip – part 9 (popup figures)

Matlab toolstrip – part 8 (galleries)

Matlab toolstrip – part 7 (selection controls)

Matlab toolstrip – part 6 (complex controls)

Matlab toolstrip – part 5 (icons)

Matlab toolstrip – part 4 (control customization)

Reverting axes controls in figure toolbar

Matlab toolstrip – part 3 (basic customization)

Matlab toolstrip – part 2 (ToolGroup App)

Matlab toolstrip – part 1

Categories
  • Desktop (45)
  • Figure window (59)
  • Guest bloggers (65)
  • GUI (165)
  • Handle graphics (84)
  • Hidden property (42)
  • Icons (15)
  • Java (174)
  • Listeners (22)
  • Memory (16)
  • Mex (13)
  • Presumed future risk (394)
    • High risk of breaking in future versions (100)
    • Low risk of breaking in future versions (160)
    • Medium risk of breaking in future versions (136)
  • Public presentation (6)
  • Semi-documented feature (10)
  • Semi-documented function (35)
  • Stock Matlab function (140)
  • Toolbox (10)
  • UI controls (52)
  • Uncategorized (13)
  • Undocumented feature (217)
  • Undocumented function (37)
Tags
AppDesigner (9) Callbacks (31) Compiler (10) Desktop (38) Donn Shull (10) Editor (8) Figure (19) FindJObj (27) GUI (141) GUIDE (8) Handle graphics (78) HG2 (34) Hidden property (51) HTML (26) Icons (9) Internal component (39) Java (178) JavaFrame (20) JIDE (19) JMI (8) Listener (17) Malcolm Lidierth (8) MCOS (11) Memory (13) Menubar (9) Mex (14) Optical illusion (11) Performance (78) Profiler (9) Pure Matlab (187) schema (7) schema.class (8) schema.prop (18) Semi-documented feature (6) Semi-documented function (33) Toolbar (14) Toolstrip (13) uicontrol (37) uifigure (8) UIInspect (12) uitable (6) uitools (20) Undocumented feature (187) Undocumented function (37) Undocumented property (20)
Recent Comments
Contact us
Captcha image for Custom Contact Forms plugin. You must type the numbers shown in the image
Undocumented Matlab © 2009 - Yair Altman
This website and Octahedron Ltd. are not affiliated with The MathWorks Inc.; MATLAB® is a registered trademark of The MathWorks Inc.
Scroll to top