Skip to content

Commit 97b1fdb

Browse files
author
Stefan Hahmann
committed
Add Python environment installation prompt and validation logic
Extend Appose integration with a user confirmation dialog for Python environment installation. Refactor environment setup to include validation via `ApposeUtils`.
1 parent 9f6b3c9 commit 97b1fdb

6 files changed

Lines changed: 107 additions & 12 deletions

File tree

src/main/java/org/mastodon/mamut/detection/DeepLearningDetector.java

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
import java.util.List;
3434
import java.util.Map;
3535

36+
import javax.swing.JOptionPane;
37+
3638
import net.imglib2.Interval;
3739
import net.imglib2.RandomAccessibleInterval;
3840
import net.imglib2.img.Img;
@@ -49,6 +51,7 @@
4951
import org.mastodon.mamut.util.ByteFormatter;
5052
import org.mastodon.mamut.util.ImgUtils;
5153
import org.mastodon.mamut.util.LabelImageUtils;
54+
import org.mastodon.mamut.util.appose.ApposeUtils;
5255
import org.mastodon.tracking.detection.AbstractDetectorOp;
5356
import org.mastodon.tracking.detection.DetectionCreatorFactory;
5457
import org.mastodon.tracking.detection.DetectionUtil;
@@ -110,15 +113,35 @@ public void compute( final List< SourceAndConverter< ? > > sources, final ModelG
110113
statusService.showStatus( "Detecting spots..." );
111114
try
112115
{
113-
log.info( "Initialize python environment." );
114-
log.info( "On first time use this requires internet connection and may take a couple of minutes." );
115-
log.info( "Progress can be observed using FIJI console: FIJI > Window > Console." );
116+
log.info( "Initialize python environment.\n" );
117+
boolean isEnvInstalled = ApposeUtils.checkEnvironmentInstalled( getPythonEnvName() );
118+
if ( !isEnvInstalled )
119+
{
120+
log.info( "Python environment not yet installed, but can be installed now.\n" );
121+
int result = JOptionPane.showConfirmDialog(
122+
null,
123+
"The required Python environment is not installed.\nWould you like to install it now?\n\nNote: This requires an internet connection and may take several minutes.",
124+
"Python Environment Installation",
125+
JOptionPane.YES_NO_OPTION,
126+
JOptionPane.QUESTION_MESSAGE
127+
);
128+
if ( result != JOptionPane.YES_OPTION )
129+
{
130+
log.info( "Python environment installation declined by user." );
131+
ok = false;
132+
errorMessage = "Python environment installation was declined. Cannot proceed without the required environment.";
133+
return;
134+
}
135+
log.info( "User confirmed installation. Installing Python environment.\n" );
136+
log.info( "Installation progress can be observed using FIJI console: FIJI > Window > Console.\n" );
137+
}
138+
ApposeUtils.confirmEnvInstallation( getPythonEnvName(), log );
116139
Environment environment = Appose.mamba().scheme( "environment.yml" ).content( getPythonEnvContent() ).logDebug()
117140
.subscribeProgress( ( title, cur, max ) -> logger.info( "{}: {}/{}", title, cur, max ) )
118141
.subscribeOutput( logger::info )
119142
.subscribeError( logger::error ).build();
120143
if ( logger.isInfoEnabled() )
121-
logger.info( "Set up environment. Path: {}", environment.base() );
144+
logger.info( "Set up environment finished. Path: {}", environment.base() );
122145
try (Service python = environment.python())
123146
{
124147
if ( isWindows() )
@@ -335,6 +358,8 @@ public Map< String, Object > getDefaultSettings()
335358

336359
protected abstract String getPythonEnvContent();
337360

361+
protected abstract String getPythonEnvName();
362+
338363
protected String getPythonEnvInit()
339364
{
340365
return "import numpy\n";

src/main/java/org/mastodon/mamut/detection/cellpose/Cellpose3Detector.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454

5555
import org.apposed.appose.Service;
5656
import org.mastodon.mamut.detection.DeepLearningDetector;
57+
import org.mastodon.mamut.detection.stardist.StarDist;
5758
import org.mastodon.tracking.mamut.detection.SpotDetectorOp;
5859
import org.scijava.Priority;
5960
import org.scijava.plugin.Plugin;
@@ -152,6 +153,12 @@ protected String getPythonEnvContent()
152153
return Cellpose3.ENV_FILE_CONTENT;
153154
}
154155

156+
@Override
157+
protected String getPythonEnvName()
158+
{
159+
return Cellpose3.ENV_NAME;
160+
}
161+
155162
@Override
156163
protected String getImportScript( final boolean dataIs2D )
157164
{

src/main/java/org/mastodon/mamut/detection/cellpose/Cellpose4Detector.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050

5151
import org.apposed.appose.Service;
5252
import org.mastodon.mamut.detection.DeepLearningDetector;
53+
import org.mastodon.mamut.detection.stardist.StarDist;
5354
import org.mastodon.tracking.mamut.detection.SpotDetectorOp;
5455
import org.mastodon.tracking.mamut.trackmate.wizard.descriptors.cellpose.Cellpose4DetectorDescriptor;
5556
import org.scijava.Priority;
@@ -137,6 +138,12 @@ protected String getPythonEnvContent()
137138
return Cellpose4.ENV_FILE_CONTENT;
138139
}
139140

141+
@Override
142+
protected String getPythonEnvName()
143+
{
144+
return Cellpose4.ENV_NAME;
145+
}
146+
140147
@Override
141148
protected String getImportScript( final boolean dataIs2D )
142149
{

src/main/java/org/mastodon/mamut/detection/stardist/StarDistDetector.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,12 @@ protected String getPythonEnvContent()
151151
return StarDist.ENV_FILE_CONTENT;
152152
}
153153

154+
@Override
155+
protected String getPythonEnvName()
156+
{
157+
return StarDist.ENV_NAME;
158+
}
159+
154160
@Override
155161
protected String getImportScript( final boolean dataIs2D )
156162
{

src/main/java/org/mastodon/mamut/linking/trackastra/TrackastraLinker.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import net.imglib2.util.Cast;
1616

1717
import org.apache.commons.lang.StringUtils;
18+
import org.apache.commons.lang3.tuple.Pair;
1819
import org.apposed.appose.Appose;
1920
import org.apposed.appose.Environment;
2021
import org.apposed.appose.Service;
@@ -28,6 +29,7 @@
2829
import org.mastodon.mamut.linking.trackastra.appose.types.RegionProps;
2930
import org.mastodon.mamut.linking.trackastra.appose.types.SingleTimepointRegionProps;
3031
import org.mastodon.mamut.util.ResourceUtils;
32+
import org.mastodon.mamut.util.appose.ApposeUtils;
3133
import org.mastodon.spatial.HasTimepoint;
3234
import org.mastodon.spatial.SpatioTemporalIndex;
3335
import org.mastodon.tracking.linking.graph.AbstractGraphParticleLinkerOp;
@@ -57,10 +59,20 @@ public void mutate1( final ReadOnlyGraph< V, E > graph, final SpatioTemporalInde
5759
Environment environment;
5860
try
5961
{
60-
environment = Appose.mamba().scheme( "environment.yml" ).content( TrackastraUtils.ENV_FILE_CONTENT ).logDebug()
61-
.subscribeProgress( ( title, cur, max ) -> log.info( "{}: {}/{}", title, cur, max ) )
62-
.subscribeOutput( log::info )
63-
.subscribeError( log::error ).build();
62+
Pair< Boolean, String > checkResult = ApposeUtils.confirmEnvInstallation( TrackastraUtils.ENV_NAME, logger );
63+
if ( Boolean.TRUE.equals( checkResult.getKey() ) )
64+
{
65+
environment = Appose.mamba().scheme( "environment.yml" ).content( TrackastraUtils.ENV_FILE_CONTENT ).logDebug()
66+
.subscribeProgress( ( title, cur, max ) -> log.info( "{}: {}/{}", title, cur, max ) )
67+
.subscribeOutput( log::info )
68+
.subscribeError( log::error ).build();
69+
}
70+
else
71+
{
72+
ok = false;
73+
errorMessage = checkResult.getValue();
74+
return;
75+
}
6476
}
6577
catch ( IOException e )
6678
{

src/main/java/org/mastodon/mamut/util/appose/ApposeUtils.java

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import javax.swing.JOptionPane;
1818
import javax.swing.SwingUtilities;
1919

20+
import org.apache.commons.lang3.tuple.Pair;
2021
import org.apposed.appose.Appose;
2122
import org.apposed.appose.builder.Builders;
2223
import org.apposed.appose.util.Environments;
@@ -137,7 +138,7 @@ public static void deleteDirectory( final File dir ) throws IOException
137138
* @param directory The directory whose size is to be calculated. Must not be null and must represent a valid directory.
138139
* @return The total size of the directory in bytes. If the directory does not exist or is null, the method will return 0.
139140
*/
140-
public static long calculateDirectorySize( File directory )
141+
public static long calculateDirectorySize( final File directory )
141142
{
142143
if ( directory == null || !directory.exists() )
143144
{
@@ -166,7 +167,7 @@ public static long calculateDirectorySize( File directory )
166167
}
167168

168169
// --- Linux/macOS: actual disk usage, like du -sb ---
169-
private static long calculateDiskUsageUnix( Path directory ) throws IOException
170+
private static long calculateDiskUsageUnix( final Path directory ) throws IOException
170171
{
171172
final long[] total = { 0 };
172173
boolean hasUnixView = supportsUnixAttributes();
@@ -205,7 +206,7 @@ private static boolean supportsUnixAttributes()
205206
* Returns the disk usage (in bytes) for a single file.
206207
* Uses actual allocated blocks if the unix view is supported.
207208
*/
208-
private static long getFileDiskUsage( Path file, boolean hasUnixView ) throws IOException
209+
private static long getFileDiskUsage( final Path file, final boolean hasUnixView ) throws IOException
209210
{
210211
if ( !hasUnixView )
211212
return Files.size( file );
@@ -224,7 +225,7 @@ private static long getFileDiskUsage( Path file, boolean hasUnixView ) throws IO
224225
}
225226

226227
// --- Windows / fallback: logical file sizes only ---
227-
private static long calculateFileLengthRecursive( File directory )
228+
private static long calculateFileLengthRecursive( final File directory )
228229
{
229230
long size = 0;
230231
File[] files = directory.listFiles();
@@ -242,4 +243,41 @@ else if ( file.isDirectory() )
242243
}
243244
return size;
244245
}
246+
247+
/**
248+
* Checks whether a specific Python environment is installed and prompts the user to install it
249+
* if it is not already installed. The user is presented with a dialog box to confirm the installation.
250+
* If the user declines, the method a pair consisting of {@code false} and an appropriate message.
251+
*
252+
* @param envName The name of the environment to check. This should match the directory
253+
* name inside the appose environments directory.
254+
* @param log The logger instance used for logging messages during the environment check
255+
* and installation process.
256+
* @return A pair where the first element is a boolean indicating if the environment is installed
257+
* or was successfully triggered for installation, and the second element is a message
258+
* providing additional information about the process or failure reason.
259+
*/
260+
public static Pair< Boolean, String > confirmEnvInstallation( final String envName, final org.scijava.log.Logger log )
261+
{
262+
boolean isEnvInstalled = checkEnvironmentInstalled( envName );
263+
if ( !isEnvInstalled )
264+
{
265+
log.info( "Python environment not yet installed, but can be installed now.\n" );
266+
int result = JOptionPane.showConfirmDialog(
267+
null,
268+
"The required Python environment is not installed.\nWould you like to install it now?\n\nNote: This requires an internet connection and may take several minutes.",
269+
"Python Environment Installation",
270+
JOptionPane.YES_NO_OPTION,
271+
JOptionPane.QUESTION_MESSAGE
272+
);
273+
if ( result != JOptionPane.YES_OPTION )
274+
{
275+
log.info( "Python environment installation declined by user." );
276+
return Pair.of( false, "Python environment installation was declined. Cannot proceed without the required environment." );
277+
}
278+
log.info( "User confirmed installation. Installing Python environment.\n" );
279+
log.info( "Installation progress can be observed using FIJI console: FIJI > Window > Console.\n" );
280+
}
281+
return Pair.of( true, "" );
282+
}
245283
}

0 commit comments

Comments
 (0)