Since chemical structures are frequently stored in databases, the most common usage of molecule visualization is perhaps in a tabular format that allows browsing the database, showing search results, selections of structures against particular criteria.
In typical Java applications, the javax.swing.JTable
is used to display tables of data. This form of visualization form will be referred to as tables. This section demonstrates how can molecules be rendered with Marvin inside existing tables.
The following picture shows a simple table with a molecule column.
The characteristic features of JTable
to bear in mind are as follows:
It is important to understand the concepts of JTable about Editors and Renderers. Let us quote the relevant part of the Java Tutorials:
You might expect each cell in a table to be a component. However, for performance reasons, Swing tables are implemented differently.
Instead, a single cell renderer is generally used to draw all of the cells that contain the same type of data. You can think of the renderer as a configurable ink stamp that the table uses to stamp appropriately formatted data onto each cell. When the user starts to edit a cell's data, a cell editor takes over the cell, controlling the cell's editing behavior.
To choose the renderer that displays the cells in a column, a table first determines whether you specified a renderer for that particular column. If you did not, then the table invokes the table model's getColumnClass
method, which gets the data type of the column's cells. Next, the table compares the column's data type with a list of data types for which cell renderers are registered. This list is initialized by the table, but you can add to it or change it.
Chemaxon recommends the following points to consider:
To use the MViewPane component as the cell renderer, it should implement the getTableCellRendererComponent
method of the TableCellRenderer
interface. The implementation of this method sets up the rendering component to display the passed-in molecule, and then returns the component.
public class MViewRenderer extends MViewPane
implements TableCellRenderer {
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
... // setting backround color and border
setM( 0, (Molecule)value );
return this;
}
}
This renderer class is accessible from the public API.
In many cases, it is sufficient to have only the static image of the molecule inside cells, that is, no need to rotate, change rendering mode, or modify the structure itself. The renderer can be more compact in this case.
This renderer extends the JPanel
class overriding its paintComponent
method. The getTableCellRenderer
method sets the molecule to be shown.
public class MolRenderer extends JPanel
implements TableCellRenderer {
private MolPrinter printer;
public MolRenderer() {
printer=new MolPrinter();
}
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
... // Setting background color and border
// Passing the current molecule to MolPrinter.
printer.setMol((Molecule)value);
return this;
}
public void paintComponent(Graphics g) {
// It is very important to set the scale factor of MolPrinter,
// otherwise the image will not appear.
// The scale factor is computed by MolPrinter from
// the current size.
double scale = printer.maxScale(getSize());
// The scale factor can be maximized to prevent overscaling small molecules.
if( scale > SketchPanel.DEFAULT_SCALE ) {
scale = SketchPanel.DEFAULT_SCALE;
}
printer.setScale(scale);
// When MolPrinter is properly initialized, it can paint the
// molecule.
printer.paint((Graphics2D) g, getSize());
}
}
There are several ways to specify a cell renderer. In this example, we use type-specific renderers using the setDefaultRenderer
method of JTable.
table.setDefaultRenderer(Molecule.class, new MViewRenderer());
for setting MViewPane renderer, while
table.setDefaultRenderer(Molecule.class, new MolRenderer());
for setting MolPrinter renderer.
Using MViewPane as a cell editor provides two major advantages.
The image below shows indirect editing in use:
The code below shows the major parts of the simple cell editor.
class MViewEditor extends DefaultCellEditor {
//returns the edited molecule
public Object getCellEditorValue() {
currentMol = ((MViewPane)editorComponent).getM(0);
return currentMol;
}
public Component getTableCellEditorComponent(JTable table,
Object value, boolean isSelected, int row, int column) {
currentMol = (Molecule)value;
... // setting background color and border
((MViewPane)editorComponent).setM(0,currentMol);
return editorComponent;
}
}
This renderer class is accessible from the public API.
In case the molecular structures can be modified by a cell editor, we can ensure that the modification affects all other data being in a connection, like computed molecular data.
In this example, the third column contains such computed molecular data, the mass.
When the editing of the molecule is finished and fireEditingStopped
is called, the setValueAt(Object value, int row, int col)
method of the table model gets called. In this example, we have set the molecule masses inside this method, so that when the molecule is initialized and every time it is modified, its mass will be instantly updated.
public void setValueAt(Object value, int row, int col) {
data[row][col] = value;
if(col==1) {
// setting the mass here keeps the column updated
// upon editing the molecules
setValueAt(""+((Molecule)value).getMass(), row, 2);
}
fireTableCellUpdated(row, col);
}
The code excerpts shown above were taken from the full sample code of
ViewJTable.java
,MolRenderer.java
,MViewRenderer.java
,MViewEditor.java
.