add help system

This commit is contained in:
Oleksandr Bezdieniezhnykh
2024-05-15 19:06:43 +03:00
parent 6809a9cedf
commit 6aba2e31db
4 changed files with 91 additions and 67 deletions
@@ -17,8 +17,8 @@ public class HelpTexts
{ HelpTextEnum.Initial, "Натисніть Файл - Відкрити папку... та виберіть папку з вашими відео для анотації" }, { HelpTextEnum.Initial, "Натисніть Файл - Відкрити папку... та виберіть папку з вашими відео для анотації" },
{ HelpTextEnum.PlayVideo, "В списку відео виберіть потрібне та [подвійний клік] чи [Eнтер] на ньому - запустіть його на перегляд" }, { HelpTextEnum.PlayVideo, "В списку відео виберіть потрібне та [подвійний клік] чи [Eнтер] на ньому - запустіть його на перегляд" },
{ HelpTextEnum.PauseForAnnotations, "В потрібному місці відео де є один з об'єктів для анотації зупиніть його [Пробіл] або кн. на панелі" }, { HelpTextEnum.PauseForAnnotations, "В потрібному місці відео де є один з об'єктів для анотації зупиніть його [Пробіл] або кн. на панелі" },
{ HelpTextEnum.AnnotationHelp, "Клавішами [1] - [9] або мишкою оберіть потрібний клас та виділіть область з об'єктом. Виділяйте всі що є об'єкти. " + { HelpTextEnum.AnnotationHelp, "Клавішами [1] - [9] або мишкою оберіть потрібний клас та виділіть, тобто зробіть анотації всіх необхідних об'єктів. " +
"При потребі [Ctrl] виділяйте анотації та [Del] для видалення. [Eнтер] для збереження і перегляду далі" }, "Непотрібні анотації можна виділити (через [Ctrl] декілька) та [Del] видалити. [Eнтер] для збереження і перегляду далі" },
}; };
} }
@@ -303,13 +303,30 @@
Grid.Row="5" Grid.Row="5"
Grid.Column="2" Grid.Column="2"
> >
<StatusBarItem> <StatusBar.ItemsPanel>
<TextBlock Margin="3 0 0 0" x:Name="StatusClock" FontSize="16"></TextBlock> <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>
<StatusBarItem> <Separator Grid.Column="1" />
<TextBlock Margin="3 0 0 0" x:Name="StatusHelp" Text="{Binding Path=CurrentHelp}"></TextBlock> <StatusBarItem Grid.Column="2">
<TextBlock Margin="3 0 0 0" x:Name="StatusHelp" FontSize="14" Foreground="DarkBlue" ></TextBlock>
</StatusBarItem> </StatusBarItem>
<StatusBarItem> <StatusBarItem Grid.Column="3">
<TextBlock x:Name="Status"></TextBlock> <TextBlock x:Name="Status"></TextBlock>
</StatusBarItem> </StatusBarItem>
</StatusBar> </StatusBar>
@@ -26,8 +26,6 @@ public partial class MainWindow
public Dictionary<string, List<AnnotationInfo>> Annotations { get; set; } = new(); public Dictionary<string, List<AnnotationInfo>> Annotations { get; set; } = new();
public string CurrentHelp { get; set; }
public MainWindow(LibVLC libVLC, MediaPlayer mediaPlayer, public MainWindow(LibVLC libVLC, MediaPlayer mediaPlayer,
IMediator mediator, IMediator mediator,
FormState formState, FormState formState,
@@ -63,7 +61,24 @@ public partial class MainWindow
AnnotationClasses = new ObservableCollection<AnnotationClass>(_config.AnnotationClasses); AnnotationClasses = new ObservableCollection<AnnotationClass>(_config.AnnotationClasses);
LvClasses.ItemsSource = AnnotationClasses; LvClasses.ItemsSource = AnnotationClasses;
LvClasses.SelectedIndex = 0; 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() private void InitControls()
@@ -78,10 +93,7 @@ public partial class MainWindow
_formState.CurrentVideoLength = TimeSpan.FromMilliseconds(_mediaPlayer.Length); _formState.CurrentVideoLength = TimeSpan.FromMilliseconds(_mediaPlayer.Length);
}; };
LvFiles.MouseDoubleClick += async (_, _) => LvFiles.MouseDoubleClick += async (_, _) => await _mediator.Publish(new PlaybackControlEvent(PlaybackControlEnum.Play));
{
Play((VideoFileInfo)LvFiles.SelectedItem);
};
LvClasses.SelectionChanged += (_, _) => LvClasses.SelectionChanged += (_, _) =>
{ {
@@ -147,20 +159,7 @@ public partial class MainWindow
Editor.Mediator = _mediator; Editor.Mediator = _mediator;
} }
private void Play(VideoFileInfo videoFileInfo) public void LoadExistingAnnotations()
{
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()
{ {
var dirInfo = new DirectoryInfo(_config.LabelsDirectory); var dirInfo = new DirectoryInfo(_config.LabelsDirectory);
if (!dirInfo.Exists) if (!dirInfo.Exists)
@@ -190,10 +189,14 @@ public partial class MainWindow
Path = x.FullName, Path = x.FullName,
Duration = TimeSpan.FromMilliseconds(_mediaPlayer.Media.Duration) Duration = TimeSpan.FromMilliseconds(_mediaPlayer.Media.Duration)
}; };
}); }).ToList();
LvFiles.ItemsSource = new ObservableCollection<VideoFileInfo>(files); LvFiles.ItemsSource = new ObservableCollection<VideoFileInfo>(files);
TbFolder.Text = _config.VideosDirectory; TbFolder.Text = _config.VideosDirectory;
BlinkHelp(files.Count == 0
? HelpTexts.HelpTextsDict[HelpTextEnum.Initial]
: HelpTexts.HelpTextsDict[HelpTextEnum.PlayVideo]);
} }
private void OnFormClosed(object? sender, EventArgs e) private void OnFormClosed(object? sender, EventArgs e)
@@ -6,27 +6,15 @@ using MediatR;
namespace Azaion.Annotator; namespace Azaion.Annotator;
public class PlayerControlHandler: public class PlayerControlHandler(LibVLC libVLC, MediaPlayer mediaPlayer, MainWindow mainWindow, FormState formState, Config config) :
INotificationHandler<KeyEvent>, INotificationHandler<KeyEvent>,
INotificationHandler<AnnClassSelectedEvent>, INotificationHandler<AnnClassSelectedEvent>,
INotificationHandler<PlaybackControlEvent> INotificationHandler<PlaybackControlEvent>
{ {
private const int STEP = 20; 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 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() private readonly Dictionary<Key, PlaybackControlEnum> KeysControlEnumDict = new()
{ {
@@ -43,10 +31,10 @@ public class PlayerControlHandler:
private void SelectClass(AnnotationClass annClass) private void SelectClass(AnnotationClass annClass)
{ {
_mainWindow.Editor.CurrentAnnClass = annClass; mainWindow.Editor.CurrentAnnClass = annClass;
foreach (var ann in _mainWindow.Editor.CurrentAnns.Where(x => x.IsSelected)) foreach (var ann in mainWindow.Editor.CurrentAnns.Where(x => x.IsSelected))
ann.AnnotationClass = annClass; ann.AnnotationClass = annClass;
_mainWindow.LvClasses.SelectedIndex = annClass.Id; mainWindow.LvClasses.SelectedIndex = annClass.Id;
} }
public async Task Handle(KeyEvent notification, CancellationToken cancellationToken) 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) if ((int)key >= (int)Key.NumPad1 && (int)key <= (int)Key.NumPad9)
keyNumber = key - Key.NumPad1; keyNumber = key - Key.NumPad1;
if (keyNumber.HasValue) if (keyNumber.HasValue)
SelectClass(_mainWindow.AnnotationClasses[keyNumber.Value]); SelectClass(mainWindow.AnnotationClasses[keyNumber.Value]);
if (KeysControlEnumDict.TryGetValue(key, out var value)) if (KeysControlEnumDict.TryGetValue(key, out var value))
await ControlPlayback(value); await ControlPlayback(value);
@@ -81,10 +69,12 @@ public class PlayerControlHandler:
switch (controlEnum) switch (controlEnum)
{ {
case PlaybackControlEnum.Play: case PlaybackControlEnum.Play:
mediaPlayer.Play(); Play();
break; break;
case PlaybackControlEnum.Pause: case PlaybackControlEnum.Pause:
mediaPlayer.Pause(); mediaPlayer.Pause();
if (!mediaPlayer.IsPlaying)
mainWindow.BlinkHelp(HelpTexts.HelpTextsDict[HelpTextEnum.AnnotationHelp]);
break; break;
case PlaybackControlEnum.Stop: case PlaybackControlEnum.Stop:
mediaPlayer.Stop(); mediaPlayer.Stop();
@@ -101,10 +91,10 @@ public class PlayerControlHandler:
await SaveAnnotations(); await SaveAnnotations();
break; break;
case PlaybackControlEnum.RemoveSelectedAnns: case PlaybackControlEnum.RemoveSelectedAnns:
_mainWindow.Editor.RemoveSelectedAnns(); mainWindow.Editor.RemoveSelectedAnns();
break; break;
case PlaybackControlEnum.RemoveAllAnns: case PlaybackControlEnum.RemoveAllAnns:
_mainWindow.Editor.RemoveAllAnns(); mainWindow.Editor.RemoveAllAnns();
break; break;
case PlaybackControlEnum.None: case PlaybackControlEnum.None:
break; 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() private async Task SaveAnnotations()
{ {
if (string.IsNullOrEmpty(_formState.CurrentFile)) if (string.IsNullOrEmpty(formState.CurrentFile))
return; return;
var fName = _formState.GetTimeName(TimeSpan.FromMilliseconds(mediaPlayer.Time)); var fName = formState.GetTimeName(TimeSpan.FromMilliseconds(mediaPlayer.Time));
var currentAnns = _mainWindow.Editor.CurrentAnns var currentAnns = mainWindow.Editor.CurrentAnns
.Select(x => x.Info.ToLabelCoordinates(_mainWindow.Editor.RenderSize, _formState.CurrentVideoSize)) .Select(x => x.Info.ToLabelCoordinates(mainWindow.Editor.RenderSize, formState.CurrentVideoSize))
.ToList(); .ToList();
var labels = string.Join(Environment.NewLine, currentAnns.Select(x => x.ToString())); var labels = string.Join(Environment.NewLine, currentAnns.Select(x => x.ToString()));
if (!Directory.Exists(_config.LabelsDirectory)) if (!Directory.Exists(config.LabelsDirectory))
Directory.CreateDirectory(_config.LabelsDirectory); Directory.CreateDirectory(config.LabelsDirectory);
if (!Directory.Exists(_config.ImagesDirectory)) if (!Directory.Exists(config.ImagesDirectory))
Directory.CreateDirectory(_config.ImagesDirectory); Directory.CreateDirectory(config.ImagesDirectory);
await File.WriteAllTextAsync($"{_config.LabelsDirectory}/{fName}.txt", labels); await File.WriteAllTextAsync($"{config.LabelsDirectory}/{fName}.txt", labels);
mediaPlayer.TakeSnapshot(0, $"{_config.ImagesDirectory}/{fName}.jpg", 0, 0); mediaPlayer.TakeSnapshot(0, $"{config.ImagesDirectory}/{fName}.jpg", 0, 0);
_mainWindow.Annotations[fName] = currentAnns; mainWindow.Annotations[fName] = currentAnns;
_mainWindow.Editor.RemoveAllAnns(); mainWindow.Editor.RemoveAllAnns();
mediaPlayer.Play(); mediaPlayer.Play();
} }
} }