Skip to content

Commit c809e53

Browse files
Merge pull request #127 from mastodon-sc/fix-dendrogram-view
Fix dendrogram view
2 parents c7c21ef + 32d8d5d commit c809e53

4 files changed

Lines changed: 92 additions & 68 deletions

File tree

src/main/java/org/mastodon/mamut/clustering/ClusterLineagesController.java

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
import org.mastodon.mamut.ProjectModel;
3535
import org.mastodon.mamut.clustering.config.ClusteringMethod;
3636
import org.mastodon.mamut.clustering.config.SimilarityMeasure;
37-
import org.mastodon.mamut.clustering.multiproject.ClassifiableProject;
37+
import org.mastodon.mamut.clustering.multiproject.ClusterableProject;
3838
import org.mastodon.mamut.clustering.multiproject.ExternalProjects;
3939
import org.mastodon.mamut.clustering.util.HierarchicalClusteringResult;
4040
import org.mastodon.mamut.clustering.config.CropCriteria;
@@ -145,31 +145,32 @@ public String createTagSet()
145145
try
146146
{
147147
running = true;
148-
return runClassification();
148+
return runClustering();
149149
}
150150
finally
151151
{
152152
running = false;
153153
}
154154
}
155155

156-
private String runClassification()
156+
private String runClustering()
157157
{
158+
referenceProjectModel.getBranchGraphSync().sync();
158159
ReentrantReadWriteLock.ReadLock lock = referenceModel.getGraph().getLock().readLock();
159160
lock.lock();
160161
String createdTagSetName;
161162
try
162163
{
163-
Pair< List< ClassifiableProject >, double[][] > rootsAndDistances = getRootsAndDistanceMatrix();
164-
List< ClassifiableProject > rootsMatrix = rootsAndDistances.getLeft();
164+
Pair< List< ClusterableProject >, double[][] > rootsAndDistances = getRootsAndDistanceMatrix();
165+
List< ClusterableProject > rootsMatrix = rootsAndDistances.getLeft();
165166
double[][] distances = rootsAndDistances.getRight();
166-
ClassifiableProject referenceProject = rootsMatrix.get( 0 );
167+
ClusterableProject referenceProject = rootsMatrix.get( 0 );
167168
HierarchicalClusteringResult< BranchSpotTree > hierarchicalClusteringResult =
168-
classifyLineageTrees( referenceProject.getTrees(), distances );
169+
clusterLineageTrees( referenceProject.getTrees(), distances );
169170
Function< BranchSpotTree, BranchSpot > branchSpotProvider = BranchSpotTree::getBranchSpot;
170-
createdTagSetName = applyClassification( hierarchicalClusteringResult, referenceModel, branchSpotProvider );
171+
createdTagSetName = applyTagSet( hierarchicalClusteringResult, referenceModel, branchSpotProvider );
171172
if ( addTagSetToExternalProjects && rootsMatrix.size() > 1 )
172-
classifyExternalProjects( rootsMatrix, distances );
173+
clusterExternalProjects( rootsMatrix, distances );
173174
if ( showDendrogram )
174175
showDendrogram( hierarchicalClusteringResult );
175176
}
@@ -180,21 +181,21 @@ private String runClassification()
180181
return createdTagSetName;
181182
}
182183

