From 5ae5963353ad642d47b51bb37c16318fdfb8d37d Mon Sep 17 00:00:00 2001 From: "zhengxuan.zhang" Date: Thu, 23 Apr 2026 17:04:41 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BD=93=E5=9B=A0=E4=B8=BA=E7=AE=97=E5=AD=90?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E8=BE=93=E5=85=A5=E4=B8=8D=E5=90=88=E7=90=86?= =?UTF-8?q?=EF=BC=8C=E6=88=96=E8=80=85=E6=89=A7=E8=A1=8C=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=EF=BC=8C=E8=A6=81=E5=9C=A8=E7=8A=B6=E6=80=81=E6=A0=8F=E6=98=BE?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Pipeline/PipelineExecutionService.cs | 25 ++++---- .../PipelineEditorViewModel.cs | 57 +++++++++++++++++-- .../ImageProcessing/ProcessorParameterVM.cs | 34 ++++++++++- .../ImageProcessing/PipelineEditorView.xaml | 32 +++++++++-- 4 files changed, 124 insertions(+), 24 deletions(-) diff --git a/XplorePlane/Services/Pipeline/PipelineExecutionService.cs b/XplorePlane/Services/Pipeline/PipelineExecutionService.cs index 6f5af09..648c42a 100644 --- a/XplorePlane/Services/Pipeline/PipelineExecutionService.cs +++ b/XplorePlane/Services/Pipeline/PipelineExecutionService.cs @@ -37,11 +37,10 @@ namespace XplorePlane.Services if (enabledNodes.Count == 0) return source; - // 大图像预览缩放 var current = ScaleForPreview(source); - int total = enabledNodes.Count; - for (int step = 0; step < total; step++) + var total = enabledNodes.Count; + for (var step = 0; step < total; step++) { cancellationToken.ThrowIfCancellationRequested(); @@ -53,15 +52,14 @@ namespace XplorePlane.Services if (invalidParameters.Count > 0) { + var invalidParameterText = string.Join("、", invalidParameters); throw new PipelineExecutionException( - $"算子 '{node.DisplayName}' 存在无效参数:{string.Join("、", invalidParameters)}", + $"算子 '{node.DisplayName}' 存在无效参数:{invalidParameterText}", node.Order, node.OperatorKey); } - var parameters = node.Parameters - .Where(p => p.IsValueValid) - .ToDictionary(p => p.Name, p => p.Value); + var parameters = node.Parameters.ToDictionary(p => p.Name, p => p.Value); try { @@ -69,9 +67,12 @@ namespace XplorePlane.Services current, node.OperatorKey, parameters, null, cancellationToken); if (current == null) + { throw new PipelineExecutionException( - $"算子 '{node.OperatorKey}' 返回了空图像", - node.Order, node.OperatorKey); + $"算子 '{node.DisplayName}' 返回了空图像", + node.Order, + node.OperatorKey); + } } catch (OperationCanceledException) { @@ -85,7 +86,9 @@ namespace XplorePlane.Services { throw new PipelineExecutionException( $"算子 '{node.DisplayName}' 执行失败:{ex.Message}", - node.Order, node.OperatorKey, ex); + node.Order, + node.OperatorKey, + ex); } progress?.Report(new PipelineProgress(step + 1, total, node.DisplayName)); @@ -102,7 +105,7 @@ namespace XplorePlane.Services if (source.PixelWidth <= UhdThreshold && source.PixelHeight <= UhdThreshold) return source; - double scale = (double)PreviewMaxHeight / source.PixelHeight; + var scale = (double)PreviewMaxHeight / source.PixelHeight; if (source.PixelWidth * scale > UhdThreshold) scale = (double)UhdThreshold / source.PixelWidth; diff --git a/XplorePlane/ViewModels/ImageProcessing/PipelineEditorViewModel.cs b/XplorePlane/ViewModels/ImageProcessing/PipelineEditorViewModel.cs index f43cd33..e2aeb21 100644 --- a/XplorePlane/ViewModels/ImageProcessing/PipelineEditorViewModel.cs +++ b/XplorePlane/ViewModels/ImageProcessing/PipelineEditorViewModel.cs @@ -34,6 +34,7 @@ namespace XplorePlane.ViewModels private string _pipelineName = "新建流水线"; private string _selectedDevice = string.Empty; private bool _isExecuting; + private bool _isStatusError; private string _statusMessage = string.Empty; private string _currentFilePath; @@ -148,6 +149,12 @@ namespace XplorePlane.ViewModels set => SetProperty(ref _statusMessage, value); } + public bool IsStatusError + { + get => _isStatusError; + private set => SetProperty(ref _isStatusError, value); + } + // ── Commands ────────────────────────────────────────────────── public DelegateCommand AddOperatorCommand { get; } @@ -329,7 +336,12 @@ namespace XplorePlane.ViewModels vm.PropertyChanged += (_, e) => { if (e.PropertyName == nameof(ProcessorParameterVM.Value)) + { + if (TryReportInvalidParameters()) + return; + TriggerDebouncedExecution(); + } }; node.Parameters.Add(vm); } @@ -339,36 +351,39 @@ namespace XplorePlane.ViewModels { if (SourceImage == null || IsExecuting) return; + if (TryReportInvalidParameters()) + return; + _executionCts?.Cancel(); _executionCts = new CancellationTokenSource(); var token = _executionCts.Token; IsExecuting = true; - StatusMessage = "正在执行流水线..."; + SetInfoStatus("正在执行流水线..."); try { var progress = new Progress(p => - StatusMessage = $"执行中:{p.CurrentOperator} ({p.CurrentStep}/{p.TotalSteps})"); + SetInfoStatus($"执行中:{p.CurrentOperator} ({p.CurrentStep}/{p.TotalSteps})")); var result = await _executionService.ExecutePipelineAsync( PipelineNodes, SourceImage, progress, token); PreviewImage = result; - StatusMessage = "流水线执行完成"; + SetInfoStatus("流水线执行完成"); PublishPipelinePreviewUpdated(result, StatusMessage); } catch (OperationCanceledException) { - StatusMessage = "流水线执行已取消"; + SetInfoStatus("流水线执行已取消"); } catch (PipelineExecutionException ex) { - StatusMessage = $"节点 '{ex.FailedOperatorKey}' 执行失败:{ex.Message}"; + SetErrorStatus($"执行失败:{ex.Message}"); } catch (Exception ex) { - StatusMessage = $"执行错误:{ex.Message}"; + SetErrorStatus($"执行错误:{ex.Message}"); } finally { @@ -376,6 +391,36 @@ namespace XplorePlane.ViewModels } } + private bool TryReportInvalidParameters() + { + var firstInvalidNode = PipelineNodes + .Where(n => n.IsEnabled) + .OrderBy(n => n.Order) + .FirstOrDefault(n => n.Parameters.Any(p => !p.IsValueValid)); + + if (firstInvalidNode == null) + return false; + + var invalidNames = firstInvalidNode.Parameters + .Where(p => !p.IsValueValid) + .Select(p => p.DisplayName); + SetErrorStatus($"参数错误:算子 '{firstInvalidNode.DisplayName}' 的 {string.Join("、", invalidNames)} 输入不合理,请修正后重试。"); + return true; + } + + private void SetInfoStatus(string message) + { + IsStatusError = false; + StatusMessage = message; + } + + private void SetErrorStatus(string message) + { + IsStatusError = true; + StatusMessage = message; + PublishPipelinePreviewUpdated(PreviewImage ?? SourceImage, message); + } + private void LoadImage() { var dialog = new OpenFileDialog diff --git a/XplorePlane/ViewModels/ImageProcessing/ProcessorParameterVM.cs b/XplorePlane/ViewModels/ImageProcessing/ProcessorParameterVM.cs index dc807d0..019aaca 100644 --- a/XplorePlane/ViewModels/ImageProcessing/ProcessorParameterVM.cs +++ b/XplorePlane/ViewModels/ImageProcessing/ProcessorParameterVM.cs @@ -53,8 +53,40 @@ namespace XplorePlane.ViewModels set { var normalizedValue = NormalizeValue(value); - if (SetProperty(ref _value, normalizedValue)) + if (!Equals(_value, normalizedValue)) + { + _value = normalizedValue; ValidateValue(normalizedValue); + RaisePropertyChanged(nameof(Value)); + RaisePropertyChanged(nameof(BoolValue)); + RaisePropertyChanged(nameof(SelectedOption)); + } + } + } + + public bool BoolValue + { + get => ParameterType == "bool" && TryConvertToBool(_value, out var boolValue) && boolValue; + set + { + if (ParameterType == "bool") + { + Value = value; + } + } + } + + public string SelectedOption + { + get => HasOptions + ? Convert.ToString(_value, CultureInfo.InvariantCulture) ?? string.Empty + : string.Empty; + set + { + if (HasOptions) + { + Value = value; + } } } diff --git a/XplorePlane/Views/ImageProcessing/PipelineEditorView.xaml b/XplorePlane/Views/ImageProcessing/PipelineEditorView.xaml index 35efed6..c54f6ad 100644 --- a/XplorePlane/Views/ImageProcessing/PipelineEditorView.xaml +++ b/XplorePlane/Views/ImageProcessing/PipelineEditorView.xaml @@ -285,7 +285,7 @@ FontFamily="Microsoft YaHei UI" FontSize="11" ItemsSource="{Binding Options}" - SelectedItem="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"> + SelectedItem="{Binding SelectedOption, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"> + + TextTrimming="CharacterEllipsis"> + + + +