feat: Update FaceAiTabView to support output file selection and validation for .pkl files

This commit is contained in:
MaddoScientisto 2026-03-12 23:24:44 +01:00
commit fa09f7c324
2 changed files with 83 additions and 19 deletions

View file

@ -4,7 +4,7 @@
<ScrollViewer> <ScrollViewer>
<StackPanel Margin="4" Spacing="6"> <StackPanel Margin="4" Spacing="6">
<TextBlock Text="Face Recognition Encoder" FontWeight="Bold" /> <TextBlock Text="Face Recognition Encoder" FontWeight="Bold" />
<TextBlock Text="Esegue face_encoder.exe usando la cartella Destinazione corrente come --images." <TextBlock Text="Esegue face_encoder.exe usando la cartella Destinazione corrente come --images e un file .pkl come --out."
TextWrapping="Wrap" Opacity="0.8" /> TextWrapping="Wrap" Opacity="0.8" />
<TextBlock Text="Eseguibile" FontWeight="Bold" Margin="0,4,0,0" /> <TextBlock Text="Eseguibile" FontWeight="Bold" Margin="0,4,0,0" />
@ -15,11 +15,17 @@
<Button Grid.Column="3" Name="FaceOpenExecutableButton" Content="Apri" Click="OpenFaceExecutableFolder_Click" Width="56" Margin="6,0,0,0" /> <Button Grid.Column="3" Name="FaceOpenExecutableButton" Content="Apri" Click="OpenFaceExecutableFolder_Click" Width="56" Margin="6,0,0,0" />
</Grid> </Grid>
<Grid ColumnDefinitions="Auto,*,Auto,Auto" ColumnSpacing="6">
<TextBlock Grid.Column="0" Text="Sorgente:" VerticalAlignment="Center" />
<TextBox Grid.Column="1" Name="FaceDestinationPathTextBox" Text="{Binding DestinationPath, Mode=OneWay}" IsReadOnly="True" />
<Button Grid.Column="3" Name="FaceOpenDestinationButton" Content="Apri" Click="OpenFaceDestinationFolder_Click" Width="56" Margin="6,0,0,0" />
</Grid>
<TextBlock Text="Output encodings" FontWeight="Bold" Margin="0,4,0,0" /> <TextBlock Text="Output encodings" FontWeight="Bold" Margin="0,4,0,0" />
<Grid ColumnDefinitions="Auto,*,Auto,Auto" ColumnSpacing="6"> <Grid ColumnDefinitions="Auto,*,Auto,Auto" ColumnSpacing="6">
<TextBlock Grid.Column="0" Text="Cartella out:" VerticalAlignment="Center" /> <TextBlock Grid.Column="0" Text="File out (.pkl):" VerticalAlignment="Center" />
<TextBox Grid.Column="1" Name="FaceOutputFolderTextBox" Text="{Binding FaceOutputFolderPath, Mode=TwoWay}" Watermark="C:\\output\\encodings" /> <TextBox Grid.Column="1" Name="FaceOutputFolderTextBox" Text="{Binding FaceOutputFolderPath, Mode=TwoWay}" Watermark="C:\\output\\encodings.pkl" />
<Button Grid.Column="2" Name="FaceSelectOutputButton" Content="Scegli..." Click="SelectFaceOutputFolder_Click" Width="88" Margin="6,0,0,0" /> <Button Grid.Column="2" Name="FaceSelectOutputButton" Content="Scegli..." Click="SelectFaceOutputFile_Click" Width="88" Margin="6,0,0,0" />
<Button Grid.Column="3" Name="FaceOpenOutputButton" Content="Apri" Click="OpenFaceOutputFolder_Click" Width="56" Margin="6,0,0,0" /> <Button Grid.Column="3" Name="FaceOpenOutputButton" Content="Apri" Click="OpenFaceOutputFolder_Click" Width="56" Margin="6,0,0,0" />
</Grid> </Grid>

View file

