WPF progress

This commit is contained in:
Tiago Conceição
2020-09-23 18:57:23 +01:00
parent c02a4781b7
commit b8683b02d4
8 changed files with 173 additions and 101 deletions
+1 -1
View File
@@ -1878,7 +1878,7 @@ namespace UVtools.GUI
if (SlicerFile.LayerCount == 0)
{
MessageBox.Show("It seems this file has no layers. Possilbe causes could be:\n" +
MessageBox.Show("It seems this file has no layers. Possible causes could be:\n" +
"- File is empty\n" +
"- File is corrupted\n" +
"- File has not been sliced\n" +
+53 -1
View File
@@ -5,12 +5,64 @@
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Avalonia.Controls;
namespace UVtools.WPF.Controls
{
public class WindowEx : Window
public class WindowEx : Window, INotifyPropertyChanged
{
#region BindableBase
/// <summary>
/// Multicast event for property change notifications.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Checks if a property already matches a desired value. Sets the property and
/// notifies listeners only when necessary.
/// </summary>
/// <typeparam name="T">Type of the property.</typeparam>
/// <param name="storage">Reference to a property with both getter and setter.</param>
/// <param name="value">Desired value for the property.</param>
/// <param name="propertyName">
/// Name of the property used to notify listeners. This
/// value is optional and can be provided automatically when invoked from compilers that
/// support CallerMemberName.
/// </param>
/// <returns>
/// True if the value was changed, false if the existing value matched the
/// desired value.
/// </returns>
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Equals(storage, value))
{
return false;
}
storage = value;
OnPropertyChanged(propertyName);
return true;
}
/// <summary>
/// Notifies listeners that a property value has changed.
/// </summary>
/// <param name="propertyName">
/// Name of the property used to notify listeners. This
/// value is optional and can be provided automatically when invoked from compilers
/// that support <see cref="CallerMemberNameAttribute" />.
/// </param>
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var eventHandler = PropertyChanged;
eventHandler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
public DialogResults DialogResult { get; set; } = DialogResults.Unknown;
public enum DialogResults
{
+1
View File
@@ -29,6 +29,7 @@ namespace UVtools.WPF
get => _layer;
set
{
//if (ReferenceEquals(_layer, value)) return;
Clear();
_layer = value;
_image = _layer.LayerMat;
+9 -2
View File
@@ -157,6 +157,7 @@
</Border>
<TabControl
Background="WhiteSmoke"
DockPanel.Dock="Left"
Width="400"
SelectedIndex="{Binding TabSelectedIndex}">
@@ -170,7 +171,7 @@
</StackPanel>
</TabItem.Header>
<Grid Background="WhiteSmoke" IsVisible="{Binding IsFileLoaded}" RowDefinitions="Auto,Auto,Auto,*">
<Grid IsVisible="{Binding IsFileLoaded}" RowDefinitions="Auto,Auto,Auto,*">
<!-- Thumbnails -->
<StackPanel
IsVisible="{Binding SlicerFile.CreatedThumbnailsCount}"
@@ -402,7 +403,9 @@
</TabItem>
<!--
Issues Tab
-->
<TabItem
ToolTip.Tip="Issues"
IsEnabled="{Binding IsFileLoaded}" >
@@ -412,6 +415,10 @@
<!--<TextBlock Margin="5,0,0,0">Issues</TextBlock>!-->
</StackPanel>
</TabItem.Header>
<Grid RowDefinitions="Auto,*">
</Grid>
</TabItem>
+76 -80
View File
@@ -36,57 +36,8 @@ using Helpers = UVtools.WPF.Controls.Helpers;
namespace UVtools.WPF
{
public class MainWindow : WindowEx, INotifyPropertyChanged
public class MainWindow : WindowEx
{
#region BindableBase
/// <summary>
/// Multicast event for property change notifications.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Checks if a property already matches a desired value. Sets the property and
/// notifies listeners only when necessary.
/// </summary>
/// <typeparam name="T">Type of the property.</typeparam>
/// <param name="storage">Reference to a property with both getter and setter.</param>
/// <param name="value">Desired value for the property.</param>
/// <param name="propertyName">
/// Name of the property used to notify listeners. This
/// value is optional and can be provided automatically when invoked from compilers that
/// support CallerMemberName.
/// </param>
/// <returns>
/// True if the value was changed, false if the existing value matched the
/// desired value.
/// </returns>
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Equals(storage, value))
{
return false;
}
storage = value;
OnPropertyChanged(propertyName);
return true;
}
/// <summary>
/// Notifies listeners that a property value has changed.
/// </summary>
/// <param name="propertyName">
/// Name of the property used to notify listeners. This
/// value is optional and can be provided automatically when invoked from compilers
/// that support <see cref="CallerMemberNameAttribute" />.
/// </param>
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var eventHandler = PropertyChanged;
eventHandler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
#region Properties
#region Redirects
public UserSettings Settings => UserSettings.Instance;
@@ -184,18 +135,24 @@ namespace UVtools.WPF
get => _visibleThumbnailIndex;
set
{
SetProperty(ref _visibleThumbnailIndex, value);
OnPropertyChanged(nameof(ThumbnailCanGoPrevious));
OnPropertyChanged(nameof(ThumbnailCanGoNext));
if (value == 0)
{
SetProperty(ref _visibleThumbnailIndex, value);
OnPropertyChanged(nameof(ThumbnailCanGoPrevious));
OnPropertyChanged(nameof(ThumbnailCanGoNext));
VisibleThumbnailImage = null;
return;
}
if (SlicerFile is null) return;
if (_visibleThumbnailIndex > SlicerFile.Thumbnails.Length) return;
VisibleThumbnailImage = SlicerFile.Thumbnails[_visibleThumbnailIndex-1].ToBitmap();
if (!IsFileLoaded) return;
var index = value-1;
if (index >= SlicerFile.CreatedThumbnailsCount) return;
if (SlicerFile.Thumbnails[index] is null) return;
if (!SetProperty(ref _visibleThumbnailIndex, value)) return;
VisibleThumbnailImage = SlicerFile.Thumbnails[index].ToBitmap();
OnPropertyChanged(nameof(ThumbnailCanGoPrevious));
OnPropertyChanged(nameof(ThumbnailCanGoNext));
}
}
@@ -434,9 +391,9 @@ namespace UVtools.WPF
public void AddLogVerbose(string description, double elapsedTime = 0)
{
Debug.WriteLine($"{description} ({elapsedTime}s)");
if (!_isVerbose) return;
AddLog(description, elapsedTime);
Debug.WriteLine(description);
}
#endregion
@@ -586,16 +543,28 @@ namespace UVtools.WPF
set
{
if (!SetProperty(ref _actualLayer, value)) return;
OnPropertyChanged(nameof(CanGoDown));
OnPropertyChanged(nameof(CanGoUp));
OnPropertyChanged(nameof(ActualLayerTooltip));
OnPropertyChanged(nameof(LayerNavigationTooltipMargin));
OnPropertyChanged(nameof(LayerPixelCountStr));
OnPropertyChanged(nameof(LayerBoundsStr));
InvalidateLayerNavigation();
ShowLayer();
}
}
public void ForceUpdateActualLayer(uint layerIndex = 0)
{
_actualLayer = layerIndex;
InvalidateLayerNavigation();
ShowLayer();
}
public void InvalidateLayerNavigation()
{
OnPropertyChanged(nameof(CanGoDown));
OnPropertyChanged(nameof(CanGoUp));
OnPropertyChanged(nameof(ActualLayerTooltip));
OnPropertyChanged(nameof(LayerNavigationTooltipMargin));
OnPropertyChanged(nameof(LayerPixelCountStr));
OnPropertyChanged(nameof(LayerBoundsStr));
}
public Thickness LayerNavigationTooltipMargin
{
get
@@ -708,6 +677,7 @@ namespace UVtools.WPF
if (SlicerFile is null) return;
SlicerFile?.Dispose();
App.SlicerFile = null;
_actualLayer = 0;
LayerCache.Clear();
VisibleThumbnailIndex = 0;
LayerImageBox.Image = null;
@@ -771,38 +741,55 @@ namespace UVtools.WPF
ProgressWindow.SetTitle($"Opening: {fileNameOnly}");
IsGUIEnabled = false;
var task = Task.Factory.StartNew(() =>
var task = Task.Factory.StartNew(async () =>
{
try
{
SlicerFile.Decode(fileName, ProgressWindow.RestartProgress());
return true;
}
catch (OperationCanceledException)
{
SlicerFile.Clear();
}
catch (Exception exception)
{
await this.MessageBoxError(exception.ToString(), "Error opening the file");
}
finally
{
IsGUIEnabled = true;
}
return false;
});
//ProgressWindow progressWindow = new ProgressWindow();
//progressWindow.ShowDialog(this);
await ProgressWindow.ShowDialog(this);
await ProgressWindow.ShowDialog<DialogResults>(this);
if (!task.Result.Result)
{
SlicerFile.Dispose();
App.SlicerFile = null;
return;
}
/*var mat = App.SlicerFile[0].LayerMat;
var matRgb = App.SlicerFile[0].BrgMat;
if (SlicerFile.LayerCount == 0)
{
await this.MessageBoxError("It seems this file has no layers. Possible causes could be:\n" +
"- File is empty\n" +
"- File is corrupted\n" +
"- File has not been sliced\n" +
"- An internal programing error\n\n" +
"Please check your file and retry", "Error reading file");
SlicerFile.Dispose();
App.SlicerFile = null;
return;
}
Debug.WriteLine("4K grayscale - BGRA convertion:");
var bitmap = mat.ToBitmap();
Debug.WriteLine("4K BGR - BGRA convertion:");
var bitmapRgb = matRgb.ToBitmap();
LayerImageBox.Image = bitmapRgb;*/
using Mat mat = SlicerFile[0].LayerMat;
_actualLayer = actualLayer;
if(SlicerFile.CreatedThumbnailsCount > 0) VisibleThumbnailIndex = 1;
VisibleThumbnailIndex = 1;
if (!(SlicerFile.Configs is null))
{
@@ -815,13 +802,24 @@ namespace UVtools.WPF
}
}
}
UpdateTitle();
ShowLayer();
_actualLayer = actualLayer.Clamp(actualLayer, SliderMaximumValue);
if (!(mat is null) && Settings.LayerPreview.AutoRotateLayerBestView)
{
_showLayerImageRotated = mat.Height > mat.Width;
}
ResetDataContext();
InvalidateVisual();
ShowLayer();
if (Settings.LayerPreview.ZoomToFitPrintVolumeBounds)
{
ZoomToFit();
}
}
public void GoFirstLayer()
@@ -863,8 +861,6 @@ namespace UVtools.WPF
unsafe void ShowLayer()
{
if (SlicerFile is null) return;
Debug.WriteLine($"Showing layer: {_actualLayer}");
Stopwatch watch = Stopwatch.StartNew();
LayerCache.Layer = SlicerFile[_actualLayer];
+3 -3
View File
@@ -20,10 +20,10 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="0.10.0-preview5" />
<PackageReference Include="Avalonia" Version="0.10.999-cibuild0010903-beta" />
<PackageReference Include="Avalonia.Angle.Windows.Natives" Version="2.1.0.2020091801" />
<PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.0-preview5" />
<PackageReference Include="Avalonia.Desktop" Version="0.10.0-preview5" />
<PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.999-cibuild0010903-beta" />
<PackageReference Include="Avalonia.Desktop" Version="0.10.999-cibuild0010903-beta" />
<PackageReference Include="Avalonia.ThemeManager" Version="0.10.0-preview3" />
<PackageReference Include="Dock.Avalonia" Version="0.10.0-preview5" />
<PackageReference Include="Emgu.CV.runtime.raspbian" Version="4.4.0.4061" />
+6 -6
View File
@@ -14,18 +14,18 @@
Icon="/Assets/Icons/UVtools.ico"
>
<StackPanel Orientation="Vertical">
<TextBlock Margin="10" Text="{Binding Title}"/>
<TextBlock Margin="10,0,10,10" Text="{Binding ElapsedTimeStr, StringFormat=Elapsed Time: \{0\}}"/>
<TextBlock Margin="10,0,10,10" Text="{Binding Description}" HorizontalAlignment="Center"/>
<TextBlock Margin="10" Text="{Binding Progress.Title}"/>
<TextBlock Margin="10,0,10,10" Text="{Binding Progress.ElapsedTimeStr, StringFormat=Elapsed Time: \{0\}}"/>
<TextBlock Margin="10,0,10,10" Text="{Binding Progress.Description}" HorizontalAlignment="Center"/>
<Grid RowDefinitions="30" ColumnDefinitions="*,Auto">
<ProgressBar
Minimum="0"
Maximum="100"
IsIndeterminate="{Binding IsIndeterminate}"
Value="{Binding ProgressPercent}" Grid.Column="0" ShowProgressText="True"/>
IsIndeterminate="{Binding Progress.IsIndeterminate}"
Value="{Binding Progress.ProgressPercent}" Grid.Column="0" ShowProgressText="True"/>
<Button
IsEnabled="{Binding CanCancel}"
Command="{Binding TokenSource.Cancel}"
Command="{Binding OnClickCancel}"
Grid.Column="1"
Padding="30,0"
IsCancel="True">Cancel</Button>
+24 -8
View File
@@ -9,34 +9,43 @@
using System;
using System.Diagnostics;
using System.Timers;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Threading;
using UVtools.Core.Operations;
using UVtools.WPF.Controls;
using UVtools.WPF.Structures;
namespace UVtools.WPF.Windows
{
public class ProgressWindow : Window, IDisposable
public class ProgressWindow : WindowEx, IDisposable
{
public Stopwatch StopWatch => Progress.StopWatch;
public OperationProgress Progress { get; } = new OperationProgress();
private LogItem _logItem = new LogItem();
private Timer _timer;
private Timer _timer = new Timer(100) { AutoReset = true };
public bool CanCancel => Progress?.CanCancel ?? false;
public bool CanCancel
{
get => Progress?.CanCancel ?? false;
set
{
Progress.CanCancel = value;
OnPropertyChanged();
}
}
public ProgressWindow()
{
InitializeComponent();
DataContext = Progress;
_timer = new Timer(100) {AutoReset = true};
_timer.Elapsed += (sender, args) =>
{
Progress.TriggerRefresh();
};
DataContext = this;
}
private void InitializeComponent()
@@ -53,6 +62,14 @@ namespace UVtools.WPF.Windows
App.MainWindow.AddLog(_logItem);
}
public void OnClickCancel()
{
if (!CanCancel) return;
DialogResult = DialogResults.Cancel;
OnPropertyChanged(nameof(CanCancel));
Progress.TokenSource.Cancel();
}
public void SetTitle(string title)
{
Progress.Title = title;
@@ -61,7 +78,7 @@ namespace UVtools.WPF.Windows
public OperationProgress RestartProgress(bool canCancel = true)
{
Progress.CanCancel = canCancel;
CanCancel = canCancel;
Progress.StopWatch.Restart();
if (!_timer.Enabled)
@@ -70,7 +87,6 @@ namespace UVtools.WPF.Windows
_timer.Start();
}
Dispatcher.UIThread.InvokeAsync(() => DataContext = Progress);
return Progress;
}