Ij2 (interactive) results table

@ASHISRAVINDRAN, @imagejan, @haesleinhuepf
I was wondering what the status of an (interactive) ij2 results table is…?
Is there something useable?

1 Like

The net.imagej.table.* classes defining ImageJ2 results tables recently moved to scijava-table into the org.scijava.table package.

There are still a few pending pull requests on some other components:

If by interactive you mean that you have a menu bar with menu commands to save the table, this is planned but not finished yet:

If you’re looking for more advanced interactivity, such as editing table cells: I don’t think this will be available any time soon, unless you start implementing it according to your requirements, and contribute it to SciJava :slight_smile:

1 Like

Thanks a lot!

In fact, my requirements would be:

  • Load & Save from tab-delimited file
  • Ability to sort by column (basic Swing feature)
  • Option to attach an ActionListener, which gets notified when the user selects a certain row.

In terms of missing contributions, I guess these features are not yet there, right?

@ctrueden @haesleinhuepf

I started looking into adding MouseListener and KeyListener to the table window but I don’t know where to start. My code looks like this:

final GenericTable table = Utils.createTable( someData );
uiService.show( table );

My issue is that the actual table window panel seems to be generated within the uiService.show call and thus I do not know to get a handle on it or add Listeners to it.

What I did for now is convert the GenericTable to a JTable: https://github.com/tischi/fiji-plugin-morphometry/blob/master/src/main/java/de/embl/cba/morphometry/table/TableUtils.java#L10

And then have an own JPanel showing the JTable: https://github.com/tischi/fiji-plugin-morphometry/blob/master/src/main/java/de/embl/cba/morphometry/table/InteractiveTablePanel.java#L19

Let me know how I could help integrating into scijava.

Here is a movie showing some interactivity of the table:

I have the suspicion that you need to derive an own class from the given Table implementation.

If you share a minimal example code repository on git, I’m happy to contribute on that. :slight_smile:

My suspicion was different, because I felt that the ActionListeners should not be part of the Table but of the JPanel that shows the table…but I will work on creating a sandbox repo…

[EDIT] I am wrong: also in the normal Swing JTable the Listeners are in fact part of the Table.

1 Like

Done: https://github.com/tischi/fiji-interactive-table-sandbox/blob/master/src/test/java/TableTestTischi.java#L6

Playground for you: https://github.com/tischi/fiji-interactive-table-sandbox/blob/master/src/main/java/de/embl/cba/metadata/table/InteractiveGenericTable.java#L8


Hi @Christian_Tischer

that was a challenging one! I had to create/copy five classes in order to make this happen. I created a pull request to your repo.


