Skip to main content

JXTreeTable usage

8 replies [Last post]
cdrichmond
Offline
Joined: 2008-08-14
Points: 0

I've been trying to use the JXTreeTable for a while now with no luck. I haven't found a simple sample of adding some rows/columns to the table and adding a few children one level down to it. I tried the following, but it does not work...:

DefaultMutableTreeTableNode node = new DefaultMutableTreeTableNode();
DefaultMutableTreeTableNode node1 = new DefaultMutableTreeTableNode();
DefaultMutableTreeTableNode node2 = new DefaultMutableTreeTableNode();
DefaultMutableTreeTableNode node3 = new DefaultMutableTreeTableNode();

node.setValueAt("", 0);
node.setValueAt("value", 1);
node.setValueAt("value", 2);

node1.setValueAt("value1", 0);
node1.setValueAt("value2", 1);
node1.setValueAt("value3", 2);

node2.setValueAt("value1", 0);
node2.setValueAt("value2", 1);
node2.setValueAt("value3", 2);

node3.setValueAt("value1", 0);
node3.setValueAt("value2", 1);
node3.setValueAt("value3", 2);

node.add(node1);
node.add(node2);
node.add(node3);

DefaultTreeTableModel mdl = new DefaultTreeTableModel(node);

JXTreeTable treeTable = new JXTreeTable(mdl);

ArrayList columns = new ArrayList();
columns.add("Col1");
columns.add("Col2");
columns.add("Col3");
mdl.setColumnIdentifiers(columns);

treeTable.updateUI();

This runs fine and the columns are visible but the display looks like this of all things:

Col1 | Col2 | Col3
value3
value3
value3

So, I can't figure out how to use it at all, and if it doesn't work on the above code, I certainly cant figure how it would lead to what I'm seeing when I run it? Anyone have a simple sample they could share or know what's going on with my code?

Thanks,
chris

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
kschaefe
Offline
Joined: 2006-06-08
Points: 0

> I've been trying to use the JXTreeTable for a while
> now with no luck. I haven't found a simple sample of
> adding some rows/columns to the table and adding a
> few children one level down to it. I tried the
> following, but it does not work...:
>
> DefaultMutableTreeTableNode node = new
> DefaultMutableTreeTableNode();
> DefaultMutableTreeTableNode node1 = new
> DefaultMutableTreeTableNode();
> DefaultMutableTreeTableNode node2 = new
> DefaultMutableTreeTableNode();
> DefaultMutableTreeTableNode node3 = new
> DefaultMutableTreeTableNode();

You should not use DefaultMutableTreeTableNode. It exists for rapid prototyping and testing only.

> node.setValueAt("", 0);
> node.setValueAt("value", 1);
> node.setValueAt("value", 2);
>
>
>
> node1.setValueAt("value1", 0);
> node1.setValueAt("value2", 1);
> node1.setValueAt("value3", 2);
>
>
> node2.setValueAt("value1", 0);
> node2.setValueAt("value2", 1);
> node2.setValueAt("value3", 2);
>
> node3.setValueAt("value1", 0);
> node3.setValueAt("value2", 1);
> node3.setValueAt("value3", 2);

Setting values on DefaultTreeTableNode is about useless. It always returns only one value and that is the last value set. It is NOT to be used for application code.

> node.add(node1);
> node.add(node2);
> node.add(node3);
>
>
> DefaultTreeTableModel mdl = new
> DefaultTreeTableModel(node);
>
>
>
> JXTreeTable treeTable = new JXTreeTable(mdl);
>
>
> ArrayList columns = new
> ArrayList();
> columns.add("Col1");
> columns.add("Col2");
> columns.add("Col3");
> mdl.setColumnIdentifiers(columns);
>
>
> treeTable.updateUI();

Application code should NEVER call updateUI; it exists for Look and Feel purposes only.

> This runs fine and the columns are visible but the
> display looks like this of all things:
>
> Col1 | Col2 | Col3
> value3
> value3
> value3

That is the expected view when using DefaultMutableTreeTableNode. Don't use it; extend AbstractMutableTreeTableNode instead.

Karl

sonnykwe
Offline
Joined: 2009-12-02
Points: 0

Look at DefaultMutableTreeTableNode class code, setValueAt method doesnt use the column param!

kschaefe
Offline
Joined: 2006-06-08
Points: 0

Why would it? It's not actually designed for use; it's designed as an example.

Karl

cdrichmond
Offline
Joined: 2008-08-14
Points: 0

ok..I was thinking that might be the problem..but I guess my experience with the built in swing components where you can use the default mutable nodes lead me down that path.

