mirror of
https://github.com/azaion/annotations.git
synced 2026-04-22 22:26:31 +00:00
fix editing non-timed annotations
This commit is contained in:
@@ -14,7 +14,7 @@ public class AnnotationControl : Border
|
|||||||
|
|
||||||
private readonly Grid _grid;
|
private readonly Grid _grid;
|
||||||
private readonly TextBlock _classNameLabel;
|
private readonly TextBlock _classNameLabel;
|
||||||
public TimeSpan Time { get; set; }
|
public TimeSpan? Time { get; set; }
|
||||||
|
|
||||||
private AnnotationClass _annotationClass = null!;
|
private AnnotationClass _annotationClass = null!;
|
||||||
public AnnotationClass AnnotationClass
|
public AnnotationClass AnnotationClass
|
||||||
@@ -41,7 +41,7 @@ public class AnnotationControl : Border
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public AnnotationControl(AnnotationClass annotationClass, TimeSpan time, Action<object, MouseButtonEventArgs> resizeStart)
|
public AnnotationControl(AnnotationClass annotationClass, TimeSpan? time, Action<object, MouseButtonEventArgs> resizeStart)
|
||||||
{
|
{
|
||||||
Time = time;
|
Time = time;
|
||||||
_resizeStart = resizeStart;
|
_resizeStart = resizeStart;
|
||||||
|
|||||||
@@ -34,13 +34,13 @@ public class CanvasEditor : Canvas
|
|||||||
public static readonly DependencyProperty GetTimeFuncProp =
|
public static readonly DependencyProperty GetTimeFuncProp =
|
||||||
DependencyProperty.Register(
|
DependencyProperty.Register(
|
||||||
nameof(GetTimeFunc),
|
nameof(GetTimeFunc),
|
||||||
typeof(Func<TimeSpan>),
|
typeof(Func<TimeSpan?>),
|
||||||
typeof(CanvasEditor),
|
typeof(CanvasEditor),
|
||||||
new PropertyMetadata(null));
|
new PropertyMetadata(null));
|
||||||
|
|
||||||
public Func<TimeSpan> GetTimeFunc
|
public Func<TimeSpan?> GetTimeFunc
|
||||||
{
|
{
|
||||||
get => (Func<TimeSpan>)GetValue(GetTimeFuncProp);
|
get => (Func<TimeSpan?>)GetValue(GetTimeFuncProp);
|
||||||
set => SetValue(GetTimeFuncProp, value);
|
set => SetValue(GetTimeFuncProp, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,7 +310,7 @@ public class CanvasEditor : Canvas
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public AnnotationControl CreateAnnotation(AnnotationClass annClass, TimeSpan time, CanvasLabel canvasLabel)
|
public AnnotationControl CreateAnnotation(AnnotationClass annClass, TimeSpan? time, CanvasLabel canvasLabel)
|
||||||
{
|
{
|
||||||
var annotationControl = new AnnotationControl(annClass, time, AnnotationResizeStart)
|
var annotationControl = new AnnotationControl(annClass, time, AnnotationResizeStart)
|
||||||
{
|
{
|
||||||
@@ -354,7 +354,10 @@ public class CanvasEditor : Canvas
|
|||||||
|
|
||||||
public void ClearExpiredAnnotations(TimeSpan time)
|
public void ClearExpiredAnnotations(TimeSpan time)
|
||||||
{
|
{
|
||||||
var expiredAnns = CurrentAnns.Where(x => Math.Abs((time - x.Time).TotalMilliseconds) > _viewThreshold.TotalMilliseconds).ToList();
|
var expiredAnns = CurrentAnns.Where(x =>
|
||||||
|
x.Time.HasValue &&
|
||||||
|
Math.Abs((time - x.Time.Value).TotalMilliseconds) > _viewThreshold.TotalMilliseconds)
|
||||||
|
.ToList();
|
||||||
RemoveAnnotations(expiredAnns);
|
RemoveAnnotations(expiredAnns);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -24,19 +24,18 @@ public class FormState
|
|||||||
public TimeSpan? GetTime(string name)
|
public TimeSpan? GetTime(string name)
|
||||||
{
|
{
|
||||||
var timeStr = name.Split("_").LastOrDefault();
|
var timeStr = name.Split("_").LastOrDefault();
|
||||||
if (string.IsNullOrEmpty(timeStr))
|
if (string.IsNullOrEmpty(timeStr) || timeStr.Length < 7)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//For some reason, TimeSpan.ParseExact doesn't work on every platform.
|
//For some reason, TimeSpan.ParseExact doesn't work on every platform.
|
||||||
return new TimeSpan(
|
if (!int.TryParse(timeStr[0..1], out var hours))
|
||||||
days: 0,
|
return null;
|
||||||
hours: int.Parse(timeStr[0..1]),
|
if (!int.TryParse(timeStr[1..3], out var minutes))
|
||||||
minutes: int.Parse(timeStr[1..3]),
|
return null;
|
||||||
seconds: int.Parse(timeStr[3..5]),
|
if (!int.TryParse(timeStr[3..5], out var seconds))
|
||||||
milliseconds: int.Parse(timeStr[5..6]) * 100);
|
return null;
|
||||||
}
|
if (!int.TryParse(timeStr[5..6], out var milliseconds))
|
||||||
catch (Exception e) { return null; }
|
return null;
|
||||||
|
return new TimeSpan(0, hours, minutes, seconds, milliseconds * 100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,10 +135,8 @@ public class YoloLabel : Label
|
|||||||
|
|
||||||
var strings = s.Replace(',', '.').Split(' ');
|
var strings = s.Replace(',', '.').Split(' ');
|
||||||
if (strings.Length != 5)
|
if (strings.Length != 5)
|
||||||
return null;
|
throw new Exception("Wrong labels format!");
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var res = new YoloLabel
|
var res = new YoloLabel
|
||||||
{
|
{
|
||||||
ClassNumber = int.Parse(strings[0], CultureInfo.InvariantCulture),
|
ClassNumber = int.Parse(strings[0], CultureInfo.InvariantCulture),
|
||||||
@@ -149,17 +147,12 @@ public class YoloLabel : Label
|
|||||||
};
|
};
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<List<YoloLabel>> ReadFromFile(string filename)
|
public static async Task<List<YoloLabel>> ReadFromFile(string filename)
|
||||||
{
|
{
|
||||||
var str = await File.ReadAllTextAsync(filename);
|
var str = await File.ReadAllTextAsync(filename);
|
||||||
|
|
||||||
return str.Split(Environment.NewLine)
|
return str.Split('\n')
|
||||||
.Select(Parse)
|
.Select(Parse)
|
||||||
.Where(ann => ann != null)
|
.Where(ann => ann != null)
|
||||||
.ToList()!;
|
.ToList()!;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.IO;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
using Azaion.Annotator.Extensions;
|
using Azaion.Annotator.Extensions;
|
||||||
@@ -26,6 +27,7 @@ public class ThumbnailDto : INotifyPropertyChanged
|
|||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public string ImageName => Path.GetFileName(ImagePath);
|
||||||
|
|
||||||
public void UpdateImage() => _image = null;
|
public void UpdateImage() => _image = null;
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,22 @@
|
|||||||
|
|
||||||
<Window.Resources>
|
<Window.Resources>
|
||||||
<DataTemplate x:Key="ThumbnailTemplate" DataType="{x:Type dto:ThumbnailDto}">
|
<DataTemplate x:Key="ThumbnailTemplate" DataType="{x:Type dto:ThumbnailDto}">
|
||||||
<Image Source="{Binding Image}" Width="480" Height="270" Margin="5" />
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*"></RowDefinition>
|
||||||
|
<RowDefinition Height="32"></RowDefinition>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Image
|
||||||
|
Grid.Row="0"
|
||||||
|
Source="{Binding Image}"
|
||||||
|
Width="480"
|
||||||
|
Height="270"
|
||||||
|
Margin="2" />
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="1"
|
||||||
|
Foreground="LightGray"
|
||||||
|
Text="{Binding ImageName}" />
|
||||||
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</Window.Resources>
|
</Window.Resources>
|
||||||
|
|
||||||
@@ -78,6 +93,7 @@
|
|||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
@@ -87,10 +103,10 @@
|
|||||||
</ItemsPanelTemplate>
|
</ItemsPanelTemplate>
|
||||||
</StatusBar.ItemsPanel>
|
</StatusBar.ItemsPanel>
|
||||||
<StatusBarItem Grid.Column="0" Background="Black">
|
<StatusBarItem Grid.Column="0" Background="Black">
|
||||||
<TextBlock>Loading:</TextBlock>
|
<TextBlock Name="LoadingAnnsCaption">Завантаження:</TextBlock>
|
||||||
</StatusBarItem>
|
</StatusBarItem>
|
||||||
<StatusBarItem Grid.Column="1" Background="Black">
|
<StatusBarItem Grid.Column="1" Background="Black">
|
||||||
<controls:UpdatableProgressBar x:Name="Volume"
|
<ProgressBar x:Name="LoadingAnnsBar"
|
||||||
Width="150"
|
Width="150"
|
||||||
Height="15"
|
Height="15"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
@@ -98,12 +114,29 @@
|
|||||||
BorderBrush="#252525"
|
BorderBrush="#252525"
|
||||||
Foreground="LightBlue"
|
Foreground="LightBlue"
|
||||||
Maximum="100"
|
Maximum="100"
|
||||||
Minimum="0">
|
Minimum="0"
|
||||||
</controls:UpdatableProgressBar>
|
Value="0">
|
||||||
|
</ProgressBar>
|
||||||
|
</StatusBarItem>
|
||||||
|
<StatusBarItem Grid.Column="2" Background="Black">
|
||||||
|
<TextBlock Name="RefreshThumbCaption">База іконок:</TextBlock>
|
||||||
</StatusBarItem>
|
</StatusBarItem>
|
||||||
<Separator Grid.Column="2"/>
|
|
||||||
<StatusBarItem Grid.Column="3" Background="Black">
|
<StatusBarItem Grid.Column="3" Background="Black">
|
||||||
<TextBlock Name="StatusText" Text="Loading..."/>
|
<ProgressBar x:Name="RefreshThumbBar"
|
||||||
|
Width="150"
|
||||||
|
Height="15"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Background="#252525"
|
||||||
|
BorderBrush="#252525"
|
||||||
|
Foreground="LightBlue"
|
||||||
|
Maximum="100"
|
||||||
|
Minimum="0"
|
||||||
|
Value="0">
|
||||||
|
</ProgressBar>
|
||||||
|
</StatusBarItem>
|
||||||
|
<Separator Grid.Column="4"/>
|
||||||
|
<StatusBarItem Grid.Column="5" Background="Black">
|
||||||
|
<TextBlock Name="StatusText" Text=""/>
|
||||||
</StatusBarItem>
|
</StatusBarItem>
|
||||||
</StatusBar>
|
</StatusBar>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Media.Imaging;
|
|
||||||
using Azaion.Annotator.DTO;
|
using Azaion.Annotator.DTO;
|
||||||
using Azaion.Annotator.Extensions;
|
using Azaion.Annotator.Extensions;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@@ -35,7 +33,8 @@ public partial class DatasetExplorer
|
|||||||
Config config,
|
Config config,
|
||||||
ILogger<DatasetExplorer> logger,
|
ILogger<DatasetExplorer> logger,
|
||||||
IConfigRepository configRepository,
|
IConfigRepository configRepository,
|
||||||
FormState formState)
|
FormState formState,
|
||||||
|
IGalleryManager galleryManager)
|
||||||
{
|
{
|
||||||
_config = config;
|
_config = config;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -93,6 +92,8 @@ public partial class DatasetExplorer
|
|||||||
SizeChanged += async (_, _) => await SaveUserSettings();
|
SizeChanged += async (_, _) => await SaveUserSettings();
|
||||||
LocationChanged += async (_, _) => await SaveUserSettings();
|
LocationChanged += async (_, _) => await SaveUserSettings();
|
||||||
StateChanged += async (_, _) => await SaveUserSettings();
|
StateChanged += async (_, _) => await SaveUserSettings();
|
||||||
|
|
||||||
|
RefreshThumbBar.Value = galleryManager.ThumbnailsPercentage;
|
||||||
};
|
};
|
||||||
|
|
||||||
Closing += (sender, args) =>
|
Closing += (sender, args) =>
|
||||||
@@ -139,7 +140,12 @@ public partial class DatasetExplorer
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ExplorerEditor.GetTimeFunc = () => _formState.GetTime(CurrentThumbnail!.ImagePath)!.Value;
|
ExplorerEditor.GetTimeFunc = () => _formState.GetTime(CurrentThumbnail!.ImagePath);
|
||||||
|
galleryManager.ThumbnailsUpdate += thumbnailsPercentage =>
|
||||||
|
{
|
||||||
|
Dispatcher.Invoke(() => RefreshThumbBar.Value = thumbnailsPercentage);
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task EditAnnotation()
|
private async Task EditAnnotation()
|
||||||
@@ -161,7 +167,7 @@ public partial class DatasetExplorer
|
|||||||
Switcher.SelectedIndex = 1;
|
Switcher.SelectedIndex = 1;
|
||||||
LvClasses.SelectedIndex = 1;
|
LvClasses.SelectedIndex = 1;
|
||||||
|
|
||||||
var time = _formState.GetTime(dto.ImagePath)!.Value;
|
var time = _formState.GetTime(dto.ImagePath);
|
||||||
ExplorerEditor.RemoveAllAnns();
|
ExplorerEditor.RemoveAllAnns();
|
||||||
foreach (var ann in await YoloLabel.ReadFromFile(dto.LabelPath))
|
foreach (var ann in await YoloLabel.ReadFromFile(dto.LabelPath))
|
||||||
{
|
{
|
||||||
@@ -215,6 +221,9 @@ public partial class DatasetExplorer
|
|||||||
|
|
||||||
private async Task ReloadThumbnails()
|
private async Task ReloadThumbnails()
|
||||||
{
|
{
|
||||||
|
LoadingAnnsCaption.Visibility = Visibility.Visible;
|
||||||
|
LoadingAnnsBar.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
if (!Directory.Exists(_config.ThumbnailsDirectory))
|
if (!Directory.Exists(_config.ThumbnailsDirectory))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -227,13 +236,21 @@ public partial class DatasetExplorer
|
|||||||
await AddThumbnail(thumbnail);
|
await AddThumbnail(thumbnail);
|
||||||
|
|
||||||
if (thumbNum % 1000 == 0)
|
if (thumbNum % 1000 == 0)
|
||||||
|
{
|
||||||
await File.WriteAllTextAsync(_thumbnailsCacheFile, JsonConvert.SerializeObject(LabelsCache));
|
await File.WriteAllTextAsync(_thumbnailsCacheFile, JsonConvert.SerializeObject(LabelsCache));
|
||||||
|
LoadingAnnsBar.Value = thumbNum * 100.0 / thumbnails.Length;
|
||||||
|
}
|
||||||
thumbNum++;
|
thumbNum++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LoadingAnnsCaption.Visibility = Visibility.Collapsed;
|
||||||
|
LoadingAnnsBar.Visibility = Visibility.Collapsed;
|
||||||
await File.WriteAllTextAsync(_thumbnailsCacheFile, JsonConvert.SerializeObject(LabelsCache));
|
await File.WriteAllTextAsync(_thumbnailsCacheFile, JsonConvert.SerializeObject(LabelsCache));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task AddThumbnail(string thumbnail)
|
private async Task AddThumbnail(string thumbnail)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var name = Path.GetFileNameWithoutExtension(thumbnail)[..^Config.ThumbnailPrefix.Length];
|
var name = Path.GetFileNameWithoutExtension(thumbnail)[..^Config.ThumbnailPrefix.Length];
|
||||||
var imageName = Path.Combine(_config.ImagesDirectory, name);
|
var imageName = Path.Combine(_config.ImagesDirectory, name);
|
||||||
@@ -253,7 +270,17 @@ public partial class DatasetExplorer
|
|||||||
{
|
{
|
||||||
if (!File.Exists(labelPath))
|
if (!File.Exists(labelPath))
|
||||||
{
|
{
|
||||||
_logger.LogError($"No label {labelPath} found ! Image {(!File.Exists(imageName) ? "not exists!" : "exists.")}");
|
var imageExists = File.Exists(imageName);
|
||||||
|
if (!imageExists)
|
||||||
|
{
|
||||||
|
_logger.LogError($"No label {labelPath} found ! Image {imageName} not found, removing thumbnail {thumbnail}");
|
||||||
|
File.Delete(thumbnail);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogError($"No label {labelPath} found! But Image {imageName} exists! Image moved to {_config.UnknownImages} directory!");
|
||||||
|
File.Move(imageName, Path.Combine(_config.UnknownImages, imageName));
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,6 +298,10 @@ public partial class DatasetExplorer
|
|||||||
LabelPath = labelPath
|
LabelPath = labelPath
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.LogError(e, e.Message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,10 +9,15 @@ using Size = System.Windows.Size;
|
|||||||
|
|
||||||
namespace Azaion.Annotator;
|
namespace Azaion.Annotator;
|
||||||
|
|
||||||
|
public delegate void ThumbnailsUpdatedEventHandler(double thumbnailsPercentage);
|
||||||
|
|
||||||
public class GalleryManager(Config config, ILogger<GalleryManager> logger) : IGalleryManager
|
public class GalleryManager(Config config, ILogger<GalleryManager> logger) : IGalleryManager
|
||||||
{
|
{
|
||||||
public int ThumbnailsCount { get; set; }
|
public event ThumbnailsUpdatedEventHandler ThumbnailsUpdate;
|
||||||
public int ImagesCount { get; set; }
|
private const int UPDATE_STEP = 20;
|
||||||
|
private readonly SemaphoreSlim _updateLock = new(1);
|
||||||
|
|
||||||
|
public double ThumbnailsPercentage { get; set; }
|
||||||
|
|
||||||
private DirectoryInfo? _thumbnailsDirectory;
|
private DirectoryInfo? _thumbnailsDirectory;
|
||||||
private DirectoryInfo ThumbnailsDirectory
|
private DirectoryInfo ThumbnailsDirectory
|
||||||
@@ -30,7 +35,16 @@ public class GalleryManager(Config config, ILogger<GalleryManager> logger) : IGa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ClearThumbnails()
|
||||||
|
{
|
||||||
|
foreach(var file in new DirectoryInfo(config.ThumbnailsDirectory).GetFiles())
|
||||||
|
file.Delete();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task RefreshThumbnails()
|
public async Task RefreshThumbnails()
|
||||||
|
{
|
||||||
|
await _updateLock.WaitAsync();
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var prefixLen = Config.ThumbnailPrefix.Length;
|
var prefixLen = Config.ThumbnailPrefix.Length;
|
||||||
|
|
||||||
@@ -39,26 +53,36 @@ public class GalleryManager(Config config, ILogger<GalleryManager> logger) : IGa
|
|||||||
.GroupBy(x => x)
|
.GroupBy(x => x)
|
||||||
.Select(gr => gr.Key)
|
.Select(gr => gr.Key)
|
||||||
.ToHashSet();
|
.ToHashSet();
|
||||||
ThumbnailsCount = thumbnails.Count;
|
var thumbnailsCount = thumbnails.Count;
|
||||||
|
|
||||||
var files = new DirectoryInfo(config.ImagesDirectory).GetFiles();
|
var files = new DirectoryInfo(config.ImagesDirectory).GetFiles();
|
||||||
ImagesCount = files.Length;
|
var imagesCount = files.Length;
|
||||||
|
|
||||||
foreach (var img in files)
|
for (int i = 0; i < files.Length; i++)
|
||||||
{
|
{
|
||||||
|
var img = files[i];
|
||||||
|
ThumbnailsPercentage = imagesCount == 0 ? 0 : Math.Min(100, i * 100 / (double)imagesCount);
|
||||||
var imgName = Path.GetFileNameWithoutExtension(img.Name);
|
var imgName = Path.GetFileNameWithoutExtension(img.Name);
|
||||||
|
if (i % UPDATE_STEP == 0)
|
||||||
|
ThumbnailsUpdate?.Invoke(ThumbnailsPercentage);
|
||||||
if (thumbnails.Contains(imgName))
|
if (thumbnails.Contains(imgName))
|
||||||
continue;
|
continue;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await CreateThumbnail(img.FullName);
|
await CreateThumbnail(img.FullName);
|
||||||
|
thumbnailsCount++;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
logger.LogError(e, $"Failed to generate thumbnail for {img.Name}");
|
logger.LogError(e, $"Failed to generate thumbnail for {img.Name}! Error: {e.Message}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ThumbnailsCount++;
|
await Task.Delay(10000);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_updateLock.Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,8 +176,9 @@ public class GalleryManager(Config config, ILogger<GalleryManager> logger) : IGa
|
|||||||
|
|
||||||
public interface IGalleryManager
|
public interface IGalleryManager
|
||||||
{
|
{
|
||||||
int ThumbnailsCount { get; set; }
|
event ThumbnailsUpdatedEventHandler ThumbnailsUpdate;
|
||||||
int ImagesCount { get; set; }
|
double ThumbnailsPercentage { get; set; }
|
||||||
Task CreateThumbnail(string imgPath);
|
Task CreateThumbnail(string imgPath);
|
||||||
Task RefreshThumbnails();
|
Task RefreshThumbnails();
|
||||||
|
void ClearThumbnails();
|
||||||
}
|
}
|
||||||
@@ -79,6 +79,9 @@
|
|||||||
<MenuItem x:Name="OpenDataExplorerItem"
|
<MenuItem x:Name="OpenDataExplorerItem"
|
||||||
Foreground="Black"
|
Foreground="Black"
|
||||||
IsEnabled="True" Header="Відкрити браузер анотацій..." Click="OpenDataExplorerItemClick"/>
|
IsEnabled="True" Header="Відкрити браузер анотацій..." Click="OpenDataExplorerItemClick"/>
|
||||||
|
<MenuItem x:Name="ReloadThumbnailsItem"
|
||||||
|
Foreground="Black"
|
||||||
|
IsEnabled="True" Header="Оновити базу іконок" Click="ReloadThumbnailsItemClick"/>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem Header="Допомога" Foreground="#FFBDBCBC" Margin="0,3,0,0">
|
<MenuItem Header="Допомога" Foreground="#FFBDBCBC" Margin="0,3,0,0">
|
||||||
<MenuItem x:Name="OpenHelpWindow"
|
<MenuItem x:Name="OpenHelpWindow"
|
||||||
|
|||||||
@@ -180,16 +180,16 @@ public partial class MainWindow
|
|||||||
};
|
};
|
||||||
|
|
||||||
_mediaPlayer.PositionChanged += (o, args) =>
|
_mediaPlayer.PositionChanged += (o, args) =>
|
||||||
{
|
|
||||||
ShowTimeAnnotations(TimeSpan.FromMilliseconds(_mediaPlayer.Time));
|
ShowTimeAnnotations(TimeSpan.FromMilliseconds(_mediaPlayer.Time));
|
||||||
};
|
|
||||||
|
|
||||||
VideoSlider.ValueChanged += (value, newValue) =>
|
VideoSlider.ValueChanged += (value, newValue) =>
|
||||||
_mediaPlayer.Position = (float)(newValue / VideoSlider.Maximum);
|
_mediaPlayer.Position = (float)(newValue / VideoSlider.Maximum);
|
||||||
|
|
||||||
VideoSlider.KeyDown += (sender, args) => _mediator.Publish(new KeyEvent(sender, args));
|
VideoSlider.KeyDown += (sender, args) =>
|
||||||
|
_mediator.Publish(new KeyEvent(sender, args));
|
||||||
|
|
||||||
Volume.ValueChanged += (_, newValue) => _mediator.Publish(new VolumeChangedEvent((int)newValue));
|
Volume.ValueChanged += (_, newValue) =>
|
||||||
|
_mediator.Publish(new VolumeChangedEvent((int)newValue));
|
||||||
|
|
||||||
SizeChanged += async (_, _) => await SaveUserSettings();
|
SizeChanged += async (_, _) => await SaveUserSettings();
|
||||||
LocationChanged += async (_, _) => await SaveUserSettings();
|
LocationChanged += async (_, _) => await SaveUserSettings();
|
||||||
@@ -401,4 +401,14 @@ public partial class MainWindow
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void Thumb_OnDragCompleted(object sender, DragCompletedEventArgs e) => _ = SaveUserSettings();
|
private void Thumb_OnDragCompleted(object sender, DragCompletedEventArgs e) => _ = SaveUserSettings();
|
||||||
|
|
||||||
|
private void ReloadThumbnailsItemClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var result = MessageBox.Show($"Видалити всі іконки і згенерувати нову базу іконок в {_config.ThumbnailsDirectory}?",
|
||||||
|
"Підтвердження оновлення іконок", MessageBoxButton.YesNo, MessageBoxImage.Question);
|
||||||
|
if (result != MessageBoxResult.Yes)
|
||||||
|
return;
|
||||||
|
_galleryManager.ClearThumbnails();
|
||||||
|
_galleryManager.RefreshThumbnails();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user