The most important part of the code looks like this. I hope you like the API:

		ImageJ imagej = new ImageJ();

		InteractiveTableDisplay tableDisplay = (InteractiveTableDisplay) imagej.display().createDisplay(defaultGenericTable);
		tableDisplay.addClickEventListener(new InteractiveTableClickEventListener() {
			public void execute(Table t, int row, int column) {
				IJ.log("CLICK! table " + t + " row" + row + " column " + column);

The whole thing only works, because the InteractiveSwingTableDiplayViewerhas a higher priority than the SwingTableDiplayViewer. Thus, you may break other parts of your ImageJ. If anybody (@imagejan ?) has an idea of how to improve / simplify this implementation, I would be super happy to learn how to do things like that. To me it appears like “Von hinten durch die Brust ins Auge” but I found no other way.


1 Like

I think the road you guys are walking is fraught with peril. As @haesleinhuepf points out, you are relying on certain UI components being present in the environment, at higher priority than others. The whole point of the UIService is to abstract away which UI is active, such that various UI implementations can all work with the same code. Ripping into the UI to find the Swing components in order to add listeners violates this abstraction.

At minimum, I would strongly recommend checking the return values of things using instanceof, and only if the implementation matches your expectations do you proceed—and otherwise either fail silently, or issue a warning. Otherwise, all environments besides Swing will crash.

If you are ambitious, you can extend the UserInterface abstraction to support attaching input behaviors. This is a thing that @tpietzsch and I briefly touched on during the currently ongoing hackathon in Madison: whether to create a scijava-input component that is a generalization of things currently in ui-behaviour and scijava-common, such that you can write UI behaviors without using any UI-specific classes such as java.awt or javax.swing or JavaFX. But we did not do it yet…

1 Like

Hey @ctrueden

thanks for confirming my doubts. However, could you please suggest an alternate solution? The only thing we are trying to achieve is adding a listener to a window. That should be doable within a couple of lines.

I’m sorry, but the UserInterface abstraction solution I don’t understand by far, likely because I’m not so familiar with ImageJ2 architecture.

I was expecting if we implement a SpecialTable that we just need a SpecialTableDisplay. However, it turned out that we also need a SpecialTableSwingViewer and messing with priorities. I see the point for separating UI (SwingViewer) and data (Table). But what is then the Display abstraction layer?


To be precise: @Christian_Tischer’s requirement is to attach a listener that responds when the user selects a certain row. I.e.: a ListSelectionListener attached to the SelectionModel of a JTable. Right?

Here is an Groovy example illustrating what I meant:

#@ TableService tableService
#@ DisplayService displayService
#@ UIService uiService

// NB: The following will change to org.scijava.table in the next release.
import net.imagej.table.*

// TODO: Make a function like this in the TableService. :-)
def makeTable(listOfMaps) {
	table = new DefaultGenericTable()
	for (columnHeader in listOfMaps[0].keySet()) {
		column = new GenericColumn(columnHeader)
		for (row in listOfMaps) {
	return table	

// Create an ImageJ Table.
table = makeTable([
	["Name": "Christian", "ID": "tischi",        "City": "Heidelberg"],
	["Name": "Jan",       "ID": "imagejan",      "City": "Basel"],
	["Name": "Robert",    "ID": "haesleinhuepf", "City": "Dresden"],
	["Name": "Curtis",    "ID": "ctrueden",      "City": "Madison"],

// Wrap it in a TableDisplay (UI-agnostic visualization object).
tableDisplay = displayService.createDisplay(table)

// Look up the DisplayViewer (UI-specific visualization object) that wraps the Display.
sleep(50) // HACK: Creation of DisplayViewer is asynchronous. :-(
tableViewer = uiService.getDisplayViewer(tableDisplay)
if (tableViewer == null) {
	println("NO DisplayViewer")

// Ask the DisplayViewer for its associated DisplayWindow.
window = tableViewer.getWindow()

// Ensure the DisplayWindow is of a type we know how to handle.
if (!(window instanceof javax.swing.JFrame)) {
	println("DisplayViewer window is not a Swing frame!")

// A function to extract a Swing component from a container recursively.
def findComponent(container, componentClass) {
	for (c in container.getComponents()) {
		if (componentClass.isInstance(c)) return c
		else if (c instanceof java.awt.Container) {
			result = findComponent(c, componentClass)
			if (result != null) return result
	return null

// Extract the JTable from the window.
swingTable = findComponent(window.getContentPane(), javax.swing.JTable.class)
if (swingTable == null) {
	println("No JTable found!")

// Add a table selection listener.
swingTable.getSelectionModel().addListSelectionListener({event ->
	row = swingTable.getSelectedRow()
	println("Row selected = " + row)
	name = swingTable.getValueAt(row, 1)
	id = swingTable.getValueAt(row, 2)
	city = swingTable.getValueAt(row, 3)
	println("You selected row #" + row + ": name=" + name + ", id=" + id + ", city=" + city)

However: I would like to discuss & design a way of using SciJava’s UI-agnostic event mechanism for UI actions. That way, every table viewer implementation would fire the same SciJava events on the EventService, and you could listen for them, without worrying about whether it is a Swing-based UI or not. Whereas the above code only works for the Swing UI, and only under certain constraints (a single JTable inside a JFrame).

This is true. It is too complicated right now. There are plans to simplify the hierarchy. But for now, yes, a Display is the UI-agnostic representation of the visualization, whereas the DisplayViewer is a UI-specifici implementation.

I hope you (almost) never need to code your own SpecialTable nor SpecialTableDisplay nor SpecialTableSwingViewer. I’d like the built-in SciJava Table objects and associated class layers to support the events people need already.


Hi All,

@imagejan @haesleinhuepf @iarganda @dgault

I have an interactive table prototype (thanks again a lot to @tpietzsch for all the discussions).

This first implementation is tailored to viewing MorphoLibJ output.

It would be amazing ( if you are interested and find some time… ), if you

  • could test it
  • suggest usability improvements
  • and if it crashes, it would be great if you could let me know during which action it crashed, as I probably still have some concurrency bugs…

To run it you would need

  • the EMBL-CBA Update Site
  • and then you could run below IJ1 Macro:
// Create image
run("Close All");
run("Blobs (25K)");
run("Invert LUT");
run("Duplicate...", "title=mask");
setThreshold(111, 255);
run("Convert to Mask");

// Segment and measure using MorphoLibJ
run("Connected Components Labeling", "connectivity=4 type=[16 bits]");
run("Analyze Regions", "area perimeter circularity euler_number centroid inertia_ellipse ellipse_elong. convexity max._feret oriented_box oriented_box_elong. geodesic tortuosity max._inscribed_disc geodesic_elong.");

// Explore results
run("Explore MorphoLibJ Segmentation");
  • next, you would have to select like this:

In theory, everything should also work for 3D and for 3D+t. For 3D+t the table would need to contain a column indicating the timepoint. As far as I know, timelapse is currently not support in MorphoLibJ, is it?


That’s so awesome, thanks for sharing, @Christian_Tischer!

I’ll try to test it soon. As far as I can see from table-utils, the repository is set up for being built with Travis CI, but apparently isn’t activated on Travis currently. Would you mind going to travis-ci.com and activating your repository there? That would allow getting the latest jar file from maven.imagej.net, and permit other projects to use yours as a dependency.

What are the prerequisites of the table? You need a label column corresponding to your label image, and any other numeric measurement?
It would be fantastic to see this integrated into ImageJ/Fiji, and working with any org.scijava.table.Table in the future (and of course still with any compatible IJ1 ResultsTable via converters in imagej-legacy).


Yes, there must be one column with the Label, which must be the same number that the object has in the label mask image.

Thanks for pointing this out! Is now everybody allowed to do this? It used to be only few people with admin rights…

Yes, that would be great, but would probably need a little hackathon to integrate the prototype into SciJava…

Maybe we can continue the discussion about core integration on github, I made an issue for the selection listeners integration: