- (Add) File - Terminal: Inject C# code into UVtools with an interactive terminal
- (Improvement) Modifiers: Better increment values for the spin up/down buttons
- (Improvement) Tool - Timelapse: Better lift and feeds for virtual layer, also allow to set custom lift speed for that mode
- (Fix) Tool - Edit print parameters: Disallow to change PositionZ in layers with an active alternating pattern
- (Fix) GCode: When generating layers higher than the next, it will not return to the correct position z
This commit is contained in:
Tiago Conceição
2022-02-15 22:14:52 +00:00
parent 554794d0d8
commit 374e590574
16 changed files with 439 additions and 84 deletions
+8
View File
@@ -1,5 +1,13 @@
# Changelog
## 15/02/2022 - v2.28.1
- (Add) File - Terminal: Inject C# code into UVtools with an interactive terminal
- (Improvement) Modifiers: Better increment values for the spin up/down buttons
- (Improvement) Tool - Timelapse: Better lift and feeds for virtual layer, also allow to set custom lift speed for that mode
- (Fix) Tool - Edit print parameters: Disallow to change PositionZ in layers with an active alternating pattern
- (Fix) GCode: When generating layers higher than the next, it will not return to the correct position z
## 13/02/2022 - v2.28.0
- **Core:**
+28 -22
View File
@@ -147,46 +147,46 @@ namespace UVtools.Core.FileFormats
{
#region Instances
public static PrintParameterModifier PositionZ { get; } = new ("Position Z", "Absolute Z position", "mm",0, 100000, Layer.HeightPrecision);
public static PrintParameterModifier BottomLayerCount { get; } = new ("Bottom layers count", "Number of bottom/burn-in layers", "layers",0, ushort.MaxValue, 0);
public static PrintParameterModifier PositionZ { get; } = new ("Position Z", "Absolute Z position", "mm",0, 100000, 0.01, Layer.HeightPrecision);
public static PrintParameterModifier BottomLayerCount { get; } = new ("Bottom layers count", "Number of bottom/burn-in layers", "layers",0, ushort.MaxValue, 1, 0);
public static PrintParameterModifier BottomLightOffDelay { get; } = new("Bottom light-off seconds", "Total motor movement time + rest time to wait before cure a new bottom layer", "s");
public static PrintParameterModifier LightOffDelay { get; } = new("Light-off seconds", "Total motor movement time + rest time to wait before cure a new layer", "s");
public static PrintParameterModifier BottomWaitTimeBeforeCure { get; } = new ("Bottom wait before cure", "Time to wait/rest before cure a new bottom layer\nChitubox: Rest after retract\nLychee: Wait before print", "s", 0, 1000, 2);
public static PrintParameterModifier WaitTimeBeforeCure { get; } = new ("Wait before cure", "Time to wait/rest before cure a new layer\nChitubox: Rest after retract\nLychee: Wait before print", "s", 0, 1000, 2);
public static PrintParameterModifier BottomWaitTimeBeforeCure { get; } = new ("Bottom wait before cure", "Time to wait/rest before cure a new bottom layer\nChitubox: Rest after retract\nLychee: Wait before print", "s");
public static PrintParameterModifier WaitTimeBeforeCure { get; } = new ("Wait before cure", "Time to wait/rest before cure a new layer\nChitubox: Rest after retract\nLychee: Wait before print", "s");
public static PrintParameterModifier BottomExposureTime { get; } = new ("Bottom exposure time", "Bottom layers cure time", "s", 0.1M, 1000, 2);
public static PrintParameterModifier ExposureTime { get; } = new ("Exposure time", "Layers cure time", "s", 0.1M, 1000, 2);
public static PrintParameterModifier BottomExposureTime { get; } = new ("Bottom exposure time", "Bottom layers cure time", "s", 0.1M);
public static PrintParameterModifier ExposureTime { get; } = new ("Exposure time", "Layers cure time", "s", 0.1M);
public static PrintParameterModifier BottomWaitTimeAfterCure { get; } = new("Bottom wait after cure", "Time to wait/rest after cure a new bottom layer\nChitubox: Rest before lift\nLychee: Wait after print", "s", 0, 1000, 2);
public static PrintParameterModifier WaitTimeAfterCure { get; } = new("Wait after cure", "Time to wait/rest after cure a new bottom layer\nChitubox: Rest before lift\nLychee: Wait after print", "s", 0, 1000, 2);
public static PrintParameterModifier BottomWaitTimeAfterCure { get; } = new("Bottom wait after cure", "Time to wait/rest after cure a new bottom layer\nChitubox: Rest before lift\nLychee: Wait after print", "s");
public static PrintParameterModifier WaitTimeAfterCure { get; } = new("Wait after cure", "Time to wait/rest after cure a new bottom layer\nChitubox: Rest before lift\nLychee: Wait after print", "s");
public static PrintParameterModifier BottomLiftHeight { get; } = new ("Bottom lift height", "Bottom lift/peel height between layers", "mm", 1);
public static PrintParameterModifier LiftHeight { get; } = new ("Lift height", @"Lift/peel height between layers", "mm", 1);
public static PrintParameterModifier BottomLiftSpeed { get; } = new ("Bottom lift speed", null, "mm/min", 10, 5000, 2);
public static PrintParameterModifier LiftSpeed { get; } = new ("Lift speed", null, "mm/min", 10, 5000, 2);
public static PrintParameterModifier BottomLiftSpeed { get; } = new ("Bottom lift speed", null, "mm/min", 10, 5000, 5);
public static PrintParameterModifier LiftSpeed { get; } = new ("Lift speed", null, "mm/min", 10, 5000, 5);
public static PrintParameterModifier BottomLiftHeight2 { get; } = new("2) Bottom lift height", "Bottom second lift/peel height between layers", "mm");
public static PrintParameterModifier LiftHeight2 { get; } = new("2) Lift height", @"Second lift/peel height between layers", "mm");
public static PrintParameterModifier BottomLiftSpeed2 { get; } = new("2) Bottom lift speed", null, "mm/min", 10, 5000, 2);
public static PrintParameterModifier LiftSpeed2 { get; } = new("2) Lift speed", null, "mm/min", 10, 5000, 2);
public static PrintParameterModifier BottomLiftSpeed2 { get; } = new("2) Bottom lift speed", null, "mm/min", 10, 5000, 5);
public static PrintParameterModifier LiftSpeed2 { get; } = new("2) Lift speed", null, "mm/min", 10, 5000, 5);
public static PrintParameterModifier BottomWaitTimeAfterLift { get; } = new("Bottom wait after lift", "Time to wait/rest after a lift/peel sequence at bottom layers\nChitubox: Rest after lift\nLychee: Wait after lift", "s", 0, 1000, 2);
public static PrintParameterModifier WaitTimeAfterLift { get; } = new("Wait after lift", "Time to wait/rest after a lift/peel sequence at layers\nChitubox: Rest after lift\nLychee: Wait after lift", "s", 0, 1000, 2);
public static PrintParameterModifier BottomWaitTimeAfterLift { get; } = new("Bottom wait after lift", "Time to wait/rest after a lift/peel sequence at bottom layers\nChitubox: Rest after lift\nLychee: Wait after lift", "s");
public static PrintParameterModifier WaitTimeAfterLift { get; } = new("Wait after lift", "Time to wait/rest after a lift/peel sequence at layers\nChitubox: Rest after lift\nLychee: Wait after lift", "s");
public static PrintParameterModifier BottomRetractSpeed { get; } = new ("Bottom retract speed", "Bottom down speed from lift height to next layer cure position", "mm/min", 10, 5000, 2);
public static PrintParameterModifier RetractSpeed { get; } = new ("Retract speed", "Down speed from lift height to next layer cure position", "mm/min", 10, 5000, 2);
public static PrintParameterModifier BottomRetractSpeed { get; } = new ("Bottom retract speed", "Bottom down speed from lift height to next layer cure position", "mm/min", 10, 5000, 5);
public static PrintParameterModifier RetractSpeed { get; } = new ("Retract speed", "Down speed from lift height to next layer cure position", "mm/min", 10, 5000, 5);
public static PrintParameterModifier BottomRetractHeight2 { get; } = new("2) Bottom retract height", null, "mm");
public static PrintParameterModifier RetractHeight2 { get; } = new("2) Retract height", null, "mm");
public static PrintParameterModifier BottomRetractSpeed2 { get; } = new("2) Bottom retract speed", null, "mm/min", 10, 5000, 2);
public static PrintParameterModifier RetractSpeed2 { get; } = new("2) Retract speed", null, "mm/min", 10, 5000, 2);
public static PrintParameterModifier BottomRetractSpeed2 { get; } = new("2) Bottom retract speed", null, "mm/min", 10, 5000, 5);
public static PrintParameterModifier RetractSpeed2 { get; } = new("2) Retract speed", null, "mm/min", 10, 5000, 5);
public static PrintParameterModifier BottomLightPWM { get; } = new ("Bottom light PWM", "UV LED power for bottom layers", "☀", 1, byte.MaxValue, 0);
public static PrintParameterModifier LightPWM { get; } = new ("Light PWM", "UV LED power for layers", "☀", 1, byte.MaxValue, 0);
public static PrintParameterModifier BottomLightPWM { get; } = new ("Bottom light PWM", "UV LED power for bottom layers", "☀", 1, byte.MaxValue, 5, 0);
public static PrintParameterModifier LightPWM { get; } = new ("Light PWM", "UV LED power for layers", "☀", 1, byte.MaxValue, 5, 0);
/*public static PrintParameterModifier[] Parameters = {
BottomLayerCount,
@@ -240,6 +240,11 @@ namespace UVtools.Core.FileFormats
/// </summary>
public decimal Maximum { get; }
/// <summary>
/// Gets the incrementing value for the dropdown
/// </summary>
public double Increment { get; set; } = 1;
/// <summary>
/// Gets the number of decimal plates
/// </summary>
@@ -268,13 +273,14 @@ namespace UVtools.Core.FileFormats
#endregion
#region Constructor
public PrintParameterModifier(string name, string description = null, string valueUnit = null, decimal minimum = 0, decimal maximum = 1000, byte decimalPlates = 2)
public PrintParameterModifier(string name, string description = null, string valueUnit = null, decimal minimum = 0, decimal maximum = 1000, double increment = 0.5, byte decimalPlates = 2)
{
Name = name;
Description = description ?? $"Modify '{name}'";
ValueUnit = valueUnit ?? string.Empty;
Minimum = minimum;
Maximum = maximum;
Increment = decimalPlates == 0 ? Math.Max(1, increment) : increment;
DecimalPlates = decimalPlates;
}
#endregion
+3 -3
View File
@@ -673,15 +673,15 @@ namespace UVtools.Core.GCode
{
AppendLiftMoveGx(lifts, retracts, waitAfterLift, 0, layer);
}
else if (lastZPosition < layer.PositionZ) // Ensure Z is on correct position
else if (lastZPosition != layer.PositionZ) // Ensure Z is on correct position
{
switch (GCodePositioningType)
{
case GCodePositioningTypes.Absolute:
AppendMoveGx(layer.PositionZ, liftSpeed);
AppendMoveGx(layer.PositionZ, lastZPosition < layer.PositionZ ? Math.Max(liftSpeed, liftSpeed2) : Math.Max(retractSpeed, retractSpeed2));
break;
case GCodePositioningTypes.Partial:
AppendMoveGx(Layer.RoundHeight(layer.PositionZ - lastZPosition), liftSpeed);
AppendMoveGx(Layer.RoundHeight(layer.PositionZ - lastZPosition), lastZPosition < layer.PositionZ ? Math.Max(liftSpeed, liftSpeed2) : Math.Max(retractSpeed, retractSpeed2));
break;
}
@@ -22,7 +22,7 @@ namespace UVtools.Core.Operations
private bool _propagateModificationsToLayers = true;
private bool _perLayerOverride;
private uint _setNumberOfLayer = 1;
private uint _skipNumberOfLayer = 0;
private uint _skipNumberOfLayer;
#endregion
@@ -92,6 +92,15 @@ namespace UVtools.Core.Operations
sb.AppendLine("Nothing changed\nDo some changes or cancel the operation.");
}
if (Modifiers.Contains(FileFormat.PrintParameterModifier.PositionZ)
&& FileFormat.PrintParameterModifier.PositionZ.HasChanged
&& _skipNumberOfLayer > 0
&& LayerRangeCount > 1
&& _setNumberOfLayer + _skipNumberOfLayer < LayerRangeCount)
{
sb.AppendLine("Can not change the PositionZ in layers with an active alternating pattern.");
}
return sb.ToString();
}
@@ -156,18 +165,18 @@ namespace UVtools.Core.Operations
protected override bool ExecuteInternally(OperationProgress progress)
{
if (PerLayerOverride)
if (_perLayerOverride)
{
uint setLayers = 0;
for (uint layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; layerIndex++)
{
SlicerFile[layerIndex].SetValuesFromPrintParametersModifiers(Modifiers);
if (SkipNumberOfLayer <= 0) continue;
if (_skipNumberOfLayer == 0) continue;
setLayers++;
if (setLayers >= SetNumberOfLayer)
if (setLayers >= _setNumberOfLayer)
{
setLayers = 0;
layerIndex += SkipNumberOfLayer;
layerIndex += _skipNumberOfLayer;
}
}
+36 -24
View File
@@ -105,11 +105,6 @@ namespace UVtools.Core.Operations
#region Properties
/// <summary>
/// Gets the minimum possible
/// </summary>
public float MinimumPositionZ => Layer.RoundHeight(SlicerFile.PrintHeight + SlicerFile.LayerHeight);
public TimelapseRaiseMode RaiseMode
{
get => _raiseMode;
@@ -178,6 +173,8 @@ namespace UVtools.Core.Operations
set => RaiseAndSetIfChanged(ref _useCustomLift, value);
}
public bool CanUseCustomLift => _useCustomLift && SlicerFile.CanUseLayerLiftHeight;
public decimal SlowLiftHeight
{
get => _slowLiftHeight;
@@ -307,7 +304,7 @@ namespace UVtools.Core.Operations
switch (_raiseMode)
{
case TimelapseRaiseMode.LiftHeight:
if (_useCustomLift)
if (CanUseCustomLift)
{
layer.LiftSpeed = (float)_liftSpeed;
layer.RetractSpeed = (float)_retractSpeed;
@@ -333,7 +330,7 @@ namespace UVtools.Core.Operations
}
}
if (SlicerFile.CanUseLayerLiftHeight2 && (layer.LiftHeight2 > 0 || _useCustomLift && _slowLiftHeight > 0)) // TSMC
if (SlicerFile.CanUseLayerLiftHeight2 && (layer.LiftHeight2 > 0 || CanUseCustomLift && _slowLiftHeight > 0)) // TSMC
{
layer.LiftHeight2 = Math.Max(0, (float)_raisePositionZ - layer.PositionZ - layer.LiftHeight);
}
@@ -375,37 +372,52 @@ namespace UVtools.Core.Operations
RetractHeight2 = 0
};
layer.SetNoDelays();
/*if (_useCustomLift)
if (CanUseCustomLift)
{
layer.LiftSpeed = (float) _liftSpeed;
layer.RetractSpeed = (float) _retractSpeed;
layer.LiftSpeed = (float)_liftSpeed;
layer.RetractSpeed = (float)_retractSpeed;
/*if (SlicerFile.CanUseLayerLiftHeight2)
{
layer.LiftHeight = (float)_slowLiftHeight;
}*/
if (SlicerFile.CanUseLayerLiftSpeed2)
{
layer.LiftSpeed2 = (float) _liftSpeed2;
layer.LiftSpeed2 = (float)_liftSpeed2;
}
if (SlicerFile.CanUseLayerRetractSpeed2)
{
layer.RetractSpeed2 = (float) _retractSpeed2;
layer.RetractSpeed2 = (float)_retractSpeed2;
}
}*/
}
layer.SetNoDelays();
var layers = SlicerFile.ToList();
for (int i = 0; i < virtualLayers.Count; i++)
{
/*var newLayer = layer.Clone();
if (!_useCustomLift)
int insertIndex = (int)virtualLayers[i];
SlicerFile[insertIndex].LiftHeightTotal = SlicerFile.SupportsGCode ? 0 : 0.1f;
if (CanUseCustomLift)
{
var intersectLayer = Math.Clamp(virtualLayers[i] + i, 0, SlicerFile.LastLayerIndex);
SlicerFile[intersectLayer].CopyLiftTo(newLayer);
// This layer does not require a lift procedure
newLayer.LiftHeightTotal = SlicerFile.SupportsGCode ? 0 : 0.1f;
newLayer.RetractHeight2 = 0;
}*/
layers.Insert((int)(virtualLayers[i] + i), layer.Clone());
SlicerFile[insertIndex].LiftSpeed = (float)_liftSpeed;
SlicerFile[insertIndex].RetractSpeed = (float)_retractSpeed;
if (SlicerFile.CanUseLayerLiftSpeed2)
{
SlicerFile[insertIndex].LiftSpeed2 = (float)_retractSpeed2;
}
if (SlicerFile.CanUseLayerRetractSpeed2)
{
SlicerFile[insertIndex].RetractSpeed2 = (float)_retractSpeed2;
}
}
layers.Insert(insertIndex + i, layer.Clone());
}
SlicerFile.SuppressRebuildPropertiesWork(() => SlicerFile.LayerManager.Layers = layers.ToArray());
}
+1 -1
View File
@@ -10,7 +10,7 @@
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl>
<Description>MSLA/DLP, file analysis, calibration, repair, conversion and manipulation</Description>
<Version>2.28.0</Version>
<Version>2.28.1</Version>
<Copyright>Copyright © 2020 PTRTECH</Copyright>
<PackageIcon>UVtools.png</PackageIcon>
<Platforms>AnyCPU;x64</Platforms>
Binary file not shown.

