Compare commits
6 Commits
fdf2419eb6
...
a483144d29
| Author | SHA1 | Date | |
|---|---|---|---|
| a483144d29 | |||
| 2eb3fed4d0 | |||
| 48c419d777 | |||
| d4050b4218 | |||
| f5dceeceb7 | |||
| fd4048a6c0 |
@@ -16,8 +16,8 @@
|
||||
Background="Transparent"
|
||||
BorderBrush="#FFD5DFE5"
|
||||
BorderThickness="1"
|
||||
Padding="10"
|
||||
Margin="0,0,0,10">
|
||||
Padding="8"
|
||||
Margin="0,0,0,8">
|
||||
<StackPanel>
|
||||
<TextBlock x:Name="txtProcessorName"
|
||||
FontSize="14"
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
<!-- 参数列表 -->
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel x:Name="pnlParameters" Margin="5" />
|
||||
<StackPanel x:Name="pnlParameters" Margin="2" />
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
|
||||
@@ -143,12 +143,15 @@ public partial class ProcessorParameterControl : UserControl
|
||||
var textBox = new TextBox
|
||||
{
|
||||
Text = param.Value.ToString(),
|
||||
Width = 100,
|
||||
HorizontalAlignment = HorizontalAlignment.Left
|
||||
Width = 56,
|
||||
MinWidth = 56,
|
||||
HorizontalContentAlignment = HorizontalAlignment.Center,
|
||||
VerticalContentAlignment = VerticalAlignment.Center
|
||||
};
|
||||
|
||||
if (param.MinValue != null && param.MaxValue != null)
|
||||
{
|
||||
var rangeGrid = CreateRangeEditorContainer();
|
||||
var slider = new Slider
|
||||
{
|
||||
Minimum = Convert.ToDouble(param.MinValue),
|
||||
@@ -156,7 +159,8 @@ public partial class ProcessorParameterControl : UserControl
|
||||
Value = Convert.ToDouble(param.Value),
|
||||
TickFrequency = 1,
|
||||
IsSnapToTickEnabled = true,
|
||||
Margin = new Thickness(0, 0, 0, 5)
|
||||
MinWidth = 120,
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
|
||||
slider.ValueChanged += (s, e) =>
|
||||
@@ -181,7 +185,11 @@ public partial class ProcessorParameterControl : UserControl
|
||||
}
|
||||
};
|
||||
|
||||
panel.Children.Add(slider);
|
||||
Grid.SetColumn(slider, 0);
|
||||
Grid.SetColumn(textBox, 2);
|
||||
rangeGrid.Children.Add(slider);
|
||||
rangeGrid.Children.Add(textBox);
|
||||
panel.Children.Add(rangeGrid);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -193,9 +201,9 @@ public partial class ProcessorParameterControl : UserControl
|
||||
OnParameterChanged();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
panel.Children.Add(textBox);
|
||||
panel.Children.Add(textBox);
|
||||
}
|
||||
|
||||
return panel;
|
||||
}
|
||||
@@ -211,19 +219,23 @@ public partial class ProcessorParameterControl : UserControl
|
||||
var textBox = new TextBox
|
||||
{
|
||||
Text = Convert.ToDouble(param.Value).ToString("F2"),
|
||||
Width = 100,
|
||||
HorizontalAlignment = HorizontalAlignment.Left
|
||||
Width = 56,
|
||||
MinWidth = 56,
|
||||
HorizontalContentAlignment = HorizontalAlignment.Center,
|
||||
VerticalContentAlignment = VerticalAlignment.Center
|
||||
};
|
||||
|
||||
if (param.MinValue != null && param.MaxValue != null)
|
||||
{
|
||||
var rangeGrid = CreateRangeEditorContainer();
|
||||
var slider = new Slider
|
||||
{
|
||||
Minimum = Convert.ToDouble(param.MinValue),
|
||||
Maximum = Convert.ToDouble(param.MaxValue),
|
||||
Value = Convert.ToDouble(param.Value),
|
||||
TickFrequency = 0.1,
|
||||
Margin = new Thickness(0, 0, 0, 5)
|
||||
MinWidth = 120,
|
||||
VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
|
||||
slider.ValueChanged += (s, e) =>
|
||||
@@ -248,7 +260,11 @@ public partial class ProcessorParameterControl : UserControl
|
||||
}
|
||||
};
|
||||
|
||||
panel.Children.Add(slider);
|
||||
Grid.SetColumn(slider, 0);
|
||||
Grid.SetColumn(textBox, 2);
|
||||
rangeGrid.Children.Add(slider);
|
||||
rangeGrid.Children.Add(textBox);
|
||||
panel.Children.Add(rangeGrid);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -260,9 +276,9 @@ public partial class ProcessorParameterControl : UserControl
|
||||
OnParameterChanged();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
panel.Children.Add(textBox);
|
||||
panel.Children.Add(textBox);
|
||||
}
|
||||
|
||||
return panel;
|
||||
}
|
||||
@@ -302,8 +318,8 @@ public partial class ProcessorParameterControl : UserControl
|
||||
var comboBox = new ComboBox
|
||||
{
|
||||
Margin = new Thickness(0, 5, 0, 0),
|
||||
Width = 200,
|
||||
HorizontalAlignment = HorizontalAlignment.Left
|
||||
MinWidth = 160,
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch
|
||||
};
|
||||
|
||||
if (param.Options != null)
|
||||
@@ -344,8 +360,8 @@ public partial class ProcessorParameterControl : UserControl
|
||||
{
|
||||
Text = param.Value?.ToString() ?? "",
|
||||
Margin = new Thickness(0, 5, 0, 0),
|
||||
Width = 200,
|
||||
HorizontalAlignment = HorizontalAlignment.Left
|
||||
MinWidth = 160,
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch
|
||||
};
|
||||
|
||||
textBox.TextChanged += (s, e) =>
|
||||
@@ -374,4 +390,16 @@ public partial class ProcessorParameterControl : UserControl
|
||||
pnlParameters.Children.Clear();
|
||||
UpdateNoProcessorText();
|
||||
}
|
||||
}
|
||||
|
||||
private static Grid CreateRangeEditorContainer()
|
||||
{
|
||||
var grid = new Grid
|
||||
{
|
||||
Margin = new Thickness(0, 0, 0, 4)
|
||||
};
|
||||
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
|
||||
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(6) });
|
||||
grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
|
||||
return grid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ namespace XplorePlane.Services.Cnc
|
||||
break;
|
||||
}
|
||||
|
||||
progress?.Report(new CncNodeExecutionProgress(node.Id, NodeExecutionState.Running));
|
||||
progress?.Report(new CncNodeExecutionProgress(node.Id, NodeExecutionState.Running, ProgressPercent: 0));
|
||||
|
||||
bool nodeSucceeded = true;
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace XplorePlane.Services.Cnc
|
||||
case WaitDelayNode waitNode:
|
||||
try
|
||||
{
|
||||
await ExecuteWaitDelayWithProgressAsync(waitNode, cancellationToken);
|
||||
await ExecuteWaitDelayWithProgressAsync(waitNode, progress, cancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
@@ -324,11 +324,17 @@ namespace XplorePlane.Services.Cnc
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
private static async Task ExecuteWaitDelayWithProgressAsync(WaitDelayNode waitNode, CancellationToken cancellationToken)
|
||||
private static async Task ExecuteWaitDelayWithProgressAsync(
|
||||
WaitDelayNode waitNode,
|
||||
IProgress<CncNodeExecutionProgress> progress,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
int totalMs = waitNode.DelayMilliseconds;
|
||||
if (totalMs <= 0)
|
||||
{
|
||||
progress?.Report(new CncNodeExecutionProgress(waitNode.Id, NodeExecutionState.Running, ProgressPercent: 100));
|
||||
return;
|
||||
}
|
||||
|
||||
const int tickMs = 50;
|
||||
|
||||
@@ -341,6 +347,10 @@ namespace XplorePlane.Services.Cnc
|
||||
int delay = Math.Min(tickMs, remaining);
|
||||
await Task.Delay(delay, cancellationToken);
|
||||
elapsed += delay;
|
||||
progress?.Report(new CncNodeExecutionProgress(
|
||||
waitNode.Id,
|
||||
NodeExecutionState.Running,
|
||||
ProgressPercent: elapsed * 100d / totalMs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,11 @@ namespace XplorePlane.Services.Cnc
|
||||
/// <summary>
|
||||
/// Progress report for a single CNC node execution.
|
||||
/// ResultImage is non-null when an InspectionModuleNode produces output.
|
||||
/// ProgressPercent is used by long-running nodes such as WaitDelayNode.
|
||||
/// </summary>
|
||||
public record CncNodeExecutionProgress(Guid NodeId, NodeExecutionState State, BitmapSource ResultImage = null);
|
||||
public record CncNodeExecutionProgress(
|
||||
Guid NodeId,
|
||||
NodeExecutionState State,
|
||||
BitmapSource ResultImage = null,
|
||||
double? ProgressPercent = null);
|
||||
}
|
||||
|
||||
@@ -455,6 +455,7 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
private async Task ExecuteRunAsync()
|
||||
{
|
||||
_cts = new CancellationTokenSource();
|
||||
ResetAllNodeStates();
|
||||
IsRunning = true;
|
||||
HasExecutionError = false;
|
||||
ExecutionError = null;
|
||||
@@ -482,7 +483,6 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
finally
|
||||
{
|
||||
IsRunning = false;
|
||||
ResetAllNodeStates();
|
||||
_cts?.Dispose();
|
||||
_cts = null;
|
||||
}
|
||||
@@ -499,6 +499,7 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
if (nodeVm != null)
|
||||
{
|
||||
nodeVm.ExecutionState = progress.State;
|
||||
nodeVm.ExecutionProgressPercent = progress.ProgressPercent ?? (progress.State == NodeExecutionState.Succeeded ? 100d : 0d);
|
||||
if (progress.State == NodeExecutionState.Running)
|
||||
StatusMessage = $"正在执行节点:{nodeVm.Name}({nodeVm.Index + 1}/{_currentProgram?.Nodes?.Count ?? 0})";
|
||||
else if (progress.State == NodeExecutionState.Succeeded)
|
||||
@@ -519,7 +520,10 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
private void ResetAllNodeStates()
|
||||
{
|
||||
foreach (var node in Nodes)
|
||||
{
|
||||
node.ExecutionState = NodeExecutionState.Idle;
|
||||
node.ExecutionProgressPercent = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void RaiseEditCommandsCanExecuteChanged()
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
private string _statusMessage = "请选择检测模块以编辑其流水线。";
|
||||
private string _pipelineFileDisplayName = "未命名模块.xpm";
|
||||
private string _currentFilePath;
|
||||
private PipelineNodeViewModel _executionEndNode;
|
||||
private bool _isSynchronizing;
|
||||
private CancellationTokenSource _debounceCts;
|
||||
|
||||
@@ -65,6 +66,8 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
RemoveOperatorCommand = new DelegateCommand<PipelineNodeViewModel>(RemoveOperator);
|
||||
ReorderOperatorCommand = new DelegateCommand<PipelineReorderArgs>(ReorderOperator);
|
||||
ToggleOperatorEnabledCommand = new DelegateCommand<PipelineNodeViewModel>(ToggleOperatorEnabled);
|
||||
ExecuteToNodeCommand = new DelegateCommand<PipelineNodeViewModel>(ExecuteToNode);
|
||||
ClearExecutionRangeCommand = new DelegateCommand(ClearExecutionRange);
|
||||
MoveNodeUpCommand = new DelegateCommand<PipelineNodeViewModel>(MoveNodeUp);
|
||||
MoveNodeDownCommand = new DelegateCommand<PipelineNodeViewModel>(MoveNodeDown);
|
||||
NewPipelineCommand = new DelegateCommand(NewPipeline);
|
||||
@@ -98,6 +101,16 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
private set => SetProperty(ref _pipelineFileDisplayName, value);
|
||||
}
|
||||
|
||||
public PipelineNodeViewModel ExecutionEndNode
|
||||
{
|
||||
get => _executionEndNode;
|
||||
private set
|
||||
{
|
||||
if (SetProperty(ref _executionEndNode, value))
|
||||
UpdateExecutionRangeState();
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasActiveModule => _activeModuleNode?.IsInspectionModule == true;
|
||||
|
||||
public Visibility EditorVisibility => HasActiveModule ? Visibility.Visible : Visibility.Collapsed;
|
||||
@@ -112,6 +125,10 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
|
||||
public ICommand ToggleOperatorEnabledCommand { get; }
|
||||
|
||||
public ICommand ExecuteToNodeCommand { get; }
|
||||
|
||||
public ICommand ClearExecutionRangeCommand { get; }
|
||||
|
||||
public ICommand MoveNodeUpCommand { get; }
|
||||
|
||||
public ICommand MoveNodeDownCommand { get; }
|
||||
@@ -179,6 +196,7 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
LoadNodeParameters(node, null);
|
||||
PipelineNodes.Add(node);
|
||||
SelectedNode = node;
|
||||
UpdateExecutionRangeState();
|
||||
PersistActiveModule($"已添加算子:{displayName}");
|
||||
}
|
||||
|
||||
@@ -190,6 +208,10 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
var removedIndex = PipelineNodes.IndexOf(node);
|
||||
PipelineNodes.Remove(node);
|
||||
RenumberNodes();
|
||||
if (ReferenceEquals(ExecutionEndNode, node))
|
||||
ExecutionEndNode = null;
|
||||
else
|
||||
UpdateExecutionRangeState();
|
||||
SelectNeighborAfterRemoval(removedIndex);
|
||||
|
||||
PersistActiveModule($"已移除算子:{node.DisplayName}");
|
||||
@@ -206,6 +228,7 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
|
||||
PipelineNodes.Move(index, index - 1);
|
||||
RenumberNodes();
|
||||
UpdateExecutionRangeState();
|
||||
PersistActiveModule($"已上移算子:{node.DisplayName}");
|
||||
}
|
||||
|
||||
@@ -225,6 +248,7 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
var node = PipelineNodes[oldIndex];
|
||||
PipelineNodes.Move(oldIndex, newIndex);
|
||||
RenumberNodes();
|
||||
UpdateExecutionRangeState();
|
||||
SelectedNode = node;
|
||||
PersistActiveModule($"已调整算子顺序:{node.DisplayName}");
|
||||
}
|
||||
@@ -240,6 +264,7 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
|
||||
PipelineNodes.Move(index, index + 1);
|
||||
RenumberNodes();
|
||||
UpdateExecutionRangeState();
|
||||
PersistActiveModule($"已下移算子:{node.DisplayName}");
|
||||
}
|
||||
|
||||
@@ -255,6 +280,25 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
: $"已停用算子:{node.DisplayName}");
|
||||
}
|
||||
|
||||
private void ExecuteToNode(PipelineNodeViewModel node)
|
||||
{
|
||||
if (!HasActiveModule || node == null || !PipelineNodes.Contains(node))
|
||||
return;
|
||||
|
||||
SelectedNode = node;
|
||||
ExecutionEndNode = node;
|
||||
PersistActiveModule($"已设置执行截止点:{node.DisplayName}");
|
||||
}
|
||||
|
||||
private void ClearExecutionRange()
|
||||
{
|
||||
if (!HasActiveModule || ExecutionEndNode == null)
|
||||
return;
|
||||
|
||||
ExecutionEndNode = null;
|
||||
PersistActiveModule("已切换为执行全部节点");
|
||||
}
|
||||
|
||||
private void NewPipeline()
|
||||
{
|
||||
if (!HasActiveModule)
|
||||
@@ -262,6 +306,7 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
|
||||
PipelineNodes.Clear();
|
||||
SelectedNode = null;
|
||||
ExecutionEndNode = null;
|
||||
_currentFilePath = null;
|
||||
PipelineFileDisplayName = GetActivePipelineFileDisplayName();
|
||||
PersistActiveModule("已为当前检测模块新建空流水线。");
|
||||
@@ -326,6 +371,7 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
{
|
||||
PipelineNodes.Clear();
|
||||
SelectedNode = null;
|
||||
ExecutionEndNode = null;
|
||||
|
||||
var orderedNodes = (pipeline?.Nodes ?? new List<PipelineNodeModel>())
|
||||
.OrderBy(node => node.Order)
|
||||
@@ -346,6 +392,7 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
}
|
||||
|
||||
SelectedNode = PipelineNodes.FirstOrDefault();
|
||||
UpdateExecutionRangeState();
|
||||
if (string.IsNullOrEmpty(_currentFilePath))
|
||||
PipelineFileDisplayName = GetActivePipelineFileDisplayName();
|
||||
StatusMessage = HasActiveModule
|
||||
@@ -423,7 +470,7 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
try
|
||||
{
|
||||
_logger.Info("[图像链路][CNC] ExecutePreviewAsync:开始执行,节点数={Count}", PipelineNodes.Count);
|
||||
var result = await _executionService.ExecutePipelineAsync(PipelineNodes, sourceImage, null, token);
|
||||
var result = await _executionService.ExecutePipelineAsync(GetNodesInExecutionScope(), sourceImage, null, token);
|
||||
_logger.Info("[图像链路][CNC] ExecutePreviewAsync:执行完成,推送结果图像");
|
||||
_mainViewportService.SetManualImage(result, string.Empty);
|
||||
_eventAggregator?.GetEvent<PipelinePreviewUpdatedEvent>()
|
||||
@@ -459,6 +506,15 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
};
|
||||
}
|
||||
|
||||
private IEnumerable<PipelineNodeViewModel> GetNodesInExecutionScope()
|
||||
{
|
||||
var orderedNodes = PipelineNodes.OrderBy(node => node.Order);
|
||||
if (ExecutionEndNode == null)
|
||||
return orderedNodes;
|
||||
|
||||
return orderedNodes.Where(node => node.Order <= ExecutionEndNode.Order);
|
||||
}
|
||||
|
||||
private string GetActivePipelineName()
|
||||
{
|
||||
if (!HasActiveModule)
|
||||
@@ -492,6 +548,19 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateExecutionRangeState()
|
||||
{
|
||||
if (_executionEndNode != null && !PipelineNodes.Contains(_executionEndNode))
|
||||
_executionEndNode = null;
|
||||
|
||||
var endOrder = _executionEndNode?.Order;
|
||||
foreach (var node in PipelineNodes)
|
||||
{
|
||||
node.IsExecutionEndNode = endOrder.HasValue && node.Order == endOrder.Value;
|
||||
node.IsSkippedByExecutionRange = endOrder.HasValue && node.Order > endOrder.Value;
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectNeighborAfterRemoval(int removedIndex)
|
||||
{
|
||||
if (PipelineNodes.Count == 0)
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
private string _icon;
|
||||
private bool _isExpanded = true;
|
||||
private NodeExecutionState _executionState = NodeExecutionState.Idle;
|
||||
private double _executionProgressPercent;
|
||||
|
||||
/// <summary>执行后缓存的流水线输出图像(仅 InspectionModuleNode)</summary>
|
||||
public BitmapSource ResultImage { get; set; }
|
||||
@@ -72,6 +73,7 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
RaisePropertyChanged(nameof(IsRunningNode));
|
||||
RaisePropertyChanged(nameof(IsSucceededNode));
|
||||
RaisePropertyChanged(nameof(IsFailedNode));
|
||||
RaisePropertyChanged(nameof(IsDelayProgressVisible));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -79,6 +81,21 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
public bool IsRunningNode => ExecutionState == NodeExecutionState.Running;
|
||||
public bool IsSucceededNode => ExecutionState == NodeExecutionState.Succeeded;
|
||||
public bool IsFailedNode => ExecutionState == NodeExecutionState.Failed;
|
||||
public bool IsDelayProgressVisible => IsWaitDelay && IsRunningNode;
|
||||
|
||||
public double ExecutionProgressPercent
|
||||
{
|
||||
get => _executionProgressPercent;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _executionProgressPercent, Math.Clamp(value, 0d, 100d)))
|
||||
{
|
||||
RaisePropertyChanged(nameof(ExecutionProgressText));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string ExecutionProgressText => $"{ExecutionProgressPercent:0}%";
|
||||
|
||||
public bool IsReferencePoint => _model is ReferencePointNode;
|
||||
public bool IsSaveNode => _model is SaveNodeNode;
|
||||
@@ -567,6 +584,9 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
RaisePropertyChanged(nameof(IsRunningNode));
|
||||
RaisePropertyChanged(nameof(IsSucceededNode));
|
||||
RaisePropertyChanged(nameof(IsFailedNode));
|
||||
RaisePropertyChanged(nameof(IsDelayProgressVisible));
|
||||
RaisePropertyChanged(nameof(ExecutionProgressPercent));
|
||||
RaisePropertyChanged(nameof(ExecutionProgressText));
|
||||
}
|
||||
|
||||
private enum MotionAxis
|
||||
|
||||
@@ -23,6 +23,10 @@ namespace XplorePlane.ViewModels
|
||||
|
||||
ICommand ToggleOperatorEnabledCommand { get; }
|
||||
|
||||
ICommand ExecuteToNodeCommand { get; }
|
||||
|
||||
ICommand ClearExecutionRangeCommand { get; }
|
||||
|
||||
ICommand MoveNodeUpCommand { get; }
|
||||
|
||||
ICommand MoveNodeDownCommand { get; }
|
||||
|
||||
@@ -36,6 +36,7 @@ namespace XplorePlane.ViewModels
|
||||
{
|
||||
private readonly IImageProcessingService _imageProcessingService;
|
||||
private string _searchText = string.Empty;
|
||||
private OperatorGroupViewModel _selectedGroup;
|
||||
|
||||
// UI 元数据(分类 + 图标)由 ProcessorUiMetadata 统一提供,保持工具箱与流水线图标一致
|
||||
|
||||
@@ -52,6 +53,12 @@ namespace XplorePlane.ViewModels
|
||||
public ObservableCollection<OperatorDescriptor> FilteredOperators { get; }
|
||||
public ObservableCollection<OperatorGroupViewModel> FilteredGroups { get; }
|
||||
|
||||
public OperatorGroupViewModel SelectedGroup
|
||||
{
|
||||
get => _selectedGroup;
|
||||
set => SetProperty(ref _selectedGroup, value);
|
||||
}
|
||||
|
||||
public string SearchText
|
||||
{
|
||||
get => _searchText;
|
||||
@@ -78,6 +85,7 @@ namespace XplorePlane.ViewModels
|
||||
{
|
||||
FilteredOperators.Clear();
|
||||
FilteredGroups.Clear();
|
||||
SelectedGroup = null;
|
||||
|
||||
var filtered = string.IsNullOrWhiteSpace(SearchText)
|
||||
? AvailableOperators
|
||||
@@ -101,6 +109,8 @@ namespace XplorePlane.ViewModels
|
||||
Operators = new ObservableCollection<OperatorDescriptor>(group)
|
||||
});
|
||||
}
|
||||
|
||||
SelectedGroup = FilteredGroups.FirstOrDefault();
|
||||
}
|
||||
|
||||
private static int GetCategoryOrder(string category) => category switch
|
||||
|
||||
@@ -32,13 +32,14 @@ namespace XplorePlane.ViewModels
|
||||
private PipelineNodeViewModel _selectedNode;
|
||||
private BitmapSource _sourceImage;
|
||||
private BitmapSource _previewImage;
|
||||
private string _pipelineName = "新建流水线";
|
||||
private string _pipelineName = "新建模块";
|
||||
private string _selectedDevice = string.Empty;
|
||||
private bool _isExecuting;
|
||||
private bool _isStatusError;
|
||||
private string _statusMessage = string.Empty;
|
||||
private string _pipelineFileDisplayName = DefaultPipelineFileDisplayName;
|
||||
private string _currentFilePath;
|
||||
private PipelineNodeViewModel _executionEndNode;
|
||||
|
||||
private CancellationTokenSource _executionCts;
|
||||
private CancellationTokenSource _debounceCts;
|
||||
@@ -64,6 +65,8 @@ namespace XplorePlane.ViewModels
|
||||
ReorderOperatorCommand = new DelegateCommand<PipelineReorderArgs>(ReorderOperator);
|
||||
ToggleOperatorEnabledCommand = new DelegateCommand<PipelineNodeViewModel>(ToggleOperatorEnabled);
|
||||
ExecutePipelineCommand = new DelegateCommand(async () => await ExecutePipelineAsync(), () => !IsExecuting && SourceImage != null);
|
||||
ExecuteToNodeCommand = new DelegateCommand<PipelineNodeViewModel>(async node => await ExecuteToNodeAsync(node), CanExecuteToNode);
|
||||
ClearExecutionRangeCommand = new DelegateCommand(async () => await ClearExecutionRangeAsync(), CanClearExecutionRange);
|
||||
CancelExecutionCommand = new DelegateCommand(CancelExecution, () => IsExecuting);
|
||||
NewPipelineCommand = new DelegateCommand(NewPipeline);
|
||||
SavePipelineCommand = new DelegateCommand(async () => await SavePipelineAsync());
|
||||
@@ -162,6 +165,20 @@ namespace XplorePlane.ViewModels
|
||||
private set => SetProperty(ref _pipelineFileDisplayName, value);
|
||||
}
|
||||
|
||||
public PipelineNodeViewModel ExecutionEndNode
|
||||
{
|
||||
get => _executionEndNode;
|
||||
private set
|
||||
{
|
||||
if (SetProperty(ref _executionEndNode, value))
|
||||
{
|
||||
UpdateExecutionRangeState();
|
||||
ExecuteToNodeCommand.RaiseCanExecuteChanged();
|
||||
ClearExecutionRangeCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Commands ──────────────────────────────────────────────────
|
||||
|
||||
public DelegateCommand<string> AddOperatorCommand { get; }
|
||||
@@ -169,6 +186,8 @@ namespace XplorePlane.ViewModels
|
||||
public DelegateCommand<PipelineReorderArgs> ReorderOperatorCommand { get; }
|
||||
public DelegateCommand<PipelineNodeViewModel> ToggleOperatorEnabledCommand { get; }
|
||||
public DelegateCommand ExecutePipelineCommand { get; }
|
||||
public DelegateCommand<PipelineNodeViewModel> ExecuteToNodeCommand { get; }
|
||||
public DelegateCommand ClearExecutionRangeCommand { get; }
|
||||
public DelegateCommand CancelExecutionCommand { get; }
|
||||
public DelegateCommand NewPipelineCommand { get; }
|
||||
public DelegateCommand SavePipelineCommand { get; }
|
||||
@@ -184,6 +203,8 @@ namespace XplorePlane.ViewModels
|
||||
ICommand IPipelineEditorHostViewModel.RemoveOperatorCommand => RemoveOperatorCommand;
|
||||
ICommand IPipelineEditorHostViewModel.ReorderOperatorCommand => ReorderOperatorCommand;
|
||||
ICommand IPipelineEditorHostViewModel.ToggleOperatorEnabledCommand => ToggleOperatorEnabledCommand;
|
||||
ICommand IPipelineEditorHostViewModel.ExecuteToNodeCommand => ExecuteToNodeCommand;
|
||||
ICommand IPipelineEditorHostViewModel.ClearExecutionRangeCommand => ClearExecutionRangeCommand;
|
||||
ICommand IPipelineEditorHostViewModel.MoveNodeUpCommand => MoveNodeUpCommand;
|
||||
ICommand IPipelineEditorHostViewModel.MoveNodeDownCommand => MoveNodeDownCommand;
|
||||
ICommand IPipelineEditorHostViewModel.NewPipelineCommand => NewPipelineCommand;
|
||||
@@ -234,6 +255,7 @@ namespace XplorePlane.ViewModels
|
||||
LoadNodeParameters(node);
|
||||
PipelineNodes.Add(node);
|
||||
SelectedNode = node;
|
||||
UpdateExecutionRangeState();
|
||||
_logger.Info("节点已添加到 PipelineNodes:{Key} ({DisplayName}),当前节点数={Count}",
|
||||
operatorKey, displayName, PipelineNodes.Count);
|
||||
SetInfoStatus($"已添加算子:{displayName}");
|
||||
@@ -247,6 +269,10 @@ namespace XplorePlane.ViewModels
|
||||
var removedIndex = PipelineNodes.IndexOf(node);
|
||||
PipelineNodes.Remove(node);
|
||||
RenumberNodes();
|
||||
if (ReferenceEquals(ExecutionEndNode, node))
|
||||
ExecutionEndNode = null;
|
||||
else
|
||||
UpdateExecutionRangeState();
|
||||
SelectNeighborAfterRemoval(removedIndex);
|
||||
|
||||
SetInfoStatus($"已移除算子:{node.DisplayName}");
|
||||
@@ -260,6 +286,7 @@ namespace XplorePlane.ViewModels
|
||||
if (index <= 0) return;
|
||||
PipelineNodes.Move(index, index - 1);
|
||||
RenumberNodes();
|
||||
UpdateExecutionRangeState();
|
||||
TriggerDebouncedExecution();
|
||||
}
|
||||
|
||||
@@ -270,6 +297,7 @@ namespace XplorePlane.ViewModels
|
||||
if (index < 0 || index >= PipelineNodes.Count - 1) return;
|
||||
PipelineNodes.Move(index, index + 1);
|
||||
RenumberNodes();
|
||||
UpdateExecutionRangeState();
|
||||
TriggerDebouncedExecution();
|
||||
}
|
||||
|
||||
@@ -287,6 +315,7 @@ namespace XplorePlane.ViewModels
|
||||
PipelineNodes.RemoveAt(oldIndex);
|
||||
PipelineNodes.Insert(newIndex, node);
|
||||
RenumberNodes();
|
||||
UpdateExecutionRangeState();
|
||||
SelectedNode = node;
|
||||
SetInfoStatus($"已调整算子顺序:{node.DisplayName}");
|
||||
TriggerDebouncedExecution();
|
||||
@@ -304,6 +333,34 @@ namespace XplorePlane.ViewModels
|
||||
TriggerDebouncedExecution();
|
||||
}
|
||||
|
||||
private bool CanExecuteToNode(PipelineNodeViewModel node) =>
|
||||
node != null && PipelineNodes.Contains(node) && !IsExecuting && SourceImage != null;
|
||||
|
||||
private async Task ExecuteToNodeAsync(PipelineNodeViewModel node)
|
||||
{
|
||||
if (!CanExecuteToNode(node))
|
||||
return;
|
||||
|
||||
SelectedNode = node;
|
||||
ExecutionEndNode = node;
|
||||
await ExecutePipelineAsync();
|
||||
}
|
||||
|
||||
private bool CanClearExecutionRange() =>
|
||||
ExecutionEndNode != null && !IsExecuting;
|
||||
|
||||
private async Task ClearExecutionRangeAsync()
|
||||
{
|
||||
if (ExecutionEndNode == null)
|
||||
return;
|
||||
|
||||
ExecutionEndNode = null;
|
||||
SetInfoStatus("已切换为执行全部节点");
|
||||
|
||||
if (SourceImage != null)
|
||||
await ExecutePipelineAsync();
|
||||
}
|
||||
|
||||
private void RenumberNodes()
|
||||
{
|
||||
for (int i = 0; i < PipelineNodes.Count; i++)
|
||||
@@ -367,10 +424,15 @@ namespace XplorePlane.ViewModels
|
||||
_executionCts?.Cancel();
|
||||
_executionCts = new CancellationTokenSource();
|
||||
var token = _executionCts.Token;
|
||||
var executionNodes = GetNodesInExecutionScope()
|
||||
.Where(n => n.IsEnabled)
|
||||
.OrderBy(n => n.Order)
|
||||
.ToList();
|
||||
|
||||
IsExecuting = true;
|
||||
SetInfoStatus("正在执行流水线...");
|
||||
_logger.Info("[图像链路] ExecutePipelineAsync:开始执行,节点数={Count}", PipelineNodes.Count);
|
||||
SetInfoStatus(BuildExecutionStartMessage(executionNodes.Count));
|
||||
_logger.Info("[图像链路] ExecutePipelineAsync:开始执行,范围节点数={Count},截止节点={Node}",
|
||||
executionNodes.Count, ExecutionEndNode?.DisplayName ?? "<all>");
|
||||
|
||||
try
|
||||
{
|
||||
@@ -378,10 +440,10 @@ namespace XplorePlane.ViewModels
|
||||
SetInfoStatus($"执行中:{p.CurrentOperator} ({p.CurrentStep}/{p.TotalSteps})"));
|
||||
|
||||
var result = await _executionService.ExecutePipelineAsync(
|
||||
PipelineNodes, SourceImage, progress, token);
|
||||
executionNodes, SourceImage, progress, token);
|
||||
|
||||
PreviewImage = result;
|
||||
SetInfoStatus("流水线执行完成");
|
||||
SetInfoStatus(BuildExecutionCompletedMessage(executionNodes.Count));
|
||||
_logger.Info("[图像链路] ExecutePipelineAsync:执行完成,准备发布 PipelinePreviewUpdatedEvent");
|
||||
PublishPipelinePreviewUpdated(result, StatusMessage);
|
||||
}
|
||||
@@ -408,7 +470,7 @@ namespace XplorePlane.ViewModels
|
||||
|
||||
private bool TryReportInvalidParameters()
|
||||
{
|
||||
var firstInvalidNode = PipelineNodes
|
||||
var firstInvalidNode = GetNodesInExecutionScope()
|
||||
.Where(n => n.IsEnabled)
|
||||
.OrderBy(n => n.Order)
|
||||
.FirstOrDefault(n => n.Parameters.Any(p => !p.IsValueValid));
|
||||
@@ -549,6 +611,7 @@ namespace XplorePlane.ViewModels
|
||||
{
|
||||
PipelineNodes.Clear();
|
||||
SelectedNode = null;
|
||||
ExecutionEndNode = null;
|
||||
PipelineName = "新建流水线";
|
||||
PreviewImage = null;
|
||||
_currentFilePath = null;
|
||||
@@ -583,7 +646,7 @@ namespace XplorePlane.ViewModels
|
||||
|
||||
var dialog = new SaveFileDialog
|
||||
{
|
||||
Filter = "XP 模块流水线 (*.xpm)|*.xpm",
|
||||
Filter = "XP 模块 (*.xpm)|*.xpm",
|
||||
DefaultExt = ".xpm",
|
||||
AddExtension = true,
|
||||
FileName = PipelineName,
|
||||
@@ -635,7 +698,7 @@ namespace XplorePlane.ViewModels
|
||||
{
|
||||
var dialog = new OpenFileDialog
|
||||
{
|
||||
Filter = "XP 模块流水线 (*.xpm)|*.xpm",
|
||||
Filter = "XP 模块 (*.xpm)|*.xpm",
|
||||
DefaultExt = ".xpm",
|
||||
InitialDirectory = GetPipelineDirectory()
|
||||
};
|
||||
@@ -648,6 +711,7 @@ namespace XplorePlane.ViewModels
|
||||
|
||||
PipelineNodes.Clear();
|
||||
SelectedNode = null;
|
||||
ExecutionEndNode = null;
|
||||
|
||||
PipelineName = model.Name;
|
||||
SelectedDevice = model.DeviceId;
|
||||
@@ -676,6 +740,8 @@ namespace XplorePlane.ViewModels
|
||||
PipelineNodes.Add(node);
|
||||
}
|
||||
|
||||
UpdateExecutionRangeState();
|
||||
|
||||
_logger.Info("流水线已加载:{Name},节点数={Count}", model.Name, PipelineNodes.Count);
|
||||
SetInfoStatus($"已加载流水线:{model.Name}({PipelineNodes.Count} 个节点)");
|
||||
}
|
||||
@@ -704,6 +770,44 @@ namespace XplorePlane.ViewModels
|
||||
};
|
||||
}
|
||||
|
||||
private System.Collections.Generic.IEnumerable<PipelineNodeViewModel> GetNodesInExecutionScope()
|
||||
{
|
||||
var orderedNodes = PipelineNodes.OrderBy(n => n.Order);
|
||||
if (ExecutionEndNode == null)
|
||||
return orderedNodes;
|
||||
|
||||
return orderedNodes.Where(n => n.Order <= ExecutionEndNode.Order);
|
||||
}
|
||||
|
||||
private void UpdateExecutionRangeState()
|
||||
{
|
||||
if (_executionEndNode != null && !PipelineNodes.Contains(_executionEndNode))
|
||||
_executionEndNode = null;
|
||||
|
||||
var endOrder = _executionEndNode?.Order;
|
||||
foreach (var node in PipelineNodes)
|
||||
{
|
||||
node.IsExecutionEndNode = endOrder.HasValue && node.Order == endOrder.Value;
|
||||
node.IsSkippedByExecutionRange = endOrder.HasValue && node.Order > endOrder.Value;
|
||||
}
|
||||
}
|
||||
|
||||
private string BuildExecutionStartMessage(int executionCount)
|
||||
{
|
||||
if (ExecutionEndNode == null)
|
||||
return "正在执行流水线...";
|
||||
|
||||
return $"正在执行到“{ExecutionEndNode.DisplayName}” ({executionCount} 个有效节点)...";
|
||||
}
|
||||
|
||||
private string BuildExecutionCompletedMessage(int executionCount)
|
||||
{
|
||||
if (ExecutionEndNode == null)
|
||||
return "流水线执行完成";
|
||||
|
||||
return $"已执行到“{ExecutionEndNode.DisplayName}” ({executionCount} 个有效节点)";
|
||||
}
|
||||
|
||||
private static string GetPipelineDirectory()
|
||||
{
|
||||
var dir = Path.Combine(
|
||||
|
||||
@@ -11,6 +11,8 @@ namespace XplorePlane.ViewModels
|
||||
private int _order;
|
||||
private bool _isSelected;
|
||||
private bool _isEnabled = true;
|
||||
private bool _isExecutionEndNode;
|
||||
private bool _isSkippedByExecutionRange;
|
||||
|
||||
public PipelineNodeViewModel(string operatorKey, string displayName, string iconPath = null)
|
||||
{
|
||||
@@ -51,9 +53,49 @@ namespace XplorePlane.ViewModels
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => _isEnabled;
|
||||
set => SetProperty(ref _isEnabled, value);
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _isEnabled, value))
|
||||
RaisePropertyChanged(nameof(NodeStateText));
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<ProcessorParameterVM> Parameters { get; }
|
||||
|
||||
public bool IsExecutionEndNode
|
||||
{
|
||||
get => _isExecutionEndNode;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _isExecutionEndNode, value))
|
||||
RaisePropertyChanged(nameof(NodeStateText));
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSkippedByExecutionRange
|
||||
{
|
||||
get => _isSkippedByExecutionRange;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _isSkippedByExecutionRange, value))
|
||||
RaisePropertyChanged(nameof(NodeStateText));
|
||||
}
|
||||
}
|
||||
|
||||
public string NodeStateText
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsExecutionEndNode && !IsEnabled)
|
||||
return "执行到此(停用)";
|
||||
if (IsExecutionEndNode)
|
||||
return "执行到此";
|
||||
if (!IsEnabled)
|
||||
return "已停用";
|
||||
if (IsSkippedByExecutionRange)
|
||||
return "未参与本次执行";
|
||||
return "已启用";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,15 @@ namespace XplorePlane.ViewModels
|
||||
public string ParameterType { get; }
|
||||
public bool HasOptions => Options is { Length: > 0 };
|
||||
public bool IsBool => ParameterType == "bool";
|
||||
public bool IsTextInput => !IsBool && !HasOptions;
|
||||
public bool IsNumeric => ParameterType is "int" or "double";
|
||||
public bool HasRange => IsNumeric && MinValue != null && MaxValue != null;
|
||||
public bool IsSliderInput => HasRange;
|
||||
public bool IsTextInput => !IsBool && !HasOptions && !IsSliderInput;
|
||||
|
||||
public double SliderMinimum => TryConvertToDouble(MinValue, out var minValue) ? minValue : 0d;
|
||||
public double SliderMaximum => TryConvertToDouble(MaxValue, out var maxValue) ? maxValue : 100d;
|
||||
public double SliderTickFrequency => ResolveTickFrequency();
|
||||
public bool IsIntegerSlider => ParameterType == "int";
|
||||
|
||||
public bool IsValueValid
|
||||
{
|
||||
@@ -60,10 +68,46 @@ namespace XplorePlane.ViewModels
|
||||
RaisePropertyChanged(nameof(Value));
|
||||
RaisePropertyChanged(nameof(BoolValue));
|
||||
RaisePropertyChanged(nameof(SelectedOption));
|
||||
RaisePropertyChanged(nameof(SliderValue));
|
||||
RaisePropertyChanged(nameof(DisplayValueText));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public double SliderValue
|
||||
{
|
||||
get => TryConvertToDouble(_value, out var sliderValue) ? sliderValue : SliderMinimum;
|
||||
set
|
||||
{
|
||||
if (!IsSliderInput)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Value = ParameterType == "int"
|
||||
? (object)Convert.ToInt32(Math.Round(value, MidpointRounding.AwayFromZero), CultureInfo.InvariantCulture)
|
||||
: Math.Round(value, ResolveDecimalPlaces(), MidpointRounding.AwayFromZero);
|
||||
}
|
||||
}
|
||||
|
||||
public string DisplayValueText
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ParameterType == "int" && TryConvertToInt(_value, out var intValue))
|
||||
{
|
||||
return intValue.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
if (ParameterType == "double" && TryConvertToDouble(_value, out var doubleValue))
|
||||
{
|
||||
return doubleValue.ToString($"F{ResolveDecimalPlaces()}", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
return Convert.ToString(_value, CultureInfo.InvariantCulture) ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public bool BoolValue
|
||||
{
|
||||
get => ParameterType == "bool" && TryConvertToBool(_value, out var boolValue) && boolValue;
|
||||
@@ -154,6 +198,58 @@ namespace XplorePlane.ViewModels
|
||||
return true;
|
||||
}
|
||||
|
||||
private double ResolveTickFrequency()
|
||||
{
|
||||
if (!HasRange)
|
||||
{
|
||||
return 1d;
|
||||
}
|
||||
|
||||
if (ParameterType == "int")
|
||||
{
|
||||
return 1d;
|
||||
}
|
||||
|
||||
double range = SliderMaximum - SliderMinimum;
|
||||
if (range <= 1d)
|
||||
{
|
||||
return 0.01d;
|
||||
}
|
||||
|
||||
if (range <= 10d)
|
||||
{
|
||||
return 0.1d;
|
||||
}
|
||||
|
||||
return 1d;
|
||||
}
|
||||
|
||||
private int ResolveDecimalPlaces()
|
||||
{
|
||||
if (ParameterType == "int")
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
double tick = SliderTickFrequency;
|
||||
if (tick >= 1d)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (tick >= 0.1d)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (tick >= 0.01d)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
private static string NormalizeNumericText(string value)
|
||||
{
|
||||
return value.Trim().TrimEnd('、', ',', ',', '。', '.', ';', ';', ':', ':');
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace XplorePlane.ViewModels
|
||||
{
|
||||
public class MainViewModel : BindableBase
|
||||
{
|
||||
private const double CncEditorHostWidth = 402d;
|
||||
private const double CncEditorHostWidth = 452d;
|
||||
|
||||
private readonly ILoggerService _logger;
|
||||
private readonly IContainerProvider _containerProvider;
|
||||
@@ -205,7 +205,7 @@ namespace XplorePlane.ViewModels
|
||||
|
||||
|
||||
LoadImageCommand = new DelegateCommand(ExecuteLoadImage);
|
||||
OpenPipelineEditorCommand = new DelegateCommand(() => ShowWindow(new Views.PipelineEditorWindow(), "流水线编辑器"));
|
||||
|
||||
OpenCncEditorCommand = new DelegateCommand(ExecuteOpenCncEditor);
|
||||
OpenMatrixEditorCommand = new DelegateCommand(() => ShowWindow(new Views.Cnc.MatrixEditorWindow(), "矩阵编排"));
|
||||
OpenToolboxCommand = new DelegateCommand(ExecuteOpenToolbox);
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
xmlns:views="clr-namespace:XplorePlane.Views"
|
||||
xmlns:vm="clr-namespace:XplorePlane.ViewModels.Cnc"
|
||||
d:DesignHeight="760"
|
||||
d:DesignWidth="402"
|
||||
d:DesignWidth="452"
|
||||
prism:ViewModelLocator.AutoWireViewModel="True"
|
||||
mc:Ignorable="d">
|
||||
|
||||
@@ -96,11 +96,26 @@
|
||||
<Setter Property="FontFamily" Value="{StaticResource UiFont}" />
|
||||
<Setter Property="FontSize" Value="11" />
|
||||
</Style>
|
||||
<Style x:Key="TreeToolbarButtonCompact" TargetType="Button" BasedOn="{StaticResource TreeToolbarButton}">
|
||||
<Setter Property="Width" Value="28" />
|
||||
<Setter Property="Height" Value="28" />
|
||||
<Setter Property="MinWidth" Value="28" />
|
||||
<Setter Property="Margin" Value="0,0,4,4" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="FontSize" Value="10.5" />
|
||||
</Style>
|
||||
<Style x:Key="TreeToolbarIcon" TargetType="Image">
|
||||
<Setter Property="Width" Value="14" />
|
||||
<Setter Property="Height" Value="14" />
|
||||
<Setter Property="Margin" Value="0" />
|
||||
<Setter Property="Stretch" Value="Uniform" />
|
||||
<Setter Property="VerticalAlignment" Value="Center" />
|
||||
</Style>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Border
|
||||
Width="402"
|
||||
MinWidth="402"
|
||||
Width="452"
|
||||
MinWidth="452"
|
||||
HorizontalAlignment="Left"
|
||||
Background="{StaticResource PanelBg}"
|
||||
BorderBrush="{StaticResource PanelBorder}"
|
||||
@@ -108,7 +123,7 @@
|
||||
CornerRadius="4">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="200" />
|
||||
<ColumnDefinition Width="180" />
|
||||
<ColumnDefinition Width="1" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
@@ -125,24 +140,109 @@
|
||||
Background="{StaticResource HeaderBg}"
|
||||
BorderBrush="{StaticResource SeparatorBrush}"
|
||||
BorderThickness="0,0,0,1">
|
||||
<WrapPanel>
|
||||
<StackPanel>
|
||||
<WrapPanel/>
|
||||
<WrapPanel Margin="0,4,0,0" Visibility="Collapsed">
|
||||
<Button
|
||||
Command="{Binding NewProgramCommand}"
|
||||
Content="新建"
|
||||
Style="{StaticResource TreeToolbarButton}" />
|
||||
Command="{Binding InsertReferencePointCommand}"
|
||||
Content="参考点"
|
||||
Style="{StaticResource TreeToolbarButtonCompact}" />
|
||||
<Button
|
||||
Command="{Binding SaveProgramCommand}"
|
||||
Content="保存"
|
||||
Style="{StaticResource TreeToolbarButton}" />
|
||||
Command="{Binding InsertSavePositionCommand}"
|
||||
Content="添加位置"
|
||||
Style="{StaticResource TreeToolbarButtonCompact}" />
|
||||
<Button
|
||||
Command="{Binding LoadProgramCommand}"
|
||||
Content="加载"
|
||||
Style="{StaticResource TreeToolbarButton}" />
|
||||
Command="{Binding InsertInspectionModuleCommand}"
|
||||
Content="检测模块"
|
||||
Style="{StaticResource TreeToolbarButtonCompact}" />
|
||||
<Button
|
||||
Command="{Binding ExportCsvCommand}"
|
||||
Content="导出"
|
||||
Style="{StaticResource TreeToolbarButton}" />
|
||||
Command="{Binding InsertInspectionMarkerCommand}"
|
||||
Content="检测标记"
|
||||
Style="{StaticResource TreeToolbarButtonCompact}" />
|
||||
<Button
|
||||
Command="{Binding InsertPauseDialogCommand}"
|
||||
Content="消息弹窗"
|
||||
Style="{StaticResource TreeToolbarButtonCompact}" />
|
||||
<Button
|
||||
Command="{Binding InsertWaitDelayCommand}"
|
||||
Content="插入等待"
|
||||
Style="{StaticResource TreeToolbarButtonCompact}" />
|
||||
<Button
|
||||
Command="{Binding InsertCompleteProgramCommand}"
|
||||
Content="完成"
|
||||
Style="{StaticResource TreeToolbarButtonCompact}" />
|
||||
</WrapPanel>
|
||||
<WrapPanel Margin="0,4,0,0">
|
||||
<WrapPanel.Resources>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</Style>
|
||||
</WrapPanel.Resources>
|
||||
<Button
|
||||
Command="{Binding InsertReferencePointCommand}"
|
||||
Style="{StaticResource TreeToolbarButtonCompact}"
|
||||
ToolTip="参考点">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image Source="/Assets/Icons/reference.png" Style="{StaticResource TreeToolbarIcon}" />
|
||||
<TextBlock Text="参考点" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button
|
||||
Command="{Binding InsertSavePositionCommand}"
|
||||
Style="{StaticResource TreeToolbarButtonCompact}"
|
||||
ToolTip="添加位置">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image Source="/Assets/Icons/add-pos.png" Style="{StaticResource TreeToolbarIcon}" />
|
||||
<TextBlock Text="添加位置" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button
|
||||
Command="{Binding InsertInspectionModuleCommand}"
|
||||
Style="{StaticResource TreeToolbarButtonCompact}"
|
||||
ToolTip="检测模块">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image Source="/Assets/Icons/Module.png" Style="{StaticResource TreeToolbarIcon}" />
|
||||
<TextBlock Text="检测模块" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button
|
||||
Command="{Binding InsertInspectionMarkerCommand}"
|
||||
Style="{StaticResource TreeToolbarButtonCompact}"
|
||||
ToolTip="检测标记">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image Source="/Assets/Icons/mark.png" Style="{StaticResource TreeToolbarIcon}" />
|
||||
<TextBlock Text="检测标记" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button
|
||||
Command="{Binding InsertPauseDialogCommand}"
|
||||
Style="{StaticResource TreeToolbarButtonCompact}"
|
||||
ToolTip="消息弹窗">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image Source="/Assets/Icons/message.png" Style="{StaticResource TreeToolbarIcon}" />
|
||||
<TextBlock Text="消息弹窗" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button
|
||||
Command="{Binding InsertWaitDelayCommand}"
|
||||
Style="{StaticResource TreeToolbarButtonCompact}"
|
||||
ToolTip="插入等待">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image Source="/Assets/Icons/wait.png" Style="{StaticResource TreeToolbarIcon}" />
|
||||
<TextBlock Text="插入等待" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button
|
||||
Command="{Binding InsertCompleteProgramCommand}"
|
||||
Style="{StaticResource TreeToolbarButtonCompact}"
|
||||
ToolTip="完成">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Image Source="/Assets/Icons/finish.png" Style="{StaticResource TreeToolbarIcon}" />
|
||||
<TextBlock Text="完成" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</WrapPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<TreeView
|
||||
@@ -180,6 +280,7 @@
|
||||
FontFamily="{StaticResource UiFont}"
|
||||
FontSize="12"
|
||||
FontWeight="SemiBold"
|
||||
x:Name="ProgramRootNameText"
|
||||
Text="{Binding DisplayName}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
</StackPanel>
|
||||
@@ -188,6 +289,7 @@
|
||||
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=TreeViewItem}}" Value="True">
|
||||
<Setter TargetName="ProgramRootCard" Property="Background" Value="#E7F1FB" />
|
||||
<Setter TargetName="ProgramRootCard" Property="BorderBrush" Value="#9FC6E8" />
|
||||
<Setter TargetName="ProgramRootNameText" Property="Foreground" Value="#111111" />
|
||||
</DataTrigger>
|
||||
</DataTemplate.Triggers>
|
||||
</HierarchicalDataTemplate>
|
||||
@@ -205,6 +307,10 @@
|
||||
BorderThickness="1"
|
||||
CornerRadius="4">
|
||||
<Grid x:Name="NodeRoot" MinHeight="23">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="15" />
|
||||
<ColumnDefinition Width="20" />
|
||||
@@ -212,7 +318,7 @@
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid Grid.Column="0">
|
||||
<Grid Grid.RowSpan="2" Grid.Column="0">
|
||||
<Border
|
||||
x:Name="ChildStem"
|
||||
Width="1"
|
||||
@@ -230,6 +336,7 @@
|
||||
</Grid>
|
||||
|
||||
<Border
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
Width="16"
|
||||
Height="16"
|
||||
@@ -246,6 +353,7 @@
|
||||
|
||||
<TextBlock
|
||||
x:Name="NodeNameText"
|
||||
Grid.Row="0"
|
||||
Grid.Column="2"
|
||||
Margin="3,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
@@ -257,6 +365,7 @@
|
||||
|
||||
<StackPanel
|
||||
x:Name="NodeActions"
|
||||
Grid.Row="0"
|
||||
Grid.Column="3"
|
||||
Margin="0,0,2,0"
|
||||
VerticalAlignment="Center"
|
||||
@@ -275,6 +384,31 @@
|
||||
FontSize="10"
|
||||
ToolTip="删除" />
|
||||
</StackPanel>
|
||||
<Grid
|
||||
x:Name="WaitDelayProgressHost"
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="3,2,6,1"
|
||||
Visibility="Collapsed">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ProgressBar
|
||||
Height="6"
|
||||
Minimum="0"
|
||||
Maximum="100"
|
||||
Value="{Binding ExecutionProgressPercent}" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="6,-4,0,0"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="{StaticResource UiFont}"
|
||||
FontSize="10"
|
||||
Foreground="#444444"
|
||||
Text="{Binding ExecutionProgressText}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Border>
|
||||
<DataTemplate.Triggers>
|
||||
@@ -287,22 +421,30 @@
|
||||
<Setter TargetName="NodeActions" Property="Visibility" Value="Visible" />
|
||||
<Setter TargetName="NodeCard" Property="Background" Value="#DCEEFF" />
|
||||
<Setter TargetName="NodeCard" Property="BorderBrush" Value="#71A9DB" />
|
||||
<Setter TargetName="NodeNameText" Property="Foreground" Value="#1F2D3D" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding ExecutionState}" Value="Running">
|
||||
<Setter TargetName="NodeCard" Property="Background" Value="#FF1E6FD9" />
|
||||
<Setter TargetName="NodeCard" Property="BorderBrush" Value="#FF1E6FD9" />
|
||||
<Setter TargetName="NodeNameText" Property="Foreground" Value="White" />
|
||||
<Setter TargetName="NodeCard" Property="Background" Value="#FFD54F" />
|
||||
<Setter TargetName="NodeCard" Property="BorderBrush" Value="#C89B00" />
|
||||
<Setter TargetName="NodeNameText" Property="Foreground" Value="#1F1F1F" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding ExecutionState}" Value="Succeeded">
|
||||
<Setter TargetName="NodeCard" Property="Background" Value="#FF2E7D32" />
|
||||
<Setter TargetName="NodeCard" Property="BorderBrush" Value="#FF2E7D32" />
|
||||
<Setter TargetName="NodeCard" Property="BorderBrush" Value="#FF1B5E20" />
|
||||
<Setter TargetName="NodeNameText" Property="Foreground" Value="White" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding ExecutionState}" Value="Failed">
|
||||
<Setter TargetName="NodeCard" Property="Background" Value="#FFC62828" />
|
||||
<Setter TargetName="NodeCard" Property="BorderBrush" Value="#FFC62828" />
|
||||
<Setter TargetName="NodeCard" Property="BorderBrush" Value="#FF8E0000" />
|
||||
<Setter TargetName="NodeNameText" Property="Foreground" Value="White" />
|
||||
</DataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsWaitDelay}" Value="True" />
|
||||
<Condition Binding="{Binding IsRunningNode}" Value="True" />
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter TargetName="WaitDelayProgressHost" Property="Visibility" Value="Visible" />
|
||||
</MultiDataTrigger>
|
||||
</DataTemplate.Triggers>
|
||||
</HierarchicalDataTemplate>
|
||||
</TreeView.Resources>
|
||||
@@ -469,6 +611,20 @@
|
||||
<StackPanel Margin="10,8,10,6">
|
||||
<TextBlock Style="{StaticResource LabelStyle}" Text="延时 (ms)" />
|
||||
<TextBox Style="{StaticResource EditorBox}" Text="{Binding SelectedNode.DelayMilliseconds, UpdateSourceTrigger=LostFocus}" />
|
||||
<ProgressBar
|
||||
Height="8"
|
||||
Margin="0,2,0,0"
|
||||
Minimum="0"
|
||||
Maximum="100"
|
||||
Value="{Binding SelectedNode.ExecutionProgressPercent}"
|
||||
Visibility="{Binding SelectedNode.IsDelayProgressVisible, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<TextBlock
|
||||
Margin="0,4,0,0"
|
||||
FontFamily="{StaticResource UiFont}"
|
||||
FontSize="10"
|
||||
Foreground="#666666"
|
||||
Text="{Binding SelectedNode.ExecutionProgressText}"
|
||||
Visibility="{Binding SelectedNode.IsDelayProgressVisible, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
|
||||
@@ -19,14 +19,6 @@ namespace XplorePlane.Views.Cnc
|
||||
/// </summary>
|
||||
public partial class CncPageView : UserControl
|
||||
{
|
||||
private static readonly Brush SelectedNodeBackground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#E7F0F7"));
|
||||
private static readonly Brush SelectedNodeBorder = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#9CB9D1"));
|
||||
private static readonly Brush HoverNodeBackground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F6FAFC"));
|
||||
private static readonly Brush HoverNodeBorder = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#D7E4EE"));
|
||||
private static readonly Brush SelectedNodeForeground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#1F4E79"));
|
||||
private static readonly Brush DefaultNodeForeground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#202020"));
|
||||
private static readonly Brush TransparentBrush = Brushes.Transparent;
|
||||
|
||||
private CncInspectionModulePipelineViewModel _inspectionModulePipelineViewModel;
|
||||
private readonly Dictionary<TextBox, Label> _textDisplayLabels = new();
|
||||
private readonly Dictionary<CheckBox, Label> _checkDisplayLabels = new();
|
||||
@@ -186,24 +178,9 @@ namespace XplorePlane.Views.Cnc
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.IsSelected)
|
||||
{
|
||||
card.Background = SelectedNodeBackground;
|
||||
card.BorderBrush = SelectedNodeBorder;
|
||||
ApplyNodeTextForeground(card, SelectedNodeForeground);
|
||||
}
|
||||
else if (card.IsMouseOver)
|
||||
{
|
||||
card.Background = HoverNodeBackground;
|
||||
card.BorderBrush = HoverNodeBorder;
|
||||
ApplyNodeTextForeground(card, DefaultNodeForeground);
|
||||
}
|
||||
else
|
||||
{
|
||||
card.Background = TransparentBrush;
|
||||
card.BorderBrush = TransparentBrush;
|
||||
ApplyNodeTextForeground(card, DefaultNodeForeground);
|
||||
}
|
||||
card.ClearValue(Border.BackgroundProperty);
|
||||
card.ClearValue(Border.BorderBrushProperty);
|
||||
ClearNodeTextForeground(card);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,14 +295,11 @@ namespace XplorePlane.Views.Cnc
|
||||
panel.Children.Insert(index + 1, companionControl);
|
||||
}
|
||||
|
||||
private static void ApplyNodeTextForeground(Border card, Brush foreground)
|
||||
private static void ClearNodeTextForeground(Border card)
|
||||
{
|
||||
foreach (var textBlock in FindVisualDescendants<TextBlock>(card))
|
||||
{
|
||||
if (textBlock.Visibility == Visibility.Visible)
|
||||
{
|
||||
textBlock.Foreground = foreground;
|
||||
}
|
||||
textBlock.ClearValue(TextBlock.ForegroundProperty);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
<Setter Property="FontWeight" Value="Bold" />
|
||||
<Setter Property="Foreground" Value="#1c1c1b" />
|
||||
<Setter Property="BorderBrush" Value="#cdcbcb" />
|
||||
<Setter Property="Padding" Value="4,6,4,4" />
|
||||
<Setter Property="Padding" Value="3,4,3,3" />
|
||||
</Style>
|
||||
</UserControl.Resources>
|
||||
|
||||
@@ -145,7 +145,7 @@
|
||||
<!-- 右侧:算子选择 + 参数配置 -->
|
||||
<Border Grid.Column="3" Style="{StaticResource PanelBorderStyle}">
|
||||
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Margin="10,8,10,10">
|
||||
<StackPanel Margin="8,6,8,8">
|
||||
|
||||
<GroupBox Margin="0,0,0,8" Header="选择算子">
|
||||
<ComboBox
|
||||
@@ -204,4 +204,4 @@
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
|
||||
@@ -100,6 +100,7 @@
|
||||
<TabControl x:Name="ToolboxListBox"
|
||||
Margin="8"
|
||||
ItemContainerStyle="{StaticResource OperatorToolboxTabItemStyle}"
|
||||
SelectedItem="{Binding SelectedGroup, Mode=TwoWay}"
|
||||
ItemsSource="{Binding FilteredGroups}">
|
||||
<TabControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:views="clr-namespace:XplorePlane.Views"
|
||||
Title="算子工具箱"
|
||||
Width="500" Height="560"
|
||||
Width="460" Height="540"
|
||||
MinWidth="420" MinHeight="500"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
ShowInTaskbar="False"
|
||||
WindowStyle="None"
|
||||
|
||||
@@ -80,25 +80,25 @@
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
Command="{Binding NewPipelineCommand}"
|
||||
Content="新建"
|
||||
Content="新建配方"
|
||||
Style="{StaticResource ToolbarBtn}"
|
||||
ToolTip="新建流水线" />
|
||||
ToolTip="新建配方" />
|
||||
<Button
|
||||
Command="{Binding SavePipelineCommand}"
|
||||
Content="保存"
|
||||
Content="保存配方"
|
||||
Style="{StaticResource ToolbarBtn}"
|
||||
ToolTip="保存当前流水线" />
|
||||
ToolTip="保存当前配方" />
|
||||
<Button
|
||||
Width="64"
|
||||
Command="{Binding SaveAsPipelineCommand}"
|
||||
Content="另存为"
|
||||
Style="{StaticResource ToolbarBtn}"
|
||||
ToolTip="另存当前流水线" />
|
||||
ToolTip="另存当前配方" />
|
||||
<Button
|
||||
Command="{Binding LoadPipelineCommand}"
|
||||
Content="加载"
|
||||
Content="加载配方"
|
||||
Style="{StaticResource ToolbarBtn}"
|
||||
ToolTip="加载流水线" />
|
||||
ToolTip="加载配方" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
@@ -132,10 +132,20 @@
|
||||
x:Name="NodeContainer"
|
||||
Margin="2"
|
||||
Padding="2"
|
||||
Tag="{Binding DataContext, ElementName=RootControl}"
|
||||
Background="Transparent"
|
||||
BorderBrush="Transparent"
|
||||
BorderThickness="1"
|
||||
CornerRadius="3">
|
||||
<Border.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="执行到此处"
|
||||
Command="{Binding PlacementTarget.Tag.ExecuteToNodeCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
|
||||
CommandParameter="{Binding}" />
|
||||
<MenuItem Header="执行全部"
|
||||
Command="{Binding PlacementTarget.Tag.ClearExecutionRangeCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
|
||||
</ContextMenu>
|
||||
</Border.ContextMenu>
|
||||
<Grid x:Name="NodeRoot" MinHeight="48">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="44" />
|
||||
@@ -193,7 +203,7 @@
|
||||
FontFamily="{StaticResource UiFont}"
|
||||
FontSize="10"
|
||||
Foreground="#6E6E6E"
|
||||
Text="已启用" />
|
||||
Text="{Binding NodeStateText}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
@@ -209,13 +219,37 @@
|
||||
<Setter TargetName="IconBorder" Property="BorderBrush" Value="{StaticResource DisabledNodeLine}" />
|
||||
<Setter TargetName="IconBorder" Property="Background" Value="#ECECEC" />
|
||||
<Setter TargetName="NodeTitle" Property="Foreground" Value="{StaticResource DisabledNodeText}" />
|
||||
<Setter TargetName="NodeState" Property="Text" Value="已停用" />
|
||||
<Setter TargetName="NodeState" Property="Foreground" Value="#9A6767" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding IsExecutionEndNode}" Value="True">
|
||||
<Setter TargetName="NodeContainer" Property="Background" Value="#E8F6EA" />
|
||||
<Setter TargetName="NodeContainer" Property="BorderBrush" Value="#4F9D69" />
|
||||
<Setter TargetName="IconBorder" Property="Background" Value="#E8F6EA" />
|
||||
<Setter TargetName="IconBorder" Property="BorderBrush" Value="#4F9D69" />
|
||||
<Setter TargetName="NodeState" Property="Foreground" Value="#2E7D32" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding IsSkippedByExecutionRange}" Value="True">
|
||||
<Setter TargetName="NodeContainer" Property="Background" Value="#FAFAFA" />
|
||||
<Setter TargetName="NodeContainer" Property="Opacity" Value="0.72" />
|
||||
<Setter TargetName="TopLine" Property="Stroke" Value="#D0D0D0" />
|
||||
<Setter TargetName="BottomLine" Property="Stroke" Value="#D0D0D0" />
|
||||
<Setter TargetName="IconBorder" Property="BorderBrush" Value="#C8C8C8" />
|
||||
<Setter TargetName="IconBorder" Property="Background" Value="#F4F4F4" />
|
||||
<Setter TargetName="NodeTitle" Property="Foreground" Value="#909090" />
|
||||
<Setter TargetName="NodeState" Property="Foreground" Value="#A0A0A0" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}, Path=IsSelected}" Value="True">
|
||||
<Setter TargetName="NodeContainer" Property="Background" Value="#D9ECFF" />
|
||||
<Setter TargetName="NodeContainer" Property="BorderBrush" Value="#5B9BD5" />
|
||||
</DataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsExecutionEndNode}" Value="True" />
|
||||
<Condition Binding="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}, Path=IsSelected}" Value="True" />
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter TargetName="NodeContainer" Property="Background" Value="#DFF0E3" />
|
||||
<Setter TargetName="NodeContainer" Property="BorderBrush" Value="#3F8A58" />
|
||||
</MultiDataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsEnabled}" Value="False" />
|
||||
@@ -225,6 +259,15 @@
|
||||
<Setter TargetName="NodeContainer" Property="BorderBrush" Value="#7E9AB6" />
|
||||
<Setter TargetName="NodeContainer" Property="Opacity" Value="1" />
|
||||
</MultiDataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsSkippedByExecutionRange}" Value="True" />
|
||||
<Condition Binding="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}, Path=IsSelected}" Value="True" />
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter TargetName="NodeContainer" Property="Background" Value="#EEF1F4" />
|
||||
<Setter TargetName="NodeContainer" Property="BorderBrush" Value="#AAB4BF" />
|
||||
<Setter TargetName="NodeContainer" Property="Opacity" Value="0.9" />
|
||||
</MultiDataTrigger>
|
||||
</DataTemplate.Triggers>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
@@ -274,9 +317,53 @@
|
||||
Text="{Binding DisplayName}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
|
||||
<Grid Grid.Column="1">
|
||||
<Grid.Style>
|
||||
<Style TargetType="Grid">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsSliderInput}" Value="True">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Grid.Style>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="58" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Slider
|
||||
Grid.Column="0"
|
||||
Margin="0,0,8,0"
|
||||
VerticalAlignment="Center"
|
||||
IsSnapToTickEnabled="{Binding IsIntegerSlider}"
|
||||
LargeChange="{Binding SliderTickFrequency}"
|
||||
Maximum="{Binding SliderMaximum}"
|
||||
Minimum="{Binding SliderMinimum}"
|
||||
SmallChange="{Binding SliderTickFrequency}"
|
||||
TickFrequency="{Binding SliderTickFrequency}"
|
||||
Value="{Binding SliderValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
|
||||
<Border
|
||||
Grid.Column="1"
|
||||
Padding="2,2"
|
||||
Background="#F8F8F8"
|
||||
BorderBrush="#CDCBCB"
|
||||
BorderThickness="1"
|
||||
CornerRadius="2">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="{StaticResource UiFont}"
|
||||
FontSize="11"
|
||||
Text="{Binding DisplayValueText}" />
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<TextBox
|
||||
Grid.Column="1"
|
||||
Padding="4,2"
|
||||
Padding="2,2"
|
||||
BorderBrush="#CDCBCB"
|
||||
BorderThickness="1"
|
||||
FontFamily="{StaticResource UiFont}"
|
||||
|
||||
@@ -59,6 +59,8 @@ namespace XplorePlane.Views
|
||||
PipelineListBox.PreviewMouseMove += OnPreviewMouseMove;
|
||||
PipelineListBox.PreviewMouseLeftButtonUp -= OnPreviewMouseLeftButtonUp;
|
||||
PipelineListBox.PreviewMouseLeftButtonUp += OnPreviewMouseLeftButtonUp;
|
||||
PipelineListBox.PreviewMouseRightButtonDown -= OnPreviewMouseRightButtonDown;
|
||||
PipelineListBox.PreviewMouseRightButtonDown += OnPreviewMouseRightButtonDown;
|
||||
PipelineListBox.MouseDoubleClick -= OnMouseDoubleClick;
|
||||
PipelineListBox.MouseDoubleClick += OnMouseDoubleClick;
|
||||
PipelineListBox.PreviewKeyDown -= OnPreviewKeyDown;
|
||||
@@ -133,6 +135,16 @@ namespace XplorePlane.Views
|
||||
ResetDragState();
|
||||
}
|
||||
|
||||
private void OnPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
var clickedNode = FindNodeFromOriginalSource(e.OriginalSource);
|
||||
if (clickedNode == null || IsInteractiveChild(e.OriginalSource))
|
||||
return;
|
||||
|
||||
PipelineListBox.SelectedItem = clickedNode;
|
||||
PipelineListBox.Focus();
|
||||
}
|
||||
|
||||
private void OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
var vm = DataContext as IPipelineEditorHostViewModel;
|
||||
|
||||
@@ -279,7 +279,7 @@
|
||||
Size="Large"
|
||||
SmallImage="/Assets/Icons/cnc.png"
|
||||
Text="CNC 编辑" />
|
||||
|
||||
<!--
|
||||
<StackPanel>
|
||||
<telerik:RadRibbonButton
|
||||
telerik:ScreenTip.Title="参考点"
|
||||
@@ -313,12 +313,7 @@
|
||||
Command="{Binding InsertInspectionModuleCommand}"
|
||||
SmallImage="/Assets/Icons/Module.png"
|
||||
Text="检测模块" />
|
||||
<telerik:RadRibbonButton
|
||||
telerik:ScreenTip.Title="全部保存"
|
||||
Size="Medium"
|
||||
Command="{Binding SaveCncProgramCommand}"
|
||||
SmallImage="/Assets/Icons/saveall.png"
|
||||
Text="全部保存" />
|
||||
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<telerik:RadRibbonButton
|
||||
@@ -334,6 +329,7 @@
|
||||
SmallImage="/Assets/Icons/wait.png"
|
||||
Text="插入等待" />
|
||||
</StackPanel>
|
||||
-->
|
||||
|
||||
<telerik:RadRibbonButton
|
||||
telerik:ScreenTip.Description="打开矩阵编排窗口,配置多工件阵列检测方案"
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<Page Remove="MainWindow.xaml" />
|
||||
<Page Remove="Views\ImageProcessing\ImageProcessingPanelView.xaml" />
|
||||
<Page Remove="Views\ImageProcessing\ImageProcessingWindow.xaml" />
|
||||
<Page Remove="Views\ImageProcessing\PipelineEditorWindow.xaml" />
|
||||
<Page Remove="Views\Main\MainWindowB.xaml" />
|
||||
<Page Remove="Views\Main\NavigationPanelView.xaml" />
|
||||
</ItemGroup>
|
||||
@@ -149,8 +150,13 @@
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<Link>Libs\Hardware\zh-TW\%(Filename)%(Extension)</Link>
|
||||
</None>
|
||||
<Compile Remove="Views\Hardware\**" />
|
||||
<EmbeddedResource Remove="Views\Hardware\**" />
|
||||
<None Remove="Views\Hardware\**" />
|
||||
<Page Remove="Views\Hardware\**" />
|
||||
<Compile Remove="Views\ImageProcessing\ImageProcessingPanelView.xaml.cs" />
|
||||
<Compile Remove="Views\ImageProcessing\ImageProcessingWindow.xaml.cs" />
|
||||
<Compile Remove="Views\ImageProcessing\PipelineEditorWindow.xaml.cs" />
|
||||
<Compile Remove="Views\Main\MainWindowB.xaml.cs" />
|
||||
<Compile Remove="Views\Main\NavigationPanelView.xaml.cs" />
|
||||
<Content Include="XplorerPlane.ico">
|
||||
|
||||
Reference in New Issue
Block a user