Page tree

Since chemical structures are frequently stored in databases, the most common usage of molecule visualization is perhaps in tablular format allowing to browse 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 visualization form will be referred to as tables. This section demonstrates how can moecules 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:

  • JTable does not contain or cache data, it is simply a view of your data.
  • Molecule tables can contain a large amount of structures, thus speed and the amount of required disk space are criticial.
  • JTable optionally enables editing the structure data.

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:

  • There is only one single cell renderer that is required to display molecules in tables, regardless of the number of structures being available.
  • There are two major approaches provided to create a cell renderer with Marvin: using MViewPane or simply painting the molecules with MolPrinter.
  • A cell editor is required only in case the structure itself is allowed to be modified by the user. Otherwise it is not needed to use a complex component for rendering.

Creating cell renderer with MViewPane

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: chemaxon.marvin.beans.MViewRenderer

Creating cell renderer with MolPrinter

In many cases it is sufficient to have only the static image of the molecule inside cells, i.e 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.

Creating cell editor with MViewPane

Using MViewPane as a cell editor provides two major advantages.

  1. It offers many functionalities that modify the visualization but not the structure itself, for example rotation, zoom, display and color schemas, or the dimension in which the structure is shown.
  2. It can also be used as an indirect cell editor. Double clicking on the MViewPane cell editor brings up MarvinSketch, in which the structure can be modified. After MarvinSketch is closed, the molecule is updated.

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 editor class is accessible from the public API: chemaxon.marvin.beans.MViewEditor

In case the molecular structures can be modified by a cell editor, we can ensure that the modification affects all other data being in 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.