After

Width:  |  Height:  |  Size: 123 B

@@ -62,7 +62,7 @@ namespace UVtools.WPF.Controls.Tools
HorizontalAlignment = HorizontalAlignment.Stretch,
Minimum = (double) modifier.Minimum,
Maximum = (double) modifier.Maximum,
Increment = modifier.DecimalPlates == 0 ? 1 : 0.5,
Increment = modifier.Increment,
Value = (double)modifier.NewValue,
Tag = this,
//Width = 100,
@@ -70,7 +70,7 @@ namespace UVtools.WPF.Controls.Tools
};
if (modifier.DecimalPlates > 0)
{
NewValue.FormatString = "F02";
NewValue.FormatString = $"F{modifier.DecimalPlates}";
}
Unit = new TextBlock
@@ -151,7 +151,6 @@
IsChecked="{Binding Operation.UseCustomLift}">
<CheckBox.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="Operation.IsLiftHeightMode"/>
<Binding Path="SlicerFile.CanUseLayerLiftHeight"/>
</MultiBinding>
</CheckBox.IsVisible>
@@ -162,7 +161,6 @@
ColumnDefinitions="Auto,10,210,5,Auto,5,210">
<Grid.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="Operation.IsLiftHeightMode"/>
<Binding Path="Operation.UseCustomLift"/>
<Binding Path="SlicerFile.CanUseLayerLiftHeight"/>
</MultiBinding>
@@ -172,6 +170,7 @@
VerticalAlignment="Center"
ToolTip.Tip="The slow first lift sequence (TSMC).
&#x0a;Use a low value or 0 to disable."
IsVisible="{Binding !Operation.IsVirtualLayerMode}"
Text="Slow lift height:"/>
<NumericUpDown Grid.Row="0" Grid.Column="2"
@@ -179,15 +178,26 @@
Minimum="0"
Maximum="20"
Increment="1"
IsVisible="{Binding SlicerFile.CanUseLayerLiftHeight2}"
Value="{Binding Operation.SlowLiftHeight}">
<NumericUpDown.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="SlicerFile.CanUseLayerLiftHeight2"/>
<Binding Path="!Operation.IsVirtualLayerMode"/>
</MultiBinding>
</NumericUpDown.IsVisible>
</NumericUpDown>
<TextBlock Grid.Row="0" Grid.Column="2"
VerticalAlignment="Center"
ToolTip.Tip="(Not supported by your printer / file format)"
IsVisible="{Binding !SlicerFile.CanUseLayerLiftHeight2}"
Text="(Not supported)"/>
Text="(Not supported)">
<TextBlock.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="!SlicerFile.CanUseLayerLiftHeight2"/>
<Binding Path="!Operation.IsVirtualLayerMode"/>
</MultiBinding>
</TextBlock.IsVisible>
</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="0"
VerticalAlignment="Center"
@@ -225,6 +235,7 @@
VerticalAlignment="Center"
ToolTip.Tip="The slow last retract sequence (TSMC).
&#x0a;Use a low value or 0 to disable."
IsVisible="{Binding !Operation.IsVirtualLayerMode}"
Text="Slow retract height:"/>
<NumericUpDown Grid.Row="4" Grid.Column="2"
@@ -232,15 +243,26 @@
Minimum="0"
Maximum="20"
Increment="1"
IsVisible="{Binding SlicerFile.CanUseLayerRetractHeight2}"
Value="{Binding Operation.SlowRetractHeight}">
<NumericUpDown.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="SlicerFile.CanUseLayerRetractHeight2"/>
<Binding Path="!Operation.IsVirtualLayerMode"/>
</MultiBinding>
</NumericUpDown.IsVisible>
</NumericUpDown>
<TextBlock Grid.Row="4" Grid.Column="2"
VerticalAlignment="Center"
ToolTip.Tip="(Not supported by your printer / file format)"
IsVisible="{Binding !SlicerFile.CanUseLayerRetractHeight2}"
Text="(Not supported)"/>
Text="(Not supported)">
<TextBlock.IsVisible>
<MultiBinding Converter="{x:Static BoolConverters.And}">
<Binding Path="!SlicerFile.CanUseLayerRetractHeight2"/>
<Binding Path="!Operation.IsVirtualLayerMode"/>
</MultiBinding>
</TextBlock.IsVisible>
</TextBlock>
<TextBlock Grid.Row="6" Grid.Column="0"
+9 -10
View File
@@ -20,7 +20,7 @@ namespace UVtools.WPF.Extensions
public static class WindowExtensions
{
public static async Task<ButtonResult> MessageBoxGeneric(this Window window, string message, string title = null,
ButtonEnum buttons = ButtonEnum.Ok, Icon icon = Icon.None, bool topMost = false, WindowStartupLocation location = WindowStartupLocation.CenterOwner, Style style = Style.None)
ButtonEnum buttons = ButtonEnum.Ok, Icon icon = Icon.None, bool topMost = false, WindowStartupLocation location = WindowStartupLocation.CenterOwner)
{
var messageBoxStandardWindow = MessageBox.Avalonia.MessageBoxManager.GetMessageBoxStandardWindow(
new MessageBoxStandardParams
@@ -29,7 +29,6 @@ namespace UVtools.WPF.Extensions
ContentTitle = title ?? window.Title,
ContentMessage = message,
Icon = icon,
Style = style,
WindowIcon = new WindowIcon(App.GetAsset("/Assets/Icons/UVtools.ico")),
WindowStartupLocation = location,
CanResize = UserSettings.Instance.General.WindowsCanResize,
@@ -42,17 +41,17 @@ namespace UVtools.WPF.Extensions
return await messageBoxStandardWindow.ShowDialog(window);
}
public static async Task<ButtonResult> MessageBoxInfo(this Window window, string message, string title = null, ButtonEnum buttons = ButtonEnum.Ok, bool topMost = false, Style style = Style.None)
=> await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Information", buttons, Icon.Info, topMost, WindowStartupLocation.CenterOwner, style);
public static async Task<ButtonResult> MessageBoxInfo(this Window window, string message, string title = null, ButtonEnum buttons = ButtonEnum.Ok, bool topMost = false)
=> await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Information", buttons, Icon.Info, topMost, WindowStartupLocation.CenterOwner);
public static async Task<ButtonResult> MessageBoxError(this Window window, string message, string title = null, ButtonEnum buttons = ButtonEnum.Ok, bool topMost = false, Style style = Style.None)
=> await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Error", buttons, Icon.Error, topMost, WindowStartupLocation.CenterOwner, style);
public static async Task<ButtonResult> MessageBoxError(this Window window, string message, string title = null, ButtonEnum buttons = ButtonEnum.Ok, bool topMost = false)
=> await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Error", buttons, Icon.Error, topMost, WindowStartupLocation.CenterOwner);
public static async Task<ButtonResult> MessageBoxQuestion(this Window window, string message, string title = null, ButtonEnum buttons = ButtonEnum.YesNo, bool topMost = false, Style style = Style.None)
=> await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Question", buttons, Icon.Setting, topMost, WindowStartupLocation.CenterOwner, style);
public static async Task<ButtonResult> MessageBoxQuestion(this Window window, string message, string title = null, ButtonEnum buttons = ButtonEnum.YesNo, bool topMost = false)
=> await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Question", buttons, Icon.Setting, topMost, WindowStartupLocation.CenterOwner);
public static async Task<ButtonResult> MessageBoxWaring(this Window window, string message, string title = null, ButtonEnum buttons = ButtonEnum.Ok, bool topMost = false, Style style = Style.None)
=> await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Question", buttons, Icon.Warning, topMost, WindowStartupLocation.CenterOwner, style);
public static async Task<ButtonResult> MessageBoxWaring(this Window window, string message, string title = null, ButtonEnum buttons = ButtonEnum.Ok, bool topMost = false)
=> await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Question", buttons, Icon.Warning, topMost, WindowStartupLocation.CenterOwner);
public static void ShowDialogSync(this Window window, Window parent = null)
+13 -2
View File
@@ -127,8 +127,19 @@
<Image Source="\Assets\Icons\extract-object-16x16.png"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem
<MenuItem Name="MainMenu.File.Terminal"
HotKey="Ctrl+Shift+T" InputGesture="Ctrl+Shift+T"
Header="_Terminal"
Command="{Binding OpenTerminal}"
IsEnabled="{Binding IsFileLoaded}">
<MenuItem.Icon>
<Image Source="\Assets\Icons\terminal-16x16.png"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem
Name="MainMenu.File.Convert"
Header="_Convert to"
IsEnabled="{Binding IsFileLoaded}"
+7 -4
View File
@@ -617,7 +617,7 @@ namespace UVtools.WPF
AddHandler(DragDrop.DropEvent, (sender, e) =>
{
ProcessFiles(e.Data.GetFileNames().ToArray());
ProcessFiles(e.Data.GetFileNames()?.ToArray());
});
_menuFileSendTo = this.FindControl<MenuItem>("MainMenu.File.SendTo");
@@ -1823,8 +1823,6 @@ namespace UVtools.WPF
VisibleThumbnailIndex = 1;
RefreshProperties();
UpdateTitle();
if (mat is not null)
@@ -1904,7 +1902,7 @@ namespace UVtools.WPF
$"Ratio: {xRatio}:{yRatio}\n",
"Incorrect image ratio detected");
}
RefreshProperties();
ResetDataContext();
ForceUpdateActualLayer(actualLayer.Clamp(actualLayer, SliderMaximumValue));
@@ -2234,6 +2232,11 @@ namespace UVtools.WPF
}
public void OpenTerminal()
{
new TerminalWindow().Show(this);
}
#region Operations
public async Task<Operation> ShowRunOperation(Operation loadOperation) =>
+2 -2
View File
@@ -12,7 +12,7 @@
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<Version>2.28.0</Version>
<Version>2.28.1</Version>
<Platforms>AnyCPU;x64</Platforms>
<PackageIcon>UVtools.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
@@ -44,7 +44,7 @@
<PackageReference Include="Avalonia.Desktop" Version="0.10.12" />
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.12" />
<PackageReference Include="Emgu.CV.runtime.windows" Version="4.5.5.4823" />
<PackageReference Include="MessageBox.Avalonia" Version="1.7.1" />
<PackageReference Include="MessageBox.Avalonia" Version="1.8.1-night" />
<PackageReference Include="ThemeEditor.Controls.ColorPicker" Version="0.10.12" />
</ItemGroup>
<ItemGroup>
+78
View File
@@ -0,0 +1,78 @@
<uc:WindowEx 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"
xmlns:uc="clr-namespace:UVtools.WPF.Controls"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="500"
MinWidth="500"
MinHeight="300"
Width="800"
Height="500"
Padding="10"
WindowStartupLocation="CenterOwner"
DragDrop.AllowDrop="True"
Icon="/Assets/Icons/UVtools.ico"
x:Class="UVtools.WPF.Windows.TerminalWindow"
Title="UVtools interactive terminal">
<Grid RowDefinitions="4*,5,*,5,Auto">
<TextBox Grid.Row="0"
Name="TerminalTextBox"
AcceptsReturn="True"
IsReadOnly="True"
Watermark="Terminal"
UseFloatingWatermark="True"
Text="{Binding TerminalText}"/>
<GridSplitter Grid.Row="1"
ResizeDirection="Rows"
ResizeBehavior="PreviousAndNext"/>
<TextBox Grid.Row="2"
AcceptsReturn="{Binding MultiLine}"
Watermark="> Type in the command or text to send
&#x0a;Alt + enter: Shortcut to send the command"
Text="{Binding CommandText}"/>
<WrapPanel Grid.Row="4" Orientation="Horizontal" HorizontalAlignment="Right">
<!--
<ToggleSwitch VerticalAlignment="Center"
IsChecked="{Binding MultiLine}"
OnContent="Multi line"
OffContent="Single line"/>
-->
<ToggleButton Margin="0,0,0,0"
VerticalAlignment="Center"
IsChecked="{Binding Verbose}"
ToolTip.Tip="Sent command appears on the terminal text"
Content="Verbose"/>
<ToggleButton Margin="2,0,0,0"
VerticalAlignment="Center"
IsChecked="{Binding ClearCommandAfterSend}"
ToolTip.Tip="Clears the sent command on the input box"
Content="Clear"/>
<ToggleButton Margin="2,0,0,0"
VerticalAlignment="Center"
IsChecked="{Binding AutoScroll}"
Content="Auto scroll"/>
<Button Margin="15,0,0,0"
Content="Clear"
VerticalAlignment="Center"
Command="{Binding Clear}"
ToolTip.Tip="Clears the terminal text (cls)
&#x0a;Alt + delete"
HotKey="Alt+Delete"/>
<Button Margin="5,0,0,0"
Content="Send"
Padding="40,6"
VerticalAlignment="Center"
Command="{Binding SendCommand}"
IsDefault="True"
HotKey="Alt+Enter"/>
</WrapPanel>
</Grid>
</uc:WindowEx>
+200
View File
@@ -0,0 +1,200 @@
/*
* GNU AFFERO GENERAL PUBLIC LICENSE
* Version 3, 19 November 2007
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
using System;
using System.IO;
using System.Text;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
using UVtools.Core;
using UVtools.WPF.Controls;
namespace UVtools.WPF.Windows
{
public partial class TerminalWindow : WindowEx
{
private static readonly string DefaultTerminalText = $"> Welcome to {About.Software} interactive terminal.\n" +
"> Type in some commands in C# language to inject code.\n" +
"> Example 1: SlicerFile.FirstLayer\n" +
"> Example 2: SlicerFile.ExposureTime = 3\n\n";
private string _terminalText = DefaultTerminalText;
private string _commandText = string.Empty;
private bool _multiLine = true;
private bool _autoScroll = true;
private bool _verbose;
private bool _clearCommandAfterSend = true;
private readonly TextBox _terminalTextBox;
public ScriptState _scriptState;
#region Properties
public string TerminalText
{
get => _terminalText;
set
{
if(!RaiseAndSetIfChanged(ref _terminalText, value)) return;
if(_autoScroll) _terminalTextBox.CaretIndex = _terminalText.Length - 1;
}
}
public string CommandText
{
get => _commandText;
set => RaiseAndSetIfChanged(ref _commandText, value ?? string.Empty);
}
public bool MultiLine
{
get => _multiLine;
set => RaiseAndSetIfChanged(ref _multiLine, value);
}
public bool AutoScroll
{
get => _autoScroll;
set => RaiseAndSetIfChanged(ref _autoScroll, value);
}
public bool Verbose
{
get => _verbose;
set => RaiseAndSetIfChanged(ref _verbose, value);
}
public bool ClearCommandAfterSend
{
get => _clearCommandAfterSend;
set => RaiseAndSetIfChanged(ref _clearCommandAfterSend, value);
}
#endregion
public TerminalWindow()
{
InitializeComponent();
#if DEBUG
this.AttachDevTools();
#endif
_terminalTextBox = this.FindControl<TextBox>("TerminalTextBox");
AddHandler(DragDrop.DropEvent, (sender, e) =>
{
var text = e.Data.GetText();
if (text is not null)
{
CommandText = text;
return;
}
var fileNames = e.Data.GetFileNames();
if (fileNames is not null)
{
var sb = new StringBuilder();
foreach (var fileName in fileNames)
{
try
{
if (!File.Exists(fileName)) continue;
var fi = new FileInfo(fileName);
if(fi.Length > 5000000) continue; // 5Mb only!
sb.AppendLine(File.ReadAllText(fi.FullName));
}
catch (Exception exception)
{
Console.WriteLine(exception);
}
}
CommandText = sb.ToString();
}
});
DataContext = this;
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
public void Clear()
{
TerminalText = DefaultTerminalText;
}
public async void SendCommand()
{
if (string.IsNullOrWhiteSpace(_commandText))
{
TerminalText += '\n';
return;
}
var output = new StringBuilder(_terminalText);
if (_verbose) output.AppendLine(_commandText);
try
{
if (_scriptState is null)
{
_scriptState = CSharpScript.RunAsync(_commandText,
ScriptOptions.Default
.AddReferences(typeof(About).Assembly)
.AddImports(
"System",
"System.Collections.Generic",
"System.Math",
"System.IO",
"System.Linq",
"System.Threading",
"System.Threading.Tasks",
"UVtools.Core",
"UVtools.Core.Extensions",
"UVtools.Core.FileFormats",
"UVtools.Core.Layers",
"UVtools.Core.Objects",
"UVtools.Core.Operations")
.WithAllowUnsafe(true), this).Result;
}
else
{
_scriptState = await _scriptState.ContinueWithAsync(_commandText);
}
if (_scriptState.ReturnValue is not null)
{
output.AppendLine(_scriptState.ReturnValue.ToString());
}
else if (_scriptState.Exception is not null)
{
output.AppendLine(_scriptState.Exception.ToString());
}
else if(!_verbose)
{
output.AppendLine(_commandText);
}
}
catch (Exception e)
{
output.AppendLine(e.Message);
}
TerminalText = output.ToString();
if (_clearCommandAfterSend) CommandText = string.Empty;
}
}
}
+8 -1
View File
@@ -1,4 +1,11 @@
using System;
/*
* GNU AFFERO GENERAL PUBLIC LICENSE
* Version 3, 19 November 2007
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
using System;
using System.Collections.ObjectModel;
using System.Drawing;
using Avalonia;