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

Customizing Matlab's Workspace table

January 2, 2010 19 Comments

A few days ago, a CSSM user asked whether it is possible to modify the appearance of the Bytes column in the Workspace pane, so that it will present data in KBytes rather than in Bytes. Although I promised that my next post will explain FindJObj and its uses, I couldn’t resist the challenge. Here’s the solution to the request:
In this post I will assume Matlab release R2008a (7.6) – the adaptations for other releases should be minor at worst. First, we need to retrieve the Workspace table’s Java reference handle. In the past I’ve already shown several uses for the Matlab Desktop’s Java handle. Today we’ll use this handle to get the Workspace pane’s handle:

>> jDesktop = com.mathworks.mde.desk.MLDesktop.getInstance
jDesktop =
com.mathworks.mde.desk.MLDesktop@42d390
>> jWSBrowser = jDesktop.getClient('Workspace')
jWSBrowser =
com.mathworks.mde.workspace.WorkspaceBrowser[Workspace,0,24,355x707,...]
>> jWSTable = jWSBrowser.getComponent(0).getComponent(0).getComponent(0)
jWSTable =
com.mathworks.mlwidgets.workspace.WorkspaceTable[WorkspaceTable,0,0,352x102,...]

>> jDesktop = com.mathworks.mde.desk.MLDesktop.getInstance jDesktop = com.mathworks.mde.desk.MLDesktop@42d390 >> jWSBrowser = jDesktop.getClient('Workspace') jWSBrowser = com.mathworks.mde.workspace.WorkspaceBrowser[Workspace,0,24,355x707,...] >> jWSTable = jWSBrowser.getComponent(0).getComponent(0).getComponent(0) jWSTable = com.mathworks.mlwidgets.workspace.WorkspaceTable[WorkspaceTable,0,0,352x102,...]

Next, we note that jWSTable is a simple java Swing JTable, and as such we can easily modify its column header:

jWSTable.getColumn('Bytes').setHeaderValue('KBytes');
jWSBrowser.repaint;

jWSTable.getColumn('Bytes').setHeaderValue('KBytes'); jWSBrowser.repaint;

Modifying the column’s behavior to display 1/1024 of the initial values is more tricky. We can use a simple TableCellRenderer, replacing WorkspaceTable’s DefaultTableCellRenderer. We first create the following KBytesCellRenderer.java file:

import java.awt.*;
import javax.swing.*;
import javax.swing.SwingConstants.*;
import javax.swing.table.*;
public class KBytesCellRenderer extends DefaultTableCellRenderer
                                implements TableCellRenderer
{
  public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
  {
    Component cell = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
    //System.out.println(row + "," + column + " => " + value);
    ((KBytesCellRenderer)cell).setHorizontalAlignment(TRAILING);  // TRAILING = right
    int bytes = Integer.parseInt(value.toString());
    ((KBytesCellRenderer)cell).setText(bytes/1024 + "");          // Bytes => KBytes
    return cell;
  }
  public KBytesCellRenderer()
  {
    super();
  }
}

import java.awt.*; import javax.swing.*; import javax.swing.SwingConstants.*; import javax.swing.table.*; public class KBytesCellRenderer extends DefaultTableCellRenderer implements TableCellRenderer { public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Component cell = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); //System.out.println(row + "," + column + " => " + value); ((KBytesCellRenderer)cell).setHorizontalAlignment(TRAILING); // TRAILING = right int bytes = Integer.parseInt(value.toString()); ((KBytesCellRenderer)cell).setText(bytes/1024 + ""); // Bytes => KBytes return cell; } public KBytesCellRenderer() { super(); } }

Next, we find out our Matlab’s Java version:

>> version -java
ans =
Java 1.6.0 with Sun Microsystems Inc. Java HotSpot(TM) Client VM mixed mode

>> version -java ans = Java 1.6.0 with Sun Microsystems Inc. Java HotSpot(TM) Client VM mixed mode

