Rich-contents log panel

I often include a log panel in Matlab applications that process data. By sending messages to the log panel, I can avoid msgbox popups and Command Window messages that interfere with the regular workflow. In Matlab, such log panels typically use a simple listbox or editbox control. The problem with this is that all text messages in the log panel look the same. Matlab does not have a documented way to highlight specific messages or words. Well, this has never stopped us before, has it? :-)

The listbox solution

I have often noted in past articles that most Matlab uicontrols support HTML formatting. We can use a listbox control for our log panel, and use HTML formatting for the various log messages, based on their severity or content. For example:

Listbox with HTML'ed items

Listbox with HTML colored items

uicontrol('Style','list', 'Position',[10,10,70,70], 'String', ...
   {'<html><font color="red">Hello</font></html>', 'world', ...
    '<html><font style="font-family:impact;color:green"><i>What a', ...   % note: </i></font></html> are not needed
    '<html><font color="blue" face="Comic Sans MS">nice day!</font>'});   % note: </html> is not needed

Rich listboxes such as this are very easy to set up and use. As I just showed, all it takes is sending an HTML string to a regular listbox. The down-side is that listboxes only display single-line messages, so if our message is too long we need to manually split it into separate listbox lines, which is rather inconvenient. Using HTML <br/> does not work since the allocated line height remains unchanged. We can fix this by playing around with the underlying Java object‘s RowHeight property, but that again is rather inconvenient.

The editbox solution

Instead of a listbox, I often use a multi-line editbox. Unfortunately, HTML is not as-easy to use in multi-line editbox contents, but as I have shown before, it is indeed possible and actually quite powerful. In fact, I am using such an editbox-based log panel’s in my online presentation at the MATLAB Computational Finance Virtual Conference tomorrow (Thursday Sep 19, 2013 at 2pm EST / 8pm CEST, see details below):

Real-time trading system demo in Matlab (click to zoom)

Real-time trading system demo in Matlab (click to zoom)

We can see in the screenshot that some log messages (the red warning message) can span multiple lines. Moreover, the panels can be interactively dragged/resized, making the multi-line log messages “flow” based on available space. So using an editbox is preferable to a listbox. The solution implemented in the demo is quite simple:

First we define the logging utility function (the icon filenames may need to be changed based on your Matlab release):

function logMessage(jEditbox,text,severity)
   % Ensure we have an HTML-ready editbox
   HTMLclassname = 'javax.swing.text.html.HTMLEditorKit';
   if ~isa(jEditbox.getEditorKit,HTMLclassname)
      jEditbox.setContentType('text/html');
   end
 
   % Parse the severity and prepare the HTML message segment
   if nargin < 3,  severity='info';  end
   switch lower(severity(1))
      case 'i',  icon = 'greenarrowicon.gif'; color='gray';
      case 'w',  icon = 'demoicon.gif';       color='black';
      otherwise, icon = 'warning.gif';        color='red';
   end
   icon = fullfile(matlabroot,'toolbox/matlab/icons',icon);
   iconTxt =['<img src="file:///',icon,'" height=16 width=16/>'];
   msgTxt = ['&nbsp;<font color=',color,'>',text,'</font>'];
   newText = [iconTxt,msgTxt];
   endPosition = jEditbox.getDocument.getLength;
   if endPosition>0, newText=['<br />' newText];  end
 
   % Place the HTML message segment at the bottom of the editbox
   currentHTML = char(jEditbox.getText);
   jEditbox.setText(strrep(currentHTML,[60 '/body>'],newText));
   endPosition = jEditbox.getDocument.getLength;
   jEditbox.setCaretPosition(endPosition); % end of content
end

Next, we initialize the log panel editbox and store the editbox’s underlying Java component (jEditbox) using the findjobj utility:

% Prepare the log editbox
hLogPanel = uicontrol('style','edit', 'max',5, 'Parent',hLeftBottomPanel, 'Units','norm', 'Position',[0,0.2,1,0.8], 'Background','w');
 
% Get the underlying Java editbox, which is contained within a scroll-panel
jScrollPanel = findjobj(hLogPanel);
try
    jScrollPanel.setVerticalScrollBarPolicy(jScrollPanel.java.VERTICAL_SCROLLBAR_AS_NEEDED);
    jScrollPanel = jScrollPanel.getViewport;
catch
    % may possibly already be the viewport, depending on release/platform etc.
