- Undocumented Matlab - https://undocumentedmatlab.com -

Customizing Matlab's Workspace table

Posted By Yair Altman On January 2, 2010 | 19 Comments

A few days ago, a CSSM user asked [1] 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 [2]. 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,...]

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

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 [4], replacing WorkspaceTable’s DefaultTableCellRenderer. We first create the following KBytesCellRenderer.java [5] 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();
  }
}

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

Next, we download and install a JDK version compatible with our Matlab’s Java version. You can download the latest JDK from here [6] or here [7]. Previous JDK versions can be downloaded from here [8] or here [9] (which even lets you select your requested update number). Development versions, usually fixing reported bugs, are described and can be downloaded from here [10]. 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

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 [11]).
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;


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 [12] 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();
  }
}

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.

Categories: Desktop, High risk of breaking in future versions, Java


19 Comments (Open | Close)

19 Comments To "Customizing Matlab's Workspace table"

#1 Comment By Oleg Komarov On January 5, 2010 @ 12:21

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();
  }
}

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

#2 Comment By Yair Altman On January 5, 2010 @ 14:08

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)));

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

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

#3 Comment By Oleg Komarov On January 6, 2010 @ 06:17

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

Oleg

#4 Comment By Ben On January 20, 2010 @ 09:12

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

#5 Comment By Yair Altman On January 20, 2010 @ 09:42

@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.

#6 Comment By Mike L On October 27, 2010 @ 13:34

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()

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

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?

#7 Comment By Yair Altman On October 27, 2010 @ 13:54

@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 [19] about this.

-Yair

#8 Pingback By Customizing Workspace context-menu | Undocumented Matlab On November 3, 2010 @ 11:01

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

#9 Comment By Tarun Jhamnani On September 26, 2011 @ 22:55

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.

#10 Comment By Yair Altman On September 27, 2011 @ 00:58

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

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…

#11 Comment By Tarun Jhamnani On September 27, 2011 @ 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.

#12 Comment By Yair Altman On September 27, 2011 @ 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.

#13 Comment By Tarun Jhamnani On September 27, 2011 @ 03:56

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

#14 Comment By Lukasz Wiklendt On March 20, 2012 @ 16:22

In Matlab 2011b the startup script fails since

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

#15 Comment By Yair Altman On March 21, 2012 @ 00:28

@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'
    ...

#16 Comment By Lukasz Wiklendt On March 22, 2012 @ 14:16

Putting this in startup.m:

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 =
     []

#17 Comment By Yair Altman On March 22, 2012 @ 14:38

@Lukasz – try

jWSBrowser = jDesktop.getClientByName('Workspace')

#18 Comment By Fred On November 29, 2012 @ 14:22

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!

#19 Comment By Yair Altman On November 29, 2012 @ 15:07

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


Article printed from Undocumented Matlab: https://undocumentedmatlab.com

URL to article: https://undocumentedmatlab.com/articles/customizing-matlabs-workspace-table

URLs in this post:

[1] asked: https://www.mathworks.com/matlabcentral/newsreader/view_thread/269361

[2] several uses for the Matlab Desktop’s Java handle: http://undocumentedmatlab.com/?s=MLDesktop

[3] JTable: http://java.sun.com/docs/books/tutorial/uiswing/components/table.html

[4] TableCellRenderer: http://java.sun.com/docs/books/tutorial/uiswing/components/table.html#renderer

[5] KBytesCellRenderer.java: http://undocumentedmatlab.com/files/KBytesCellRenderer.java

[6] here: http://www.java.com/en/download/manual.jsp

[7] here: http://java.sun.com/javase/downloads/index.jsp

[8] here: http://java.sun.com/javase/downloads/previous.jsp

[9] here: http://java.sun.com/products/archive/

[10] here: https://jdk6.dev.java.net/

[11] here: http://undocumentedmatlab.com/files/KBytesCellRenderer.class

[12] thousands (comma) separator: http://java.sun.com/j2se/1.4.2/docs/api/java/text/DecimalFormat.html

[13] Customizing Workspace context-menu : https://undocumentedmatlab.com/articles/customizing-workspace-context-menu

[14] Customizing listbox/combobox items : https://undocumentedmatlab.com/articles/customizing-listbox-combobox-items

[15] Customizing help popup contents : https://undocumentedmatlab.com/articles/customizing-help-popup-contents

[16] Customizing uitree nodes – part 2 : https://undocumentedmatlab.com/articles/customizing-uitree-nodes-2

[17] Customizing Matlab uipanels : https://undocumentedmatlab.com/articles/customizing-matlab-uipanels

[18] Customizing uitree : https://undocumentedmatlab.com/articles/customizing-uitree

[19] : https://undocumentedmatlab.com/blog/customizing-workspace-context-menu/

Copyright © Yair Altman - Undocumented Matlab. All rights reserved.