Next, we download and install a JDK version compatible with our Matlab’s Java version. You can download the latest JDK from here or here. Previous JDK versions can be downloaded from here or here (which even lets you select your requested update number). Development versions, usually fixing reported bugs, are described and can be downloaded from here. Note that you need the full JDK, not just the JVM/JRE runtime versions.
Next, we compile this file using the JDK’s Java compiler (javac) utility: In your system’s command-line (outside Matlab), type the following in the folder containing your KBytesCellRenderer.java file:

javac KBytesCellRenderer.java

javac KBytesCellRenderer.java

If all goes well, javac will report no error and will create a KBytesCellRenderer.class file in the current folder (or you can download it directly from here).
Now copy this KBytesCellRenderer.class file to one of the folders in your Matlab’s javaclasspath (for example, C:\Program Files\Matlab\R2008a\java\patch\) and restart Matlab. Don’t worry – all this is only a one-time operation.
After restarting Matlab, we have all the chips in place, so we can place the following code in our startup.m script:

jDesktop = com.mathworks.mde.desk.MLDesktop.getInstance;
jWSBrowser = jDesktop.getClient('Workspace');
jWSTable = jWSBrowser.getComponent(0).getComponent(0).getComponent(0);
jWSTable.getColumn('Bytes').setHeaderValue('KBytes');
jWSTable.getColumn('Bytes').setCellRenderer(KBytesCellRenderer);
jWSBrowser.repaint;

jDesktop = com.mathworks.mde.desk.MLDesktop.getInstance; jWSBrowser = jDesktop.getClient('Workspace'); jWSTable = jWSBrowser.getComponent(0).getComponent(0).getComponent(0); jWSTable.getColumn('Bytes').setHeaderValue('KBytes'); jWSTable.getColumn('Bytes').setCellRenderer(KBytesCellRenderer); jWSBrowser.repaint;


Before - bytes
Before - bytes

After - KBytes
After - KBytes


We can use a slightly more complex CellRenderer to highlight cells with too high a value or to present thousands (comma) separator by simply modifying and recompiling KBytesCellRenderer.java, updating the class file in our javaclasspath folder and restarting Matlab. Here’s the version for the thousands separator (American locale):

import java.awt.*;
import javax.swing.*;
import javax.swing.SwingConstants.*;
import javax.swing.table.*;
import java.text.NumberFormat;
public class KBytesCellRenderer extends DefaultTableCellRenderer
                                implements TableCellRenderer
{
  public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
  {
    Component cell = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
    //System.out.println(row + "," + column + " => " + value);
    ((KBytesCellRenderer)cell).setHorizontalAlignment(TRAILING);  // TRAILING = Right
    int bytes = Integer.parseInt(value.toString());
    NumberFormat nf = NumberFormat.getInstance();
    ((KBytesCellRenderer)cell).setText(nf.format(bytes/1024));    // Bytes => KBytes
    return cell;
  }
  public KBytesCellRenderer()
  {
    super();
  }
}

import java.awt.*; import javax.swing.*; import javax.swing.SwingConstants.*; import javax.swing.table.*; import java.text.NumberFormat; public class KBytesCellRenderer extends DefaultTableCellRenderer implements TableCellRenderer { public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Component cell = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); //System.out.println(row + "," + column + " => " + value); ((KBytesCellRenderer)cell).setHorizontalAlignment(TRAILING); // TRAILING = Right int bytes = Integer.parseInt(value.toString()); NumberFormat nf = NumberFormat.getInstance(); ((KBytesCellRenderer)cell).setText(nf.format(bytes/1024)); // Bytes => KBytes return cell; } public KBytesCellRenderer() { super(); } }

After - formatted KBytes
After - formatted KBytes

If you have created an interesting CellRenderer, I will be happy to hear about it in the comments section below.

