mirror of
https://github.com/azaion/annotations.git
synced 2026-04-22 22:56:29 +00:00
add help system
This commit is contained in:
@@ -17,8 +17,8 @@ public class HelpTexts
|
||||
{ HelpTextEnum.Initial, "Натисніть Файл - Відкрити папку... та виберіть папку з вашими відео для анотації" },
|
||||
{ HelpTextEnum.PlayVideo, "В списку відео виберіть потрібне та [подвійний клік] чи [Eнтер] на ньому - запустіть його на перегляд" },
|
||||
{ HelpTextEnum.PauseForAnnotations, "В потрібному місці відео де є один з об'єктів для анотації зупиніть його [Пробіл] або кн. на панелі" },
|
||||
{ HelpTextEnum.AnnotationHelp, "Клавішами [1] - [9] або мишкою оберіть потрібний клас та виділіть область з об'єктом. Виділяйте всі що є об'єкти. " +
|
||||
"При потребі [Ctrl] виділяйте анотації та [Del] для видалення. [Eнтер] для збереження і перегляду далі" },
|
||||
{ HelpTextEnum.AnnotationHelp, "Клавішами [1] - [9] або мишкою оберіть потрібний клас та виділіть, тобто зробіть анотації всіх необхідних об'єктів. " +
|
||||
"Непотрібні анотації можна виділити (через [Ctrl] декілька) та [Del] видалити. [Eнтер] для збереження і перегляду далі" },
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -303,13 +303,30 @@
|
||||
Grid.Row="5"
|
||||
Grid.Column="2"
|
||||
>
|
||||
<StatusBarItem>
|
||||
<TextBlock Margin="3 0 0 0" x:Name="StatusClock" FontSize="16"></TextBlock>
|
||||
<StatusBar.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
</Grid>
|
||||
</ItemsPanelTemplate>
|
||||
</StatusBar.ItemsPanel>
|
||||
<StatusBarItem Grid.Column="0">
|
||||
<TextBlock Margin="3 0 0 0" x:Name="StatusClock" FontSize="16" Text="00:00 / 00:00"></TextBlock>
|
||||
</StatusBarItem>
|
||||
<StatusBarItem>
|
||||
<TextBlock Margin="3 0 0 0" x:Name="StatusHelp" Text="{Binding Path=CurrentHelp}"></TextBlock>
|
||||
<Separator Grid.Column="1" />
|
||||
<StatusBarItem Grid.Column="2">
|
||||
<TextBlock Margin="3 0 0 0" x:Name="StatusHelp" FontSize="14" Foreground="DarkBlue" ></TextBlock>
|
||||
</StatusBarItem>
|
||||
<StatusBarItem>
|
||||
<StatusBarItem Grid.Column="3">
|
||||
<TextBlock x:Name="Status"></TextBlock>
|
||||
</StatusBarItem>
|
||||
</StatusBar>
|
||||
|
||||
@@ -25,8 +25,6 @@ public partial class MainWindow
|
||||
private bool _suspendLayout;
|
||||
|
||||
public Dictionary<string, List<AnnotationInfo>> Annotations { get; set; } = new();
|
||||
|
||||
public string CurrentHelp { get; set; }
|
||||
|
||||
public MainWindow(LibVLC libVLC, MediaPlayer mediaPlayer,
|
||||
IMediator mediator,
|
||||
@@ -63,7 +61,24 @@ public partial class MainWindow
|
||||
AnnotationClasses = new ObservableCollection<AnnotationClass>(_config.AnnotationClasses);
|
||||
LvClasses.ItemsSource = AnnotationClasses;
|
||||
LvClasses.SelectedIndex = 0;
|
||||
CurrentHelp = HelpTexts.HelpTextsDict[HelpTextEnum.Initial];
|
||||
|
||||
if (LvFiles.Items.IsEmpty)
|
||||
BlinkHelp(HelpTexts.HelpTextsDict[HelpTextEnum.Initial]);
|
||||
}
|
||||
|
||||
public void BlinkHelp(string helpText, int times = 3)
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
for (int i = 0; i < times; i++)
|
||||
{
|
||||
Dispatcher.Invoke(() => StatusHelp.Text = helpText);
|
||||
await Task.Delay(200);
|
||||
Dispatcher.Invoke(() => StatusHelp.Text = "");
|
||||
await Task.Delay(200);
|
||||
}
|
||||
Dispatcher.Invoke(() => StatusHelp.Text = helpText);
|
||||
});
|
||||
}
|
||||
|
||||
private void InitControls()
|
||||
@@ -78,11 +93,8 @@ public partial class MainWindow
|
||||
_formState.CurrentVideoLength = TimeSpan.FromMilliseconds(_mediaPlayer.Length);
|
||||
};
|
||||
|
||||
LvFiles.MouseDoubleClick += async (_, _) =>
|
||||
{
|
||||
Play((VideoFileInfo)LvFiles.SelectedItem);
|
||||
};
|
||||
|
||||
LvFiles.MouseDoubleClick += async (_, _) => await _mediator.Publish(new PlaybackControlEvent(PlaybackControlEnum.Play));
|
||||
|
||||
LvClasses.SelectionChanged += (_, _) =>
|
||||
{
|
||||
var selectedClass = (AnnotationClass)LvClasses.SelectedItem;
|
||||
@@ -146,21 +158,8 @@ public partial class MainWindow
|
||||
Editor.FormState = _formState;
|
||||
Editor.Mediator = _mediator;
|
||||
}
|
||||
|
||||
private void Play(VideoFileInfo videoFileInfo)
|
||||
{
|
||||
if (LvFiles.SelectedItem == null)
|
||||
return;
|
||||
var fileInfo = (VideoFileInfo)LvFiles.SelectedItem;
|
||||
|
||||
_formState.CurrentFile = fileInfo.Name;
|
||||
LoadExistingAnnotations();
|
||||
|
||||
_mediaPlayer.Stop();
|
||||
_mediaPlayer.Play(new Media(_libVLC, fileInfo.Path));
|
||||
}
|
||||
|
||||
private void LoadExistingAnnotations()
|
||||
|
||||
public void LoadExistingAnnotations()
|
||||
{
|
||||
var dirInfo = new DirectoryInfo(_config.LabelsDirectory);
|
||||
if (!dirInfo.Exists)
|
||||
@@ -190,10 +189,14 @@ public partial class MainWindow
|
||||
Path = x.FullName,
|
||||
Duration = TimeSpan.FromMilliseconds(_mediaPlayer.Media.Duration)
|
||||
};
|
||||
});
|
||||
}).ToList();
|
||||
|
||||
LvFiles.ItemsSource = new ObservableCollection<VideoFileInfo>(files);
|
||||
TbFolder.Text = _config.VideosDirectory;
|
||||
|
||||
BlinkHelp(files.Count == 0
|
||||
? HelpTexts.HelpTextsDict[HelpTextEnum.Initial]
|
||||
: HelpTexts.HelpTextsDict[HelpTextEnum.PlayVideo]);
|
||||
}
|
||||
|
||||
private void OnFormClosed(object? sender, EventArgs e)
|
||||
|
||||
@@ -6,28 +6,16 @@ using MediatR;
|
||||
|
||||
namespace Azaion.Annotator;
|
||||
|
||||
public class PlayerControlHandler:
|
||||
INotificationHandler<KeyEvent>,
|
||||
INotificationHandler<AnnClassSelectedEvent>,
|
||||
INotificationHandler<PlaybackControlEvent>
|
||||
public class PlayerControlHandler(LibVLC libVLC, MediaPlayer mediaPlayer, MainWindow mainWindow, FormState formState, Config config) :
|
||||
INotificationHandler<KeyEvent>,
|
||||
INotificationHandler<AnnClassSelectedEvent>,
|
||||
INotificationHandler<PlaybackControlEvent>
|
||||
{
|
||||
private const int STEP = 20;
|
||||
private const int LARGE_STEP = 2000;
|
||||
private const int LARGE_STEP = 5000;
|
||||
|
||||
private static readonly string[] CatchSenders = ["ForegroundWindow", "ScrollViewer"];
|
||||
private MediaPlayer mediaPlayer;
|
||||
private readonly MainWindow _mainWindow;
|
||||
private readonly FormState _formState;
|
||||
private readonly Config _config;
|
||||
|
||||
public PlayerControlHandler(MediaPlayer mediaPlayer1, MainWindow mainWindow, FormState formState, Config config)
|
||||
{
|
||||
mediaPlayer = mediaPlayer1;
|
||||
_mainWindow = mainWindow;
|
||||
_formState = formState;
|
||||
_config = config;
|
||||
}
|
||||
|
||||
private readonly Dictionary<Key, PlaybackControlEnum> KeysControlEnumDict = new()
|
||||
{
|
||||
{ Key.Space, PlaybackControlEnum.Pause },
|
||||
@@ -43,10 +31,10 @@ public class PlayerControlHandler:
|
||||
|
||||
private void SelectClass(AnnotationClass annClass)
|
||||
{
|
||||
_mainWindow.Editor.CurrentAnnClass = annClass;
|
||||
foreach (var ann in _mainWindow.Editor.CurrentAnns.Where(x => x.IsSelected))
|
||||
mainWindow.Editor.CurrentAnnClass = annClass;
|
||||
foreach (var ann in mainWindow.Editor.CurrentAnns.Where(x => x.IsSelected))
|
||||
ann.AnnotationClass = annClass;
|
||||
_mainWindow.LvClasses.SelectedIndex = annClass.Id;
|
||||
mainWindow.LvClasses.SelectedIndex = annClass.Id;
|
||||
}
|
||||
|
||||
public async Task Handle(KeyEvent notification, CancellationToken cancellationToken)
|
||||
@@ -62,7 +50,7 @@ public class PlayerControlHandler:
|
||||
if ((int)key >= (int)Key.NumPad1 && (int)key <= (int)Key.NumPad9)
|
||||
keyNumber = key - Key.NumPad1;
|
||||
if (keyNumber.HasValue)
|
||||
SelectClass(_mainWindow.AnnotationClasses[keyNumber.Value]);
|
||||
SelectClass(mainWindow.AnnotationClasses[keyNumber.Value]);
|
||||
|
||||
if (KeysControlEnumDict.TryGetValue(key, out var value))
|
||||
await ControlPlayback(value);
|
||||
@@ -81,10 +69,12 @@ public class PlayerControlHandler:
|
||||
switch (controlEnum)
|
||||
{
|
||||
case PlaybackControlEnum.Play:
|
||||
mediaPlayer.Play();
|
||||
Play();
|
||||
break;
|
||||
case PlaybackControlEnum.Pause:
|
||||
mediaPlayer.Pause();
|
||||
if (!mediaPlayer.IsPlaying)
|
||||
mainWindow.BlinkHelp(HelpTexts.HelpTextsDict[HelpTextEnum.AnnotationHelp]);
|
||||
break;
|
||||
case PlaybackControlEnum.Stop:
|
||||
mediaPlayer.Stop();
|
||||
@@ -101,10 +91,10 @@ public class PlayerControlHandler:
|
||||
await SaveAnnotations();
|
||||
break;
|
||||
case PlaybackControlEnum.RemoveSelectedAnns:
|
||||
_mainWindow.Editor.RemoveSelectedAnns();
|
||||
mainWindow.Editor.RemoveSelectedAnns();
|
||||
break;
|
||||
case PlaybackControlEnum.RemoveAllAnns:
|
||||
_mainWindow.Editor.RemoveAllAnns();
|
||||
mainWindow.Editor.RemoveAllAnns();
|
||||
break;
|
||||
case PlaybackControlEnum.None:
|
||||
break;
|
||||
@@ -113,27 +103,41 @@ public class PlayerControlHandler:
|
||||
}
|
||||
}
|
||||
|
||||
private void Play()
|
||||
{
|
||||
if (mainWindow.LvFiles.SelectedItem == null)
|
||||
return;
|
||||
var fileInfo = (VideoFileInfo)mainWindow.LvFiles.SelectedItem;
|
||||
|
||||
formState.CurrentFile = fileInfo.Name;
|
||||
mainWindow.LoadExistingAnnotations();
|
||||
|
||||
mediaPlayer.Stop();
|
||||
mediaPlayer.Play(new Media(libVLC, fileInfo.Path));
|
||||
mainWindow.BlinkHelp(HelpTexts.HelpTextsDict[HelpTextEnum.PauseForAnnotations]);
|
||||
}
|
||||
|
||||
private async Task SaveAnnotations()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_formState.CurrentFile))
|
||||
if (string.IsNullOrEmpty(formState.CurrentFile))
|
||||
return;
|
||||
|
||||
var fName = _formState.GetTimeName(TimeSpan.FromMilliseconds(mediaPlayer.Time));
|
||||
var currentAnns = _mainWindow.Editor.CurrentAnns
|
||||
.Select(x => x.Info.ToLabelCoordinates(_mainWindow.Editor.RenderSize, _formState.CurrentVideoSize))
|
||||
var fName = formState.GetTimeName(TimeSpan.FromMilliseconds(mediaPlayer.Time));
|
||||
var currentAnns = mainWindow.Editor.CurrentAnns
|
||||
.Select(x => x.Info.ToLabelCoordinates(mainWindow.Editor.RenderSize, formState.CurrentVideoSize))
|
||||
.ToList();
|
||||
var labels = string.Join(Environment.NewLine, currentAnns.Select(x => x.ToString()));
|
||||
|
||||
if (!Directory.Exists(_config.LabelsDirectory))
|
||||
Directory.CreateDirectory(_config.LabelsDirectory);
|
||||
if (!Directory.Exists(_config.ImagesDirectory))
|
||||
Directory.CreateDirectory(_config.ImagesDirectory);
|
||||
if (!Directory.Exists(config.LabelsDirectory))
|
||||
Directory.CreateDirectory(config.LabelsDirectory);
|
||||
if (!Directory.Exists(config.ImagesDirectory))
|
||||
Directory.CreateDirectory(config.ImagesDirectory);
|
||||
|
||||
await File.WriteAllTextAsync($"{_config.LabelsDirectory}/{fName}.txt", labels);
|
||||
mediaPlayer.TakeSnapshot(0, $"{_config.ImagesDirectory}/{fName}.jpg", 0, 0);
|
||||
await File.WriteAllTextAsync($"{config.LabelsDirectory}/{fName}.txt", labels);
|
||||
mediaPlayer.TakeSnapshot(0, $"{config.ImagesDirectory}/{fName}.jpg", 0, 0);
|
||||
|
||||
_mainWindow.Annotations[fName] = currentAnns;
|
||||
_mainWindow.Editor.RemoveAllAnns();
|
||||
mainWindow.Annotations[fName] = currentAnns;
|
||||
mainWindow.Editor.RemoveAllAnns();
|
||||
mediaPlayer.Play();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user