I notice some sample code where people are using a class which extends DefaultMutableTreeTableNode rather than extending AbstractMutableTreeTableNode as you suggest.

What is the proper way to do this and why?

Thanks,

Chris

kschaefe
Offline
Joined: 2006-06-08
Points: 0

Chris,

> ok..I was thinking that might be the problem..but I
> guess my experience with the built in swing
> components where you can use the default mutable
> nodes lead me down that path.

The documentation makes the purpose of the DefaultMutableTreeTableNode clear. It exists for completeness and rapid prototyping. I personally would like to see it go away as it often leads developers astray, but there was a requirement to have a concrete implementation and so it is.

> I notice some sample code where people are using a
> class which extends DefaultMutableTreeTableNode
> rather than extending AbstractMutableTreeTableNode as
> you suggest.

Always create your own custom nodes. Unlike J(X)Tree which only has "one column" to display, JXTreeTable must support any number of columns. There is no practical way to do that intelligently without creating a custom node. I recommend building custom nodes from AbstractTreeTableNode because the methods that it forces you to override will give you a display that you're looking for.

Karl

cdrichmond
Offline
Joined: 2008-08-14
Points: 0

Karl,

Thanks very much...I've got it working now based on what you told me and the sample someone posted above.

Thanks,

Chris

cdrichmond
Offline
Joined: 2008-08-14
Points: 0

I guess I should also ask, is it possible to get a simple sample running using DefaultTreeTableModel and DefaultMutableTreeTableNodes only? I realize i should realistically create my own concrete implementations of the iterfaces/abstract classes later, but it should be possible to simply create a demo using the default concrete classes should it not?

Thanks,

Chris

rturnbull
Offline
Joined: 2005-08-27
Points: 0

> I guess I should also ask, is it possible to get a
> simple sample running using DefaultTreeTableModel and
> DefaultMutableTreeTableNodes only? I realize i should
> realistically create my own concrete implementations
> of the iterfaces/abstract classes later, but it
> should be possible to simply create a demo using the
> default concrete classes should it not?
>
> Thanks,
>
> Chris

Here is an example of a simple JXTreeTable. It has been extracted from a larger
example I wroter for myself with add/modify/delete options, so the Nodes have
a little extra code.
It uses standard DefaultTreeTableModel, with an override of getColumnClass(), and
AbstractMutableTreeTableNode
[code]
/** TreeTableDemo.java
*
* Created 10/01/2008
*
* @author Ray Turnbull
*/
package tests;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListSelectionModel;
import javax.swing.UIManager;

import org.jdesktop.swingx.JXTreeTable;
import org.jdesktop.swingx.treetable.AbstractMutableTreeTableNode;
import org.jdesktop.swingx.treetable.DefaultTreeTableModel;