Related posts:

  1. Customizing Workspace context-menu – Matlab's Workspace table context-menu can be configured with user-defined actions - this article explains how....
  2. Customizing listbox/combobox items – Matlab listboxes can be customized using custom Java cell-renderers. ...
  3. Customizing help popup contents – The built-in HelpPopup, available since Matlab R2007b, has a back-door that enables displaying arbitrary text, HTML and URL web-pages....
  4. Customizing uitree nodes – part 2 – This article shows how Matlab GUI tree nodes can be customized with checkboxes and similar controls...
  5. Customizing Matlab uipanels – Matlab uipanel controls can be customized using Java in ways that are impossible with plain Matlab. ...
  6. Customizing uitree – This article describes how to customize Matlab GUI tree controls created using the undocumented uitree function...
Desktop Java
Print Print
« Previous
Next »
19 Responses
  1. Oleg Komarov January 5, 2010 at 12:21 Reply

    I surely noted your answer the day you posted it in my NG help request. I also implemented your solution but i wanted to display MB with two decimal places and comma separated values…
    Since i never programmed in JAVA i spent all day trying on the links that you added here…and this is as far as i went:

    import java.awt.*;
    import javax.swing.*;
    import javax.swing.SwingConstants.*;
    import javax.swing.table.*;
    import java.text.DecimalFormat;
     
    public class MBytesCellRenderer extends DefaultTableCellRenderer implements TableCellRenderer
    {
      public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
        Component cell = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
        //System.out.println(row + "," + column + " => " + value);
        int bytes = Integer.parseInt(value.toString());
        ((MBytesCellRenderer)cell).setHorizontalAlignment(TRAILING);  // TRAILING = Right
     
        DecimalFormat nf = DecimalFormat.applyPattern("#,##0.00");
        ((MBytesCellRenderer) cell).setText(nf.format(bytes/(1024*1024)));    // Bytes => MBytes
        return cell;
      }
     
      public MBytesCellRenderer()
      {
        super();
      }
    }

    import java.awt.*; import javax.swing.*; import javax.swing.SwingConstants.*; import javax.swing.table.*; import java.text.DecimalFormat; public class MBytesCellRenderer extends DefaultTableCellRenderer implements TableCellRenderer { public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Component cell = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); //System.out.println(row + "," + column + " => " + value); int bytes = Integer.parseInt(value.toString()); ((MBytesCellRenderer)cell).setHorizontalAlignment(TRAILING); // TRAILING = Right DecimalFormat nf = DecimalFormat.applyPattern("#,##0.00"); ((MBytesCellRenderer) cell).setText(nf.format(bytes/(1024*1024))); // Bytes => MBytes return cell; } public MBytesCellRenderer() { super(); } }

    first of all i don’t know how to elevate to a power :).
    second i can’t get it rigtht with the decimal format….

    I’ll keep trying…

    Oleg

    • Yair Altman January 5, 2010 at 14:08 Reply

      Oleg – There are two separate problems with your code:

      1) you need a DecimalFormat object (instance) before you can use applyPattern().
      2) bytes/(1024*1024) is an integer division so the result is rounded to an integer. Instead, use bytes/(1024.0*1024.0) to ensure a floating-point result.

      Here’s the resulting code fragment:

      DecimalFormat nf = DecimalFormat.getInstance();
      nf.applyPattern("#,##0.00");
      // Alternative: DecimalFormat nf = DecimalFormat("#,##0.00");
      ((MBytesCellRenderer) cell).setText(nf.format(bytes/(1024.0*1024.0)));

      DecimalFormat nf = DecimalFormat.getInstance(); nf.applyPattern("#,##0.00"); // Alternative: DecimalFormat nf = DecimalFormat("#,##0.00"); ((MBytesCellRenderer) cell).setText(nf.format(bytes/(1024.0*1024.0)));

      You might also be interested to add a “MB” suffix:

      nf.applyPattern("#,##0.00 MB");

      nf.applyPattern("#,##0.00 MB");

      I plan to submit a utility to the File Exchange within a few days, that will enable users to choose their display format. I will post a followup when it is available.

      By the way, DecimalFormat (and most Java) experiments can easily be done directly in the Matlab Command Window, without the annoying need to recompile Java and restart Matlab. For example:

      >> nf = java.text.DecimalFormat('#,##0.00 MB')
      nf =
      java.text.DecimalFormat@674dc
      >> nf.format(1234.567)
      ans =
      1,234.57 MB
      >> nf.applyPattern('#,##0.0');
      >> nf.format(1234.567)
      ans =
      1,234.6

      >> nf = java.text.DecimalFormat('#,##0.00 MB') nf = java.text.DecimalFormat@674dc >> nf.format(1234.567) ans = 1,234.57 MB >> nf.applyPattern('#,##0.0'); >> nf.format(1234.567) ans = 1,234.6

      • Oleg Komarov January 6, 2010 at 06:17

        Thank you very much for the tips. I’m eager to try your next submission!

        Oleg

  2. Ben January 20, 2010 at 09:12 Reply

    Nice trick!

    Is it possible to make the Size column display the size for 4-D matrices this way?

    Right now, the Size column just displays “4-D” in case of a 4-D matrix, instead of “m x n x p x q”, which is very annoying…

    Cheers,
    Ben

    • Yair Altman January 20, 2010 at 09:42 Reply

      @Ben – I think what you ask is possible if you add a JMI call in your CellRenderer. In a nutshell, the Renderer code will get the variable name from the table’s other column, and query its size by invoking JMI with the size function. However, this is non-trivial and also goes against known programming practices (that Renderers should be kept as light and fast as possible). On the other hand, I cannot think of any other way to do it.

  3. Mike L October 27, 2010 at 13:34 Reply

    Fantastic work! This is a really good idea. Not sure if anyone still reads this but:

    I was trying to see if I could take this one step further — I have a function that allows me to preview a certain type of structure I use a lot so I thought I would try to add a new contextual menu item for this.

    There appears to be two popup handlers for the Workspace browser/table, that I’m able to access using:

    jWSTable.getNoSelectionPopupMenu()
    jWSTable.getSelectionPopupMenu()

    jWSTable.getNoSelectionPopupMenu() jWSTable.getSelectionPopupMenu()

    I’m able to change the first one easily enough; if I just call:

    jWSTable.getNoSelectionPopupMenu().addSeparator()

    jWSTable.getNoSelectionPopupMenu().addSeparator()

    This adds a new separator just fine. However, I can’t do the same with the more useful popup used when I have a workspace variable selected. For some reason whenever I add a new menu item to it, it gets reset after I do anything.

    Would something like this require me to write a custom cell renderer as well? Or is there an easy way to do this?

    • Yair Altman October 27, 2010 at 13:54 Reply

      @Mike – there a simple way to do this that does not directly modify the Workspace table but rather the class-registry which drives the context menu. Here is an article about this.

      -Yair

  4. Customizing Workspace context-menu | Undocumented Matlab November 3, 2010 at 11:01 Reply

    […] Last week, a reader of that article posted a comment asking how to customize the context (right-click) menu with some user-defined actions […]

  5. Tarun Jhamnani September 26, 2011 at 22:55 Reply

    Hi,
    Works like a charm,really awesome.
    My question is slightly off the track to this article.Since,it is related to workspace i will go ahead and ask. I will be glad to have input on this.
    Can i monitor deletion of any variable from the workspace table(event which gets fired on deletion of variable).I tried listening events related to jworkspace table but it seems they are of no use to detect the deletion of variable.Mouseclicked,property change,key pressed etc all these event i was able to listen to.Am i missing something or i need to change my approach.
    Please Help.

    • Yair Altman September 27, 2011 at 00:58 Reply

      @Tarun – you can try listening to the TableChanged event on the workspace table’s model:

      hjModel = handle(jWSTable.getModel, 'CallbackProperties');
      set(hjModel, 'TableChangedCallback', @myCallbackFunction);

      hjModel = handle(jWSTable.getModel, 'CallbackProperties'); set(hjModel, 'TableChangedCallback', @myCallbackFunction);

      Just note that this keeps firing all the time so you need to check for actual changes in the rowcount at the very top of your callback and bail-out if no change. Otherwise your Matlab will get stuck in 100% CPU load just processing the callback events…

      • Tarun Jhamnani September 27, 2011 at 02:27

        Thanks Yair.
        I was able to detect the variable deletion. As you have mentioned about CPU load, is there a way where deletion of particular variable can be detected (depending on memory location or something). I want to know more about variable storage in matlab environment so that I can monitor clearing of particular memory location (if it is possible).
        The whole point being more efficient technique to monitor variable deletion.

      • Yair Altman September 27, 2011 at 03:28

        There’s probably a way somehow to monitor variable allocation and deallocation, but I do not know how to do it. If there is any person in the world (outside MathWorks) who knows, it would probably be Andrew Janke. I plan to post an article of his about Matlab’s memory usage sometime in the hazy future, and you could re-post your question there.

      • Tarun Jhamnani September 27, 2011 at 03:56

        Looking forward for the article. Will definitely re-post.
        Thanks Yair.

  6. Lukasz Wiklendt March 20, 2012 at 16:22 Reply

    In Matlab 2011b the startup script fails since

    jWSBrowser = jDesktop.getClient('Workspace');

    jWSBrowser = jDesktop.getClient('Workspace');

    returns an empty array. The crude workaround that I use to get it to work again is

    jWSBrowser = [];
    while isempty(jWSBrowser)
        jWSBrowser = jDesktop.getClient('Workspace');
    end

    jWSBrowser = []; while isempty(jWSBrowser) jWSBrowser = jDesktop.getClient('Workspace'); end

    • Yair Altman March 21, 2012 at 00:28 Reply

      @Lukasz – maybe your Workspace client is called something else. run jDesktop.getClientTitles to get the full list of clients in your Matlab – the Workspace should be near the top of this list:

      >> jDesktop.getClientTitles
      ans =
       
      java.lang.String[]:
          'Command Window'
          'Command History'
          'Current Folder'
          'Workspace'
          'Help'
          'Profiler'
          'File Exchange'
          ...

      >> jDesktop.getClientTitles ans = java.lang.String[]: 'Command Window' 'Command History' 'Current Folder' 'Workspace' 'Help' 'Profiler' 'File Exchange' ...

    • Lukasz Wiklendt March 22, 2012 at 14:16 Reply

      Putting this in startup.m:

      jDesktop = com.mathworks.mde.desk.MLDesktop.getInstance;
      jDesktop.getClientTitles
      jDesktop.getClient('Workspace')

      jDesktop = com.mathworks.mde.desk.MLDesktop.getInstance; jDesktop.getClientTitles jDesktop.getClient('Workspace')

      results in this showing up in the command window:

      ans =
      java.lang.String[]:
          'Command Window'
          'Command History'
          'Current Folder'
          'Workspace'
          'Help'
          'Profiler'
          'File Exchange'
          'Figure Palette'
          'Plot Browser'
          'Property Editor'
       
      ans =
           []

      ans = java.lang.String[]: 'Command Window' 'Command History' 'Current Folder' 'Workspace' 'Help' 'Profiler' 'File Exchange' 'Figure Palette' 'Plot Browser' 'Property Editor' ans = []

    • Yair Altman March 22, 2012 at 14:38 Reply

      @Lukasz – try

      jWSBrowser = jDesktop.getClientByName('Workspace')

      jWSBrowser = jDesktop.getClientByName('Workspace')

  7. Fred November 29, 2012 at 14:22 Reply

    Hi,
    I’m having a weird bug in R2010b running on linux. The right-click is disabled in the table column header of the workspace, so I can only see the columns enabled by default (Name, Value, Min, Max). I really need to see “Bytes” at least! Would you have any idea how to sort this out with your Java magic?
    I was thinking: either we need to enable the mouse listener if it’s been disabled for some reason, or access the table model somehow and manually add the column?
    It’d be really great if you can help!

    • Yair Altman November 29, 2012 at 15:07 Reply

      @Fred – no need for any Java magic. Simply go to the main Desktop menu-bar, then choose View => Choose Columns

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