end
jEditbox = handle(jScrollPanel.getView,'CallbackProperties');

Now, we can use this logging utility function to log messages in our application. For example:

logMessage(jEditbox, 'a regular info message...');
logMessage(jEditbox, 'a warning message...', 'warn');
logMessage(jEditbox, 'an error message!!!', 'error');
logMessage(jEditbox, 'a regular message again...', 'info');

Rich editbox contents (a log file)

Rich editbox contents (a log file)

HTML editboxes are normally editable, images included. In actual applications, we usually wish to prevent editing the displayed log. To do this, we simply call jEditbox.setEditable(false). Similarly, it is easy to set-up a Matlab callback-function to handle hyperlink clicks in the log panel (unlike what we might think, this is not handled automatically by the HTML processing engine):

% Prevent user editing in the log-panel
jEditbox.setEditable(false);
 
% Set-up a Matlab callback function to handle hyperlink clicks
set(jEditbox,'HyperlinkUpdateCallback',@linkCallbackFcn);

About the MATLAB Computational Finance Virtual Conference and my presentation

The MATLAB Computational Finance Virtual Conference is a one-day (Thursday Sep 19, 2013) event of hour-long presentations by industry professionals that showcase real-world examples demonstrating how financial-industry researchers and developers can excel at their jobs, improve their research and business processes, reduce costs, and mitigate risks by using Matlab. Registration to the conference is free and it’s a virtual conference, so there’s no need for a tie and jacket and you’re welcome to join…

MATLAB Computational Finance Conference 2013

My presentation on “A Real-Time Trading System in MATLAB“, is scheduled for 2pm EST / 8pm CEST. Following a half-hour presentation, I will answer audience questions online. The presentation slides can be downloaded here. Here is the recorded presentation video:

The demo system’s user interface showcases the hidden visualization and interactivity potential of Matlab for tracking order executions and charting financial time-series in real time. The undocumented features used in the demo include:

So, even if you’re not interested in real-time financial trading with Matlab, you might find it interesting to see the neat things that Matlab can do using a Java API interface and a few undocumented GUI tricks (such as the rich-contents log that I explained above).

The demo source code is provided here (tradingDemo.m and supporting files). Note that this is provided as-is, free of charge but without any warranty or support. You would naturally need IB-Matlab and an Interactive Brokers account to run it. But you can reuse parts of the source code in your Matlab programs even without an IB account or IB-Matlab.

Categories: GUI, Icons, Low risk of breaking in future versions, UI controls, Undocumented feature

Tags: , , , ,

Bookmark and SharePrint Print