@ -57,7 +57,7 @@ public partial class FaceAiTabView : Avalonia.Controls.UserControl
} }
} }
private async void SelectFaceOutputFolder_Click(object? sender, RoutedEventArgs e) private async void SelectFaceOutputFile_Click(object? sender, RoutedEventArgs e)
{ {
var outputBox = this.FindControl<Avalonia.Controls.TextBox>("FaceOutputFolderTextBox"); var outputBox = this.FindControl<Avalonia.Controls.TextBox>("FaceOutputFolderTextBox");
if (outputBox is null) if (outputBox is null)
@ -72,14 +72,21 @@ public partial class FaceAiTabView : Avalonia.Controls.UserControl
return; return;
} }
var folders = await storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions var files = await storageProvider.SaveFilePickerAsync(new FilePickerSaveOptions
{ {
Title = "Seleziona cartella output encodings" Title = "Seleziona file output encodings (.pkl)",
SuggestedFileName = "encodings.pkl",
DefaultExtension = "pkl",
FileTypeChoices =
[
new FilePickerFileType("Pickle file") { Patterns = ["*.pkl"] }
],
ShowOverwritePrompt = true
}); });
if (folders.Count > 0) if (files is not null)
{ {
outputBox.Text = folders[0].Path.LocalPath; outputBox.Text = files.Path.LocalPath;
if (DataContext is DataModel model) if (DataContext is DataModel model)
{ {
model.FaceOutputFolderPath = outputBox.Text; model.FaceOutputFolderPath = outputBox.Text;
@ -119,7 +126,38 @@ public partial class FaceAiTabView : Avalonia.Controls.UserControl
return; return;
} }
OpenInExplorer(outputBox.Text); var outputPath = outputBox.Text?.Trim();
if (string.IsNullOrWhiteSpace(outputPath))
{
return;
}
if (File.Exists(outputPath))
{
OpenInExplorer(outputPath);
return;
}
var directory = Path.GetDirectoryName(outputPath);
OpenInExplorer(string.IsNullOrWhiteSpace(directory) ? outputPath : directory);
}
private void OpenFaceDestinationFolder_Click(object? sender, RoutedEventArgs e)
{
var destBox = this.FindControl<Avalonia.Controls.TextBox>("FaceDestinationPathTextBox");
string? path = destBox?.Text?.Trim();
if (string.IsNullOrWhiteSpace(path) && DataContext is DataModel model)
{
path = (model.DestinationPath ?? string.Empty).Trim();
}
if (string.IsNullOrWhiteSpace(path))
{
return;
}
var directory = Path.GetDirectoryName(path);
OpenInExplorer(string.IsNullOrWhiteSpace(directory) ? path : directory);
} }
private async void RunFaceEncoder_Click(object? sender, RoutedEventArgs e) private async void RunFaceEncoder_Click(object? sender, RoutedEventArgs e)
@ -142,11 +180,11 @@ public partial class FaceAiTabView : Avalonia.Controls.UserControl
} }
var executablePath = executableBox.Text?.Trim().Trim('"') ?? string.Empty; var executablePath = executableBox.Text?.Trim().Trim('"') ?? string.Empty;
var outputFolder = outputFolderBox.Text?.Trim().Trim('"') ?? string.Empty; var outputFilePath = outputFolderBox.Text?.Trim().Trim('"') ?? string.Empty;
var imagesFolder = (model.DestinationPath ?? string.Empty).Trim().Trim('"'); var imagesFolder = (model.DestinationPath ?? string.Empty).Trim().Trim('"');
model.FaceExecutablePath = executablePath; model.FaceExecutablePath = executablePath;
model.FaceOutputFolderPath = outputFolder; model.FaceOutputFolderPath = outputFilePath;
if (string.IsNullOrWhiteSpace(executablePath) || !File.Exists(executablePath)) if (string.IsNullOrWhiteSpace(executablePath) || !File.Exists(executablePath))
{ {
@ -160,20 +198,30 @@ public partial class FaceAiTabView : Avalonia.Controls.UserControl
return; return;
} }
if (string.IsNullOrWhiteSpace(outputFolder)) if (string.IsNullOrWhiteSpace(outputFilePath))
{ {
statusBlock.Text = "Inserisci la cartella di output."; statusBlock.Text = "Inserisci il file di output .pkl.";
return;
}
if (!string.Equals(Path.GetExtension(outputFilePath), ".pkl", StringComparison.OrdinalIgnoreCase))
{
statusBlock.Text = "Il file di output deve avere estensione .pkl.";
return; return;
} }
try try
{ {
Directory.CreateDirectory(outputFolder); var outputDirectory = Path.GetDirectoryName(outputFilePath);
if (!string.IsNullOrWhiteSpace(outputDirectory))
{
Directory.CreateDirectory(outputDirectory);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "Unable to create face output folder: {OutputFolder}", outputFolder); _logger.LogError(ex, "Unable to create face output directory for file: {OutputFilePath}", outputFilePath);
statusBlock.Text = "Impossibile creare la cartella di output."; statusBlock.Text = "Impossibile creare la cartella del file di output.";
return; return;
} }
@ -187,7 +235,7 @@ public partial class FaceAiTabView : Avalonia.Controls.UserControl
try try
{ {
var imagesFolderArg = NormalizeDirectoryPathArgument(imagesFolder); var imagesFolderArg = NormalizeDirectoryPathArgument(imagesFolder);
var outputFolderArg = NormalizeDirectoryPathArgument(outputFolder); var outputFileArg = NormalizeFilePathArgument(outputFilePath);
var processStartInfo = new ProcessStartInfo var processStartInfo = new ProcessStartInfo
{ {
@ -201,7 +249,7 @@ public partial class FaceAiTabView : Avalonia.Controls.UserControl
processStartInfo.ArgumentList.Add("--images"); processStartInfo.ArgumentList.Add("--images");
processStartInfo.ArgumentList.Add(imagesFolderArg); processStartInfo.ArgumentList.Add(imagesFolderArg);
processStartInfo.ArgumentList.Add("--out"); processStartInfo.ArgumentList.Add("--out");
processStartInfo.ArgumentList.Add(outputFolderArg); processStartInfo.ArgumentList.Add(outputFileArg);
using var process = new Process { StartInfo = processStartInfo, EnableRaisingEvents = true }; using var process = new Process { StartInfo = processStartInfo, EnableRaisingEvents = true };
process.OutputDataReceived += (_, args) => process.OutputDataReceived += (_, args) =>
@ -314,4 +362,14 @@ public partial class FaceAiTabView : Avalonia.Controls.UserControl
return normalized; return normalized;
} }
private static string NormalizeFilePathArgument(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return string.Empty;
}
return value.Trim().Trim('"');
}
} }