WPF Progress

This commit is contained in:
Tiago Conceição
2020-10-07 03:20:48 +01:00
parent d9cd0022ca
commit 396313f334
7 changed files with 518 additions and 0 deletions
+19
View File
@@ -0,0 +1,19 @@
using System.IO;
using System.Security.Cryptography;
namespace UVtools.Core.Objects
{
public static class StaticObjects
{
public static SHA256 Sha256 { get; } = SHA256.Create();
// Compute the file's hash.
public static byte[] GetHashSha256(string filename)
{
using (var stream = File.OpenRead(filename))
{
return Sha256.ComputeHash(stream);
}
}
}
}
+5
View File
@@ -162,6 +162,11 @@ namespace UVtools.WPF
public static Bitmap GetBitmapFromAsset(string url) => new Bitmap(GetAsset(url)); public static Bitmap GetBitmapFromAsset(string url) => new Bitmap(GetAsset(url));
public static string GetApplicationPath()
{
return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
}
#endregion #endregion
} }
} }
@@ -5,6 +5,8 @@
* Everyone is permitted to copy and distribute verbatim copies * Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed. * of this license document, but changing it is not allowed.
*/ */
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Threading; using Avalonia.Threading;
@@ -44,6 +46,30 @@ namespace UVtools.WPF.Extensions
=> await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Question", buttons, Icon.Setting, WindowStartupLocation.CenterOwner, style); => await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Question", buttons, Icon.Setting, WindowStartupLocation.CenterOwner, style);
public static void ShowDialogSync(this Window window, Window parent = null)
{
if (parent is null) parent = window;
using (var source = new CancellationTokenSource())
{
window.ShowDialog(parent).ContinueWith(t => source.Cancel(), TaskScheduler.FromCurrentSynchronizationContext());
Dispatcher.UIThread.MainLoop(source.Token);
}
}
public static T ShowDialogSync<T>(this Window window, Window parent = null)
{
if (parent is null) parent = window;
using (var source = new CancellationTokenSource())
{
var task = window.ShowDialog<T>(parent);
task.ContinueWith(t => source.Cancel(), TaskScheduler.FromCurrentSynchronizationContext());
Dispatcher.UIThread.MainLoop(source.Token);
return task.Result;
}
return default(T);
}
public static void ResetDataContext(this Window window) public static void ResetDataContext(this Window window)
{ {
var old = window.DataContext; var old = window.DataContext;
+5
View File
@@ -584,6 +584,11 @@ namespace UVtools.WPF
await new AboutWindow().ShowDialog(this); await new AboutWindow().ShowDialog(this);
} }
public async void MenuHelpInstallProfilesClicked()
{
await new PrusaSlicerManager().ShowDialog(this);
}
#endregion #endregion
#region Methods #region Methods
+132
View File
@@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using Avalonia.Controls;
using Avalonia.Media;
using UVtools.Core.Objects;
namespace UVtools.WPF.Structures
{
public class PEProfileFolder : BindableBase
{
private ObservableCollection<CheckBox> _items = new ObservableCollection<CheckBox>();
private ushort _installed;
private ushort _updates;
public enum FolderType
{
Print,
Printer
}
public FolderType Type { get; }
public string SourcePath { get; }
public string TargetPath { get; }
public ObservableCollection<CheckBox> Items
{
get => _items;
set => RaiseAndSetIfChanged(ref _items, value);
}
public ushort Installed
{
get => _installed;
set => RaiseAndSetIfChanged(ref _installed, value);
}
public ushort Updates
{
get => _updates;
set => RaiseAndSetIfChanged(ref _updates, value);
}
public PEProfileFolder(FolderType type)
{
Type = type;
switch (type)
{
case FolderType.Print:
SourcePath = string.Format("{0}{1}Assets{1}PrusaSlicer{1}sla_print",
App.GetApplicationPath(), Path.DirectorySeparatorChar);
TargetPath = string.Format("{0}{1}PrusaSlicer{1}sla_print",
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
Path.DirectorySeparatorChar);
break;
case FolderType.Printer:
SourcePath = string.Format("{0}{1}Assets{1}PrusaSlicer{1}printer",
App.GetApplicationPath(), Path.DirectorySeparatorChar);
TargetPath = string.Format("{0}{1}PrusaSlicer{1}printer",
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
Path.DirectorySeparatorChar);
break;
}
Reset();
}
public void Reset()
{
Items.Clear();
Updates = 0;
Installed = 0;
if (!Directory.Exists(SourcePath)) return;
DirectoryInfo di = new DirectoryInfo(SourcePath);
var files = di.GetFiles("*.ini");
if (files.Length == 0) return;
bool folderExists = Directory.Exists(TargetPath);
for (int i = 0; i < files.Length; i++)
{
Items.Add(new CheckBox
{
Content = files[i].Name,
Tag = files
});
if (folderExists)
{
var targetFile = $"{TargetPath}{Path.DirectorySeparatorChar}{files[i].Name}";
FileInfo targetFileInfo = new FileInfo(targetFile);
if (targetFileInfo.Exists)
{
Installed++;
if (targetFileInfo.Length != files[i].Length || !StaticObjects.GetHashSha256(targetFileInfo.FullName).SequenceEqual(StaticObjects.GetHashSha256(files[i].FullName)))
{
Items[i].Foreground = Brushes.Red;
Items[i].IsChecked = true;
Updates++;
}
else
{
Items[i].Foreground = Brushes.Green;
Items[i].IsEnabled = false;
}
}
}
}
}
public void SelectNone()
{
foreach (var checkBox in Items)
{
checkBox.IsChecked = false;
}
}
public void SelectAll()
{
foreach (var checkBox in Items)
{
checkBox.IsChecked = checkBox.IsEnabled;
}
}
}
}
@@ -0,0 +1,293 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="1024" d:DesignHeight="700"
x:Class="UVtools.WPF.Windows.PrusaSlicerManager"
Title="Install profiles into PrusaSlicer"
MinWidth="900"
MinHeight="700"
Icon="/Assets/Icons/UVtools.ico">
<DockPanel LastChildFill="True">
<Grid DockPanel.Dock="Top"
RowDefinitions="Auto"
ColumnDefinitions="Auto,*">
<Border
Margin="5,5,0,5"
BorderBrush="LightBlue"
BorderThickness="4"
>
<StackPanel Orientation="Vertical">
<TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Legend"/>
<StackPanel Margin="15" Orientation="Vertical" Spacing="15">
<StackPanel Orientation="Horizontal" Spacing="5">
<Border
Width="18" Height="18"
BorderBrush="Black"
BorderThickness="2"
Background="Green"></Border>
<TextBlock
VerticalAlignment="Center"
Text="Installed Profile - Files match, no need to update"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="5">
<Border
Width="18" Height="18"
BorderBrush="Black"
BorderThickness="2"
Background="Red"></Border>
<TextBlock
VerticalAlignment="Center"
Text="Installed Profile - Files mismatch, update available"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="5">
<Border
Width="18" Height="18"
BorderBrush="Black"
BorderThickness="2"
Background="Black"></Border>
<TextBlock
VerticalAlignment="Center"
Text="Uninstalled Profile - Not present on PrusaSlicer"/>
</StackPanel>
</StackPanel>
</StackPanel>
</Border>
<Border
Grid.Column="1"
Margin="5"
BorderBrush="LightBlue"
BorderThickness="4"
>
<StackPanel Orientation="Vertical">
<TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Information"/>
<TextBlock
TextWrapping="Wrap"
Margin="15"
Text="This will install and overwrite all checked profiles into PrusaSlicer.
&#x0a;On PrusaSlicer access 'Printer' -> 'Notes' to change source parameters.
&#x0a;NOTE: If you modify a base profile and save under PrusaSlicer, this tool will be mark it as 'update available' since it got modified, in those cases we always recommend to never update base profiles, instead clone it and give different name as your own profile." />
</StackPanel>
</Border>
</Grid>
<Border DockPanel.Dock="Bottom"
Background="LightGray"
Height="80"
Padding="5,20,0,5"
>
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto">
<Button
Grid.Row="0"
Grid.Column="0"
Padding="10"
Command="{Binding RefreshProfiles}"
>
<StackPanel Orientation="Horizontal" Spacing="10">
<Image Source="/Assets/Icons/refresh-16x16.png"/>
<TextBlock Text="Refresh profiles"/>
</StackPanel>
</Button>
<StackPanel
Margin="0,0,5,5"
Grid.Row="0"
Grid.Column="1"
HorizontalAlignment="Right"
Orientation="Horizontal"
Spacing="5">
<Button Padding="10">
<StackPanel Orientation="Horizontal" Spacing="10">
<Image Source="/Assets/Icons/refresh-16x16.png"/>
<TextBlock Text="Install selected profiles"/>
</StackPanel>
</Button>
<Button Padding="10" Command="{Binding Close}">
<StackPanel Orientation="Horizontal" Spacing="10">
<Image Source="/Assets/Icons/exit-16x16.png"/>
<TextBlock Text="Close"/>
</StackPanel>
</Button>
</StackPanel>
</Grid>
</Border>
<Grid
ColumnDefinitions="*,*"
RowDefinitions="*"
>
<Border
Grid.Column="0"
Margin="5"
BorderBrush="LightBlue"
BorderThickness="4"
>
<Grid RowDefinitions="Auto,Auto,*">
<TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Printer profiles"/>
<StackPanel
Grid.Row="1"
Orientation="Horizontal" Spacing="5">
<Button Padding="10"
IsEnabled="{Binding Profiles[0].Updates}"
Command="{Binding Profiles[0].SelectNone}"
>
<StackPanel Orientation="Horizontal" Spacing="5">
<Image Source="/Assets/Icons/checkbox-unmarked-16x16.png"/>
<TextBlock Text="Unselect all"/>
</StackPanel>
</Button>
<Button Padding="10"
IsEnabled="{Binding Profiles[0].Updates}"
Command="{Binding Profiles[0].SelectAll}"
>
<StackPanel Orientation="Horizontal" Spacing="5">
<Image Source="/Assets/Icons/checkbox-marked-16x16.png"/>
<TextBlock Text="Select all"/>
</StackPanel>
</Button>
</StackPanel>
<StackPanel
Grid.Row="1"
HorizontalAlignment="Right"
Orientation="Horizontal"
Spacing="5">
<TextBlock
VerticalAlignment="Center"
Text="{Binding Profiles[0].Updates,
StringFormat=\{0\} Update(s)}"/>
<TextBlock
VerticalAlignment="Center"
Text="|"/>
<TextBlock
VerticalAlignment="Center"
Text="{Binding Profiles[0].Installed,
StringFormat=\{0\} Installed}"/>
<TextBlock
VerticalAlignment="Center"
Text="|"/>
<TextBlock
Margin="0,0,5,0"
VerticalAlignment="Center"
Text="{Binding Profiles[0].Items.Count,
StringFormat=\{0\} Profiles}"/>
</StackPanel>
<ListBox
Grid.Row="2"
SelectionMode="Toggle"
Items="{Binding Profiles[0].Items}"/>
</Grid>
</Border>
<Border
Grid.Column="1"
Margin="5"
BorderBrush="LightBlue"
BorderThickness="4"
>
<Grid RowDefinitions="Auto,Auto,*">
<TextBlock Padding="10" Background="LightBlue" FontWeight="Bold" Text="Printer profiles"/>
<StackPanel
Grid.Row="1"
Orientation="Horizontal" Spacing="5">
<Button Padding="10"
IsEnabled="{Binding Profiles[1].Updates}"
Command="{Binding Profiles[1].SelectNone}"
>
<StackPanel Orientation="Horizontal" Spacing="5">
<Image Source="/Assets/Icons/checkbox-unmarked-16x16.png"/>
<TextBlock Text="Unselect all"/>
</StackPanel>
</Button>
<Button Padding="10"
IsEnabled="{Binding Profiles[1].Updates}"
Command="{Binding Profiles[1].SelectAll}"
>
<StackPanel Orientation="Horizontal" Spacing="5">
<Image Source="/Assets/Icons/checkbox-marked-16x16.png"/>
<TextBlock Text="Select all"/>
</StackPanel>
</Button>
</StackPanel>
<StackPanel
Grid.Row="1"
HorizontalAlignment="Right"
Orientation="Horizontal"
Spacing="5">
<TextBlock
VerticalAlignment="Center"
Text="{Binding Profiles[1].Updates,
StringFormat=\{0\} Update(s)}"/>
<TextBlock
VerticalAlignment="Center"
Text="|"/>
<TextBlock
VerticalAlignment="Center"
Text="{Binding Profiles[1].Installed,
StringFormat=\{0\} Installed}"/>
<TextBlock
VerticalAlignment="Center"
Text="|"/>
<TextBlock
Margin="0,0,5,0"
VerticalAlignment="Center"
Text="{Binding Profiles[1].Items.Count,
StringFormat=\{0\} Profiles}"/>
</StackPanel>
<ListBox
Grid.Row="2"
SelectionMode="Toggle"
Items="{Binding Profiles[1].Items}"/>
</Grid>
</Border>
</Grid>
</DockPanel>
</Window>
@@ -0,0 +1,38 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using UVtools.WPF.Controls;
using UVtools.WPF.Structures;
namespace UVtools.WPF.Windows
{
public class PrusaSlicerManager : WindowEx
{
public PEProfileFolder[] Profiles { get;}
public PrusaSlicerManager()
{
InitializeComponent();
Profiles = new[]
{
new PEProfileFolder(PEProfileFolder.FolderType.Print),
new PEProfileFolder(PEProfileFolder.FolderType.Printer),
};
DataContext = this;
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
public void RefreshProfiles()
{
foreach (var profile in Profiles)
{
profile.Reset();
}
}
}
}