/**
*
*/
public class TreeTableDemo {

private final String[] headings = { "Family Name", "Given Name", "Post Code",
"Relationship", "DOB", "Sex" };
private Node root;
private DefaultTreeTableModel model;
private JXTreeTable table;
private Object[][] initialData;
private Object[][] childData;
private Object[][] nameData;
private DateFormat format;

public void show() {
JFrame f = new JFrame("TreeTable Demo");
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setMinimumSize(new Dimension(700, 400));
f.setLocationRelativeTo(null);
f.setContentPane(getPanel());
f.pack();
f.setVisible(true);
}

private JPanel getPanel() {
JPanel p = new JPanel(new BorderLayout());
JScrollPane sp = new JScrollPane(getTreeTable());
p.add(sp);
return p;
}

// ============================================================= treetable

private JXTreeTable getTreeTable() {
loadData();
root = new RootNode("People");
addFamilies();
model = new DefaultTreeTableModel(root, Arrays.asList(headings)) {
@Override
public Class getColumnClass(int column) {
if (column == 2) {
return Integer.class;
} else if (column == 4) {
return Date.class;
} else {
return String.class;
}
}
};
table = new JXTreeTable(model);
table.setShowGrid(true, true);
table.setRootVisible(true);
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.setColumnControlVisible(true);
table.setHorizontalScrollEnabled(true);
table.setFillsViewportHeight(false);

table.expandRow(0);
table.packAll();
return table;
}

private void addFamilies() {
for (Object[] element : initialData) {
Object[] data = new Object[] {element[0], null, element[2] };
FamilyNode child = new FamilyNode(data);
root.add(child);
PersonNode person = new PersonNode(element);
child.add(person);
addNames(person);
addPeople(child);
}
}

private void addPeople(FamilyNode parent) {
for (Object[] element : childData) {
String family = element[0].toString();
if (parent.isFamily(family)) {
PersonNode child = new PersonNode(element);
parent.add(child);
addNames(child);
}
}
}

private void addNames(PersonNode parent) {
for (Object[] element : nameData) {
String person = element[0].toString() + element[1].toString();
if (parent.isPerson(person)) {
Node child = new NameNode(element);
parent.add(child);
}
}
}

private void loadData() {
format = new SimpleDateFormat("dd/MM/yyyy");
try {
initialData = new Object[][] {
{"Turnbull", "Ray", 2196, "", format.parse("01/01/1900"), "M"},
{"Hadden", "Brian", 2030, "Husband", format.parse("15/07/1950"), "M"},
{"Lesnie", "James", 2587, "Husband", format.parse("27/03/1976"), "M"},
{"Neyle", "Barbara", 2258, "Wife", format.parse("26/08/1975"), "F"},
{"Dowd", "Charles", 2658, "", format.parse("12/07/1982"), "M"},
{"Powers", "Raymond", 2272, "", format.parse("26/02/1940"), "M"},
{"Maguire", "Jerry", 2050, "", format.parse("30/11/1953"), "M"},
{"Lee", "Stan", 2666, null, format.parse("01/10/1981"), "M"}
};
} catch (ParseException e) {
e.printStackTrace();
}
try {
childData = new Object[][] {
{"Hadden", "James", null, "Son", format.parse("15/07/1975"), "M"},
{"Hadden", "Lois", null, "Daughter", format.parse("15/07/1977"), "F"},
{"Hadden", "Lucy", null, "Daughter", format.parse("15/07/1978"), "F"},
{"Hadden", "Margaret", null, "Wife", format.parse("15/07/1953"), "F"},
{"Lesnie", "Clare", null, "Wife", format.parse("27/03/1976"), "F"},
{"Lesnie", "Maya", null, "Daughter", format.parse("27/03/1987"), "F"},
{"Neyle", "Robert", null, "Husband", format.parse("26/08/1973"), "M"},
{"Neyle", "Clarise", null, "Daughter", format.parse("26/08/2000"), "F"},
{"Neyle", "Robert Jnr", null, "Son", format.parse("26/08/1998"), "M"},
};
} catch (ParseException e) {
e.printStackTrace();
}
nameData = new Object[][] {
{"Turnbull", "Ray", "Raymond"},
{"Turnbull", "Ray", "Barry"},
{"Hadden", "James", "Jimmy"},
{"Hadden", "James", "William"},
{"Powers", "Raymond", "Robert"},
{"Neyle", "Clarise", "Barbara"}
};
}

// ========================================================== node classes

private abstract class Node extends AbstractMutableTreeTableNode {

public Node(Object[] data) {
super(data);
if (data == null) {
throw new IllegalArgumentException("Node data cannot be null");
}
}

public abstract String getFamily();

/*
* Inherited
*/
@Override
public int getColumnCount() {
return getData().length;
}

/*
* Inherited
*/
@Override
public Object getValueAt(int column) {
return getData()[column];
}

public Object[] getData() {
return (Object[]) getUserObject();
}

}

private class RootNode extends Node {

public RootNode(String key) {
super(new Object[] {key});
}

public String getFamily() {
return "";
}
}

private class FamilyNode extends Node {

public FamilyNode(Object[] data) {
super(data);
}

public String getFamily() {
return (String) getData()[0];
}

public boolean isFamily(String family) {
return getFamily().equals(family);
}

}

private class PersonNode extends Node {

public PersonNode(Object[] data) {
super(data);
}

public String getFamily() {
return ((Node) getParent()).getFamily();
}

public String getPerson() {
return getFamily() + (String) getData()[1];
}

public boolean isPerson(String person) {
return getPerson().equals(person);
}

@Override
public Object getValueAt(int column) {
// null for family name and post code
if (column == 0 || column == 2) {
return null;
}
return getData()[column];
}

}

private class NameNode extends Node {

public NameNode(Object[] data) {
super(data);
}

public String getFamily() {
return ((Node) getParent()).getFamily();
}

@Override
public int getColumnCount() {
return 2;
}

@Override
public Object getValueAt(int column) {
if (column == 0) {
return null;
}
return getData()[2];
}

}

// =================================================================== main

/**
* @param args
*/
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
new TreeTableDemo().show();
}

}
[/code]

Edit: Note this is not a 'good' example, in that the
column names passed to the model are only used to give
column headings.
In my 'real' application, these are true data column names and are used to return column class, name and value

Message was edited by: rturnbull