183-
private void classifyExternalProjects( final List< ClassifiableProject > rootsMatrix, final double[][] distances )
184+
private void clusterExternalProjects( final List< ClusterableProject > rootsMatrix, final double[][] distances )
184185
{
185186
Function< BranchSpotTree, BranchSpot > branchSpotProvider;
186187
for ( int i = 1; i < rootsMatrix.size(); i++ ) // NB: start at 1 to skip reference project
187188
{
188-
ClassifiableProject project = rootsMatrix.get( i );
189+
ClusterableProject project = rootsMatrix.get( i );
189190
HierarchicalClusteringResult< BranchSpotTree > hierarchicalClusteringResult =
190-
classifyLineageTrees( project.getTrees(), distances );
191+
clusterLineageTrees( project.getTrees(), distances );
191192
ProjectModel projectModel = project.getProjectModel();
192193
Model model = projectModel.getModel();
193194
File file = project.getFile();
194195
branchSpotProvider = branchSpotTree -> model.getBranchGraph().vertices().stream()
195196
.filter( ( branchSpot -> branchSpot.getFirstLabel().equals( branchSpotTree.getName() ) ) )
196197
.findFirst().orElse( null );
197-
applyClassification( hierarchicalClusteringResult, model, branchSpotProvider );
198+
applyTagSet( hierarchicalClusteringResult, model, branchSpotProvider );
198199
try
199200
{
200201
ProjectSaver.saveProject( file, projectModel );
@@ -207,28 +208,28 @@ private void classifyExternalProjects( final List< ClassifiableProject > rootsMa
207208
}
208209
}
209210

210-
private Pair< List< ClassifiableProject >, double[][] > getRootsAndDistanceMatrix()
211+
private Pair< List< ClusterableProject >, double[][] > getRootsAndDistanceMatrix()
211212
{
212213
List< BranchSpotTree > roots = getRoots();
213-
ClassifiableProject referenceProject = new ClassifiableProject( null, referenceProjectModel, roots );
214+
ClusterableProject referenceProject = new ClusterableProject( null, referenceProjectModel, roots );
214215
if ( externalProjects.isEmpty() )
215216
{
216217
double[][] distances = HierarchicalClusteringUtils.getDistanceMatrix( roots, similarityMeasure );
217218
return Pair.of( Collections.singletonList( referenceProject ), distances );
218219
}
219220

220221
List< String > commonRootNames = findCommonRootNames();
221-
List< ClassifiableProject > projects = new ArrayList<>();
222+
List< ClusterableProject > projects = new ArrayList<>();
222223

223224
keepCommonRootsAndSort( roots, commonRootNames );
224225
projects.add( referenceProject );
225226
for ( Map.Entry< File, ProjectModel > project : externalProjects.getProjects() )
226227
{
227228
List< BranchSpotTree > externalRoots = getRoots( project.getValue() );
228229
keepCommonRootsAndSort( externalRoots, commonRootNames );
229-
projects.add( new ClassifiableProject( project.getKey(), project.getValue(), externalRoots ) );
230+
projects.add( new ClusterableProject( project.getKey(), project.getValue(), externalRoots ) );
230231
}
231-
List< List< BranchSpotTree > > treeMatrix = projects.stream().map( ClassifiableProject::getTrees ).collect( Collectors.toList() );
232+
List< List< BranchSpotTree > > treeMatrix = projects.stream().map( ClusterableProject::getTrees ).collect( Collectors.toList() );
232233
return Pair.of( projects, HierarchicalClusteringUtils.getAverageDistanceMatrix( treeMatrix, similarityMeasure ) );
233234
}
234235

@@ -286,10 +287,10 @@ private void showDendrogram( final HierarchicalClusteringResult< BranchSpotTree
286287
String header = "<html><body>Dendrogram of hierarchical clustering of lineages<br>" + getParameters() + "</body></html>";
287288
DendrogramView< BranchSpotTree > dendrogramView =
288289
new DendrogramView<>( hierarchicalClusteringResult, header, referenceModel, prefs, referenceProjectModel.getProjectName() );
289-
dendrogramView.show();
290+
dendrogramView.setVisible( true );
290291
}
291292

292-
private HierarchicalClusteringResult< BranchSpotTree > classifyLineageTrees( final List< BranchSpotTree > roots,
293+
private HierarchicalClusteringResult< BranchSpotTree > clusterLineageTrees( final List< BranchSpotTree > roots,
293294
final double[][] distances )
294295
{
295296
if ( roots.size() != distances.length )
@@ -306,9 +307,8 @@ private HierarchicalClusteringResult< BranchSpotTree > classifyLineageTrees( fin
306307
return result;
307308
}
308309

309-
private String applyClassification( final HierarchicalClusteringResult< BranchSpotTree > hierarchicalClusteringResult,
310-
final Model model,
311-
final Function< BranchSpotTree, BranchSpot > branchSpotProvider )
310+
private String applyTagSet( final HierarchicalClusteringResult< BranchSpotTree > hierarchicalClusteringResult,
311+
final Model model, final Function< BranchSpotTree, BranchSpot > branchSpotProvider )
312312
{
313313
String tagSetName = getTagSetName();
314314
List< HierarchicalClusteringResult.Group< BranchSpotTree > > groups = hierarchicalClusteringResult.getGroups();

src/main/java/org/mastodon/mamut/clustering/multiproject/ClassifiableProject.java renamed to src/main/java/org/mastodon/mamut/clustering/multiproject/ClusterableProject.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,17 @@
3535
import java.util.List;
3636

3737
/**
38-
* Small helper class to hold a project file, its {@link ProjectModel} and the trees that are to be classified.
38+
* Small helper class to hold a project file, its {@link ProjectModel} and the trees that are to be clustered.
3939
*/
40-
public class ClassifiableProject
40+
public class ClusterableProject
4141
{
4242
private final File file;
4343

4444
private final ProjectModel projectModel;
4545

4646
private final List< BranchSpotTree > trees;
4747

48-
public ClassifiableProject( final File file, final ProjectModel projectModel, final List< BranchSpotTree > trees )
48+
public ClusterableProject( final File file, final ProjectModel projectModel, final List< BranchSpotTree > trees )
4949
{
5050
this.file = file;
5151
this.projectModel = projectModel;

src/main/java/org/mastodon/mamut/clustering/treesimilarity/tree/BranchSpotTree.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,16 @@ public void setParams( final boolean includeName, final boolean includeTag, fina
169169
private String getTagLabel()
170170
{
171171
Spot ref = model.getGraph().vertexRef();
172-
String tagLabel = LegacyTagSetUtils.getTagLabel( model, branchSpot, tagSet, ref );
172+
String tagLabel = null;
173+
try
174+
{
175+
// TODO: this try-catch block is a workaround. It will not be needed anymore after mastodon-core beta-34
176+
tagLabel = LegacyTagSetUtils.getTagLabel( model, branchSpot, tagSet, ref );
177+
}
178+
catch ( NullPointerException e )
179+
{
180+
// happens, when the branchSpot is not in the model anymore
181+
}
173182
model.getGraph().releaseRef( ref );
174183
if ( tagLabel == null )
175184
tagLabel = "";

src/main/java/org/mastodon/mamut/clustering/ui/DendrogramView.java

Lines changed: 56 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,19 @@
2828
*/
2929
package org.mastodon.mamut.clustering.ui;
3030

31-
import net.miginfocom.swing.MigLayout;
32-
import org.mastodon.mamut.clustering.util.HierarchicalClusteringResult;
33-
import org.mastodon.mamut.model.Model;
34-
import org.mastodon.model.tag.TagSetModel;
35-
import org.mastodon.model.tag.TagSetStructure;
36-
import org.mastodon.ui.util.ExtensionFileFilter;
37-
import org.mastodon.ui.util.FileChooser;
38-
import org.scijava.prefs.PrefService;
39-
import org.slf4j.Logger;
40-
import org.slf4j.LoggerFactory;
31+
import java.awt.Color;
32+
import java.awt.Desktop;
33+
import java.awt.Dimension;
34+
import java.awt.Toolkit;
35+
import java.awt.event.ActionListener;
36+
import java.awt.event.WindowAdapter;
37+
import java.awt.event.WindowEvent;
38+
import java.io.File;
39+
import java.io.IOException;
40+
import java.lang.invoke.MethodHandles;
41+
import java.util.ArrayList;
42+
import java.util.List;
43+
import java.util.function.ObjIntConsumer;
4144

4245
import javax.swing.BorderFactory;
4346
import javax.swing.JButton;
@@ -50,24 +53,25 @@
5053
import javax.swing.JPopupMenu;
5154
import javax.swing.JScrollPane;
5255
import javax.swing.WindowConstants;
53-
import java.awt.Color;
54-
import java.awt.Desktop;
55-
import java.awt.Dimension;
56-
import java.awt.Toolkit;
57-
import java.awt.event.ActionListener;
58-
import java.io.File;
59-
import java.io.IOException;
60-
import java.lang.invoke.MethodHandles;
61-
import java.util.ArrayList;
62-
import java.util.List;
63-
import java.util.function.ObjIntConsumer;
56+
57+
import net.miginfocom.swing.MigLayout;
58+
59+
import org.mastodon.mamut.clustering.util.HierarchicalClusteringResult;
60+
import org.mastodon.mamut.model.Model;
61+
import org.mastodon.model.tag.TagSetModel;
62+
import org.mastodon.model.tag.TagSetStructure;
63+
import org.mastodon.ui.util.ExtensionFileFilter;
64+
import org.mastodon.ui.util.FileChooser;
65+
import org.scijava.prefs.PrefService;
66+
import org.slf4j.Logger;
67+
import org.slf4j.LoggerFactory;
6468

6569
/**
6670
* A class that represents a UI view of a dendrogram.<br>
6771
* It encapsulates a {@link HierarchicalClusteringResult} object and a headline that write the parameters that were used for it.
6872
* @param <T> the type of the objects that are clustered
6973
*/
70-
public class DendrogramView< T > implements TagSetModel.TagSetModelListener
74+
public class DendrogramView< T > extends JFrame implements TagSetModel.TagSetModelListener
7175
{
7276
private static final Logger logger = LoggerFactory.getLogger( MethodHandles.lookup().lookupClass() );
7377

@@ -93,8 +97,6 @@ public class DendrogramView< T > implements TagSetModel.TagSetModelListener
9397

9498
private final PrefService prefs;
9599

96-
private final JFrame frame;
97-
98100
private final JPanel canvas = new JPanel( new MigLayout( "fill" ) );
99101

100102
private final JCheckBox showThresholdCheckBox = new JCheckBox( "Show clustering threshold" );
@@ -126,21 +128,23 @@ public DendrogramView( final HierarchicalClusteringResult< T > hierarchicalClust
126128
final PrefService prefs,
127129
final String projectName )
128130
{
131+
super( "Hierarchical clustering of lineage trees" );
132+
129133
this.hierarchicalClusteringResult = hierarchicalClusteringResult;
130134
this.model = model;
131135
this.prefs = prefs;
132136
this.projectName = projectName;
133137

134-
frame = new JFrame( "Hierarchical clustering of lineage trees" );
135-
frame.setDefaultCloseOperation( WindowConstants.DISPOSE_ON_CLOSE );
138+
136139
int minHeight = 50;
137140
int initialDendrogramHeight = hierarchicalClusteringResult == null ? minHeight
138141
: ( hierarchicalClusteringResult.getObjectCount() ) * getDefaultFontHeight() + DendrogramPanel.DENDROGRAM_VERTICAL_OFFSET;
139142
initialDendrogramHeight += 200;
140143
initialDendrogramHeight = Math.min( initialDendrogramHeight, 1000 );
141-
frame.setSize( 1000, initialDendrogramHeight );
142-
frame.setLayout( new MigLayout( "insets 10, fill" ) );
143-
frame.add( canvas, "grow" );
144+
setSize( 1000, initialDendrogramHeight );
145+
setLayout( new MigLayout( "insets 10, fill" ) );
146+
add( canvas, "grow" );
147+
setDefaultCloseOperation( WindowConstants.DISPOSE_ON_CLOSE );
144148

145149
headlineLabel = new JLabel( headline );
146150
dendrogramPanel = new DendrogramPanel<>( hierarchicalClusteringResult );
@@ -153,14 +157,6 @@ public DendrogramView( final HierarchicalClusteringResult< T > hierarchicalClust
153157
this.model.getTagSetModel().listeners().add( this );
154158
}
155159

156-
/**
157-
* Sets the visibility of the frame to {@code true}.
158-
*/
159-
public void show()
160-
{
161-
frame.setVisible( true );
162-
}
163-
164160
public JPanel getCanvas()
165161
{
166162
return canvas;
@@ -214,8 +210,10 @@ private void initSettings()
214210

215211
private void initBehavior()
216212
{
217-
showThresholdCheckBox.addActionListener( ignore -> showThreshold( showThresholdCheckBox.isSelected() ) );
218-
showMedianCheckBox.addActionListener( ignore -> showMedian( showMedianCheckBox.isSelected() ) );
213+
ActionListener showThresholdListener = ignore -> showThreshold( showThresholdCheckBox.isSelected() );
214+
showThresholdCheckBox.addActionListener( showThresholdListener );
215+
ActionListener showMedianListener = ignore -> showMedian( showMedianCheckBox.isSelected() );
216+
showMedianCheckBox.addActionListener( showMedianListener );
219217
ActionListener tagSetListener = event -> {
220218
selectedTagSet =
221219
tagSetComboBox.getSelectedItem() == null ? null : ( ( TagSetElement ) tagSetComboBox.getSelectedItem() ).tagSet;
@@ -225,7 +223,24 @@ private void initBehavior()
225223
showTagLabelsCheckBox.addActionListener( tagSetListener );
226224
tagSetComboBox.addActionListener( tagSetListener );
227225
JPopupMenu popupMenu = new PopupMenu();
228-
menuButton.addActionListener( event -> popupMenu.show( menuButton, 0, menuButton.getHeight() ) );
226+
ActionListener menuListener = event -> popupMenu.show( menuButton, 0, menuButton.getHeight() );
227+
menuButton.addActionListener( menuListener );
228+
229+
addWindowListener( new WindowAdapter()
230+
{
231+
@Override
232+
public void windowClosing( WindowEvent windowEvent )
233+
{
234+
showThresholdCheckBox.removeActionListener( showThresholdListener );
235+
showMedianCheckBox.removeActionListener( showMedianListener );
236+
showRootLabelsCheckBox.removeActionListener( tagSetListener );
237+
showTagLabelsCheckBox.removeActionListener( tagSetListener );
238+
tagSetComboBox.removeActionListener( tagSetListener );
239+
menuButton.removeActionListener( menuListener );
240+
if ( null != model )
241+
model.getTagSetModel().listeners().remove( DendrogramView.this );
242+
}
243+
} );
229244
}
230245

231246
private void showThreshold( final boolean showThreshold )
@@ -333,7 +348,7 @@ private PopupMenu()
333348

334349
private void chooseFileAndExport( final String extension, final String fileName, final ObjIntConsumer< File > exportFunction )
335350
{
336-
File chosenFile = FileChooser.chooseFile( frame, fileName + '.' + extension,
351+
File chosenFile = FileChooser.chooseFile( this, fileName + '.' + extension,
337352
new ExtensionFileFilter( extension ), "Save dendrogram to " + extension, FileChooser.DialogType.SAVE );
338353
if ( chosenFile != null )
339354
{

0 commit comments

Comments
 (0)