15 Responses to Rich-contents log panel

  1. Sven says:

    Wonderful timing, Yair. I’m building a complex gui with a lot of my debugging just printing to the screen. My next task/idea was to implement such a log panel. I want to make debugging easier, so I was thinking of making each of my logged messages also have a “debugLevel”. While testing, my application can have a low “debugThreshold”, so that all messages get shown. For production, I can set a higher debugThreshold so that only user-relevant message get shown.

    In your experience, have you preferred something like this (ALL messages are generated by the application and sent to the logger, but they’re only printed if they have a high enough debugLevel), or something where you wrap each of your debug strings with:

    if debugThreshold>3
        logMessage('hey ho')
    end

    The first method has the highest cost (all messages are created), while the second method is just a little more cumbersome (needing to wrap messages in if-statements inline with your application code).

    By the way, there’s a little hiccup in the html parsing of “nargin<3" printing the full code of "nargin<3"

    • @Sven – thanks, the HTML hickup is fixed.

      I normally prefer setting some global config indicating the minimal display level. Within the logging function, I then compare the severity of the reported message to the config value and bail out of the function if the message should not be logged. I normally use either 3 severity levels (INFO, WARN, ERROR) or 5 (DEBUG, INFO, WARN, ERROR, FATAL), with the default config level being either INFO or WARN. Naturally this can and should be wrapped in some nice logging class, but I never took the time to do this until now…

    • Sven says:

      Thanks Yair, makes sense to me.

      I’ve also mused about how, in the production version of any code, it would be great to just snip out all debug-level commands. I’ve considered putting in my own comment-based markup such as:

      a = magic(3);
      % $DEBUG-BEGIN$
      if DEBUG_LEVEL > 0
        aStr = sprintf('%g,',a);
        fprintf('Contents of ''a'' are: %s\n',aStr)
      end
      % $DEBUG-END$
      ... % rest of code

      With syntax like that, you could have the best both worlds. During testing you can run the original code and set the DEBUG_LEVEL to whatever prints your chosen output. Then for production you can have a script that automatically comments out code between $DEBUG-BEGIN$ and $DEBUG-END$ tags, which means that in the production version, you don’t even get the (minor) overhead of checking debug levels throughout the code.

      But yes… sometimes it’s easier to think of a good idea than find the time to implement it :)

  2. Chris says:

    Hi Yair,

    Another great post. I was just wondering if you could explain the following lines, which seem to do nothing.

    currentHTML = char(jEditbox.getText);
    jEditbox.setText(strrep(currentHTML,'',newText));

    Is this a typo/bug, or am I missing something?

    • Yair Altman says:

      @Chris – it’s good that you noticed. This is in fact due to problematic rendering of the code by my blog server engine. The original code in the second line was jEditbox.setText(strrep(currentHTML,'</body>',newText)); and the purpose is to replace the HTML body-end tag with the new text.

      Since I couldn’t get around my blog engine’s limitation, I have now replaced that line with the equivalent (although a little more obscure):

      jEditbox.setText(strrep(currentHTML,[60 '/body>'],newText));
  3. Chris says:

    I’ve implemented this log, but I had a few questions about it. I’ve posted a question on stackexchange, and I was hoping you would weigh-in.

    • Chris says:

      After getting some feedback on stackexchange, I have a way to dramatically improve performance. Instead of performing getText and setText, you can insert the new HTML at the end of the document in the following way

      function logMessage(jEditbox, text)
      text = [text ''];
       
      % Place the HTML message segment at the bottom of the editbox
      Doc = jEditbox.getDocument();
      jEditbox.getEditorKit().read(java.io.StringReader(text), Doc, Doc.getLength());
      jEditbox.setCaretPosition(Doc.getLength());
      end
  4. Pingback: CopyQuery | Question & Answer Tool for your Technical Queries

  5. Holger says:

    Hi Yair,

    I was using this for some time (from older posts).
    But in R2013b (all prior versions work), the hyperlink feature broke. Not completely, but the mouse cursor doesn’t change to a hand when hovering over a hyperlink.

    In addition the following code also does nothing:

    jHelpLinkButton = java(findjobj(hButton));
    jHelpLinkButton.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.HAND_CURSOR));

    When using a “com.mathworks.widgets.HyperlinkTextLabel” (from your book), also the hand cursor doesn’t appear, but at least, the label changes color when hovering.

    Any idea, how I can get this working again?
    An acceptable workaround would be to change the hyperlink style, so it has an alternate color when hovering over it. But I think I would have to change the default style for hyperlinks in the HTMLEditorKit

    • @Holger – this is probably related to the fact thar R2013b started using Java 7, which is a major overhaul over the previous Java 6. I assume that this broke some functionality of the editbox. Since the underlying Java control is a standard JTextPane, you can easily search the net for relevant reports of (and possible workarounds for) this issue. Let us know if you find something relevant.

  6. Carsten says:

    Hello Yair,

    is there a way to get rid of the vertical scrollbar? I tried to do so using

    jScrollPane.setVerticalScrollBarPolicy(javax.swing.JScrollPane.VERTICAL_SCROLLBAR_NEVER)

    but this has no effect.

    Thanks,
    Carsten

    • @Carsten – it should work, you’re probably doing something wrong. Perhaps you are updating the wrong handle, or updating the editbox’s contents via Matlab not Java (this resets the Java customizations).

  7. Pingback: CheckboxList | Undocumented Matlab

  8. Daisuke says:

    This is a cool stuff! I need to use rather text than html and am having a problem with getting the code to work. Since I need to put so many lines in the log, I thought something to append a new line would be computationally less demanding than setText (is that true?). I mean something like,

    jEditbox.append(new_text);

    Would this be possible?
    Another question: Since I don’t know Java, I have no clue about what kind of member functions are available. How can one know that?

    • Yair Altman says:

      @Daisuke – there is no append method (by this or any other name) that I know of.

      To list a Java object’s methods, you can use the builtin methods or methodsview functions, or one of my utilities: uiinspect or checkClass.

Leave a Reply

Your email address will not be published. Required fields are marked *