forked from amkovkov/GranuSightSoftware2
feat: safe image loading
add AsyncImageViewModel that loading images sequentially
This commit is contained in:
@@ -39,7 +39,7 @@ public partial class AmineContentSeparatorCalibrationViewModel : ViewModelBase
|
||||
|
||||
[ObservableProperty] private Task? _captureImagesTask = null;
|
||||
|
||||
[ObservableProperty] private ObservableCollection<KeyValuePair<IImage, ImageRecord>> _images = new();
|
||||
[ObservableProperty] private ObservableCollection<AsyncImageRecordViewModel> _images = new();
|
||||
[ObservableProperty] private bool _imagesIsCapturing = false;
|
||||
[ObservableProperty] private string _imagesCapturingProgress = "";
|
||||
[ObservableProperty] private int _imagesSectionCurrentIndex = 0;
|
||||
@@ -88,12 +88,16 @@ public partial class AmineContentSeparatorCalibrationViewModel : ViewModelBase
|
||||
partial void OnEditingRecordChanged(SeparatorRecord value)
|
||||
{
|
||||
var images = value.Images
|
||||
.ToAsyncEnumerable()
|
||||
.Select(i => new KeyValuePair<string?, ImageRecord>(_imageStorageService.GetFullPath(i.ImagePath, "separator"), i))
|
||||
.Where(kv => kv.Key is not null)
|
||||
.Select(kv => new KeyValuePair<IImage, ImageRecord>(new Bitmap(kv.Key!), kv.Value));
|
||||
.Select(i =>
|
||||
{
|
||||
var path = _imageStorageService.GetFullPath(i.ImagePath, "separator");
|
||||
if (path is null)
|
||||
return null;
|
||||
return new AsyncImageRecordViewModel(path, i);
|
||||
})
|
||||
.OfType<AsyncImageRecordViewModel>();
|
||||
|
||||
Images = new(images.ToBlockingEnumerable());
|
||||
Images = new(images);
|
||||
}
|
||||
|
||||
[RelayCommand] private void ToggleMenu() => IsMenuOpen = !IsMenuOpen;
|
||||
@@ -277,10 +281,14 @@ public partial class AmineContentSeparatorCalibrationViewModel : ViewModelBase
|
||||
_illuminatorService.TurnOff();
|
||||
|
||||
var images = imageRecords
|
||||
.ToAsyncEnumerable()
|
||||
.Select(i => new KeyValuePair<string?, ImageRecord>(_imageStorageService.GetFullPath(i.ImagePath, "separator"), i))
|
||||
.Where(kv => kv.Key is not null)
|
||||
.Select(kv => new KeyValuePair<IImage, ImageRecord>(new Bitmap(kv.Key!), kv.Value));
|
||||
.Select(i =>
|
||||
{
|
||||
var path = _imageStorageService.GetFullPath(i.ImagePath, "separator");
|
||||
if (path is null)
|
||||
return null;
|
||||
return new AsyncImageRecordViewModel(path, i);
|
||||
})
|
||||
.OfType<AsyncImageRecordViewModel>();
|
||||
|
||||
Dispatcher.UIThread.Invoke(() =>
|
||||
{
|
||||
@@ -288,7 +296,7 @@ public partial class AmineContentSeparatorCalibrationViewModel : ViewModelBase
|
||||
EditingRecord.Images.AddRange(imageRecords);
|
||||
|
||||
Images.Clear();
|
||||
Images = new(images.ToBlockingEnumerable());
|
||||
Images = new(images);
|
||||
});
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Threading;
|
||||
|
||||
using GSS2.Core.Analysis.AmineContent.Database.Calibration;
|
||||
|
||||
namespace GSS2.UI.Core.ViewModels;
|
||||
|
||||
public partial class AsyncImageRecordViewModel : ViewModelBase
|
||||
{
|
||||
private static readonly SemaphoreSlim _semaphore = new(1, 1);
|
||||
|
||||
private readonly string _path;
|
||||
|
||||
[ObservableProperty] private ImageRecord _imageRecord;
|
||||
[ObservableProperty] private IImage? _image;
|
||||
[ObservableProperty] private bool _isLoading = true;
|
||||
|
||||
public AsyncImageRecordViewModel(string path, ImageRecord imageRecord)
|
||||
{
|
||||
_path = path;
|
||||
_imageRecord = imageRecord;
|
||||
_ = LoadAsync();
|
||||
}
|
||||
|
||||
private async Task LoadAsync()
|
||||
{
|
||||
await _semaphore.WaitAsync();
|
||||
|
||||
try
|
||||
{
|
||||
var bitmap = await Task.Run(() => new Bitmap(_path));
|
||||
|
||||
await Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
Image = bitmap;
|
||||
IsLoading = false;
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
await Dispatcher.UIThread.InvokeAsync(() => IsLoading = false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_semaphore.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -272,14 +272,33 @@
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<core:ZoomPanImage
|
||||
<Grid
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="11"
|
||||
Grid.Column="0"
|
||||
Grid.Column="0">
|
||||
|
||||
<core:ZoomPanImage
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Background="Black"
|
||||
ImageSource="{Binding Key}" />
|
||||
ImageSource="{Binding Image}"
|
||||
IsVisible="{Binding IsLoading, Converter={x:Static BoolConverters.Not}}" />
|
||||
|
||||
<StackPanel
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding IsLoading}">
|
||||
|
||||
<ProgressBar IsIndeterminate="True" />
|
||||
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="18"
|
||||
Text="Изображение загружается" />
|
||||
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
@@ -291,7 +310,7 @@
|
||||
Grid.Column="1"
|
||||
Classes="singleline"
|
||||
IsReadOnly="True"
|
||||
Text="{Binding Value.Id}" />
|
||||
Text="{Binding ImageRecord.Id}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
@@ -303,7 +322,7 @@
|
||||
Grid.Column="1"
|
||||
Classes="singleline"
|
||||
IsReadOnly="True"
|
||||
Text="{Binding Value.ImagePath}" />
|
||||
Text="{Binding ImageRecord.ImagePath}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
@@ -315,7 +334,7 @@
|
||||
Grid.Column="1"
|
||||
Classes="singleline"
|
||||
IsReadOnly="True"
|
||||
Text="{Binding Value.VisibleIntensity}" />
|
||||
Text="{Binding ImageRecord.VisibleIntensity}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="6"
|
||||
@@ -327,7 +346,7 @@
|
||||
Grid.Column="1"
|
||||
Classes="singleline"
|
||||
IsReadOnly="True"
|
||||
Text="{Binding Value.Uv365Intensity}" />
|
||||
Text="{Binding ImageRecord.Uv365Intensity}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="8"
|
||||
@@ -339,7 +358,7 @@
|
||||
Grid.Column="1"
|
||||
Classes="singleline"
|
||||
IsReadOnly="True"
|
||||
Text="{Binding Value.Uv254Intensity}" />
|
||||
Text="{Binding ImageRecord.Uv254Intensity}" />
|
||||
|
||||
</Grid>
|
||||
|
||||
@@ -389,13 +408,20 @@
|
||||
|
||||
</TextBlock>
|
||||
|
||||
<StackPanel
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding ImagesIsCapturing}">
|
||||
|
||||
<ProgressBar IsIndeterminate="True" />
|
||||
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="18"
|
||||
IsVisible="{Binding ImagesIsCapturing}"
|
||||
Text="{Binding ImagesCapturingProgress}" />
|
||||
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
|
||||
<!-- Кнопки -->
|
||||
|
||||
Reference in New Issue
Block a user