mirror of
https://github.com/riegera2412/UVtools.git
synced 2026-07-08 17:42:31 +02:00
WPF progress
This commit is contained in:
@@ -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" +
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace UVtools.WPF
|
||||
get => _layer;
|
||||
set
|
||||
{
|
||||
//if (ReferenceEquals(_layer, value)) return;
|
||||
Clear();
|
||||
_layer = value;
|
||||
_image = _layer.LayerMat;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user