#0024 修复浮动工具箱
This commit is contained in:
@@ -141,22 +141,30 @@ namespace XplorePlane.ViewModels
|
|||||||
|
|
||||||
private void AddOperator(string operatorKey)
|
private void AddOperator(string operatorKey)
|
||||||
{
|
{
|
||||||
|
Serilog.Log.Debug("AddOperator 被调用,operatorKey={OperatorKey}", operatorKey);
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(operatorKey))
|
if (string.IsNullOrWhiteSpace(operatorKey))
|
||||||
{
|
{
|
||||||
StatusMessage = "算子键不能为空";
|
StatusMessage = "算子键不能为空";
|
||||||
|
Serilog.Log.Warning("AddOperator 失败:operatorKey 为空");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var available = _imageProcessingService.GetAvailableProcessors();
|
var available = _imageProcessingService.GetAvailableProcessors();
|
||||||
|
Serilog.Log.Debug("可用算子数量:{Count},包含 {Key}:{Contains}",
|
||||||
|
available.Count(), operatorKey, available.Contains(operatorKey));
|
||||||
|
|
||||||
if (!available.Contains(operatorKey))
|
if (!available.Contains(operatorKey))
|
||||||
{
|
{
|
||||||
StatusMessage = $"算子 '{operatorKey}' 未注册";
|
StatusMessage = $"算子 '{operatorKey}' 未注册";
|
||||||
|
Serilog.Log.Warning("AddOperator 失败:算子 {Key} 未注册", operatorKey);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PipelineNodes.Count >= MaxPipelineLength)
|
if (PipelineNodes.Count >= MaxPipelineLength)
|
||||||
{
|
{
|
||||||
StatusMessage = $"流水线节点数已达上限({MaxPipelineLength})";
|
StatusMessage = $"流水线节点数已达上限({MaxPipelineLength})";
|
||||||
|
Serilog.Log.Warning("AddOperator 失败:节点数已达上限 {Max}", MaxPipelineLength);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,6 +175,8 @@ namespace XplorePlane.ViewModels
|
|||||||
};
|
};
|
||||||
LoadNodeParameters(node);
|
LoadNodeParameters(node);
|
||||||
PipelineNodes.Add(node);
|
PipelineNodes.Add(node);
|
||||||
|
Serilog.Log.Information("节点已添加到 PipelineNodes:{Key} ({DisplayName}),当前节点数={Count}",
|
||||||
|
operatorKey, displayName, PipelineNodes.Count);
|
||||||
StatusMessage = $"已添加算子:{displayName}";
|
StatusMessage = $"已添加算子:{displayName}";
|
||||||
TriggerDebouncedExecution();
|
TriggerDebouncedExecution();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Input;
|
||||||
using Prism.Ioc;
|
using Prism.Ioc;
|
||||||
using Telerik.Windows.DragDrop;
|
using Serilog;
|
||||||
using XplorePlane.ViewModels;
|
using XplorePlane.ViewModels;
|
||||||
|
|
||||||
namespace XplorePlane.Views
|
namespace XplorePlane.Views
|
||||||
{
|
{
|
||||||
public partial class OperatorToolboxView : UserControl
|
public partial class OperatorToolboxView : UserControl
|
||||||
{
|
{
|
||||||
private const string DragFormat = "OperatorDescriptor";
|
public const string DragFormat = "PipelineOperatorKey";
|
||||||
|
|
||||||
|
private Point _dragStartPoint;
|
||||||
|
private bool _isDragging;
|
||||||
|
|
||||||
public OperatorToolboxView()
|
public OperatorToolboxView()
|
||||||
{
|
{
|
||||||
@@ -21,29 +25,55 @@ namespace XplorePlane.Views
|
|||||||
if (DataContext == null)
|
if (DataContext == null)
|
||||||
DataContext = ContainerLocator.Container.Resolve<OperatorToolboxViewModel>();
|
DataContext = ContainerLocator.Container.Resolve<OperatorToolboxViewModel>();
|
||||||
|
|
||||||
// 启用拖拽源 + 注册拖拽初始化事件
|
ToolboxListBox.PreviewMouseLeftButtonDown += OnPreviewMouseDown;
|
||||||
DragDropManager.SetAllowDrag(ToolboxListBox, true);
|
ToolboxListBox.PreviewMouseMove += OnPreviewMouseMove;
|
||||||
DragDropManager.AddDragInitializeHandler(ToolboxListBox, OnDragInitialize, true);
|
Log.Debug("OperatorToolboxView 原生拖拽源已注册");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDragInitialize(object sender, DragInitializeEventArgs e)
|
private void OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
if (ToolboxListBox.SelectedItem is OperatorDescriptor descriptor)
|
_dragStartPoint = e.GetPosition(null);
|
||||||
|
_isDragging = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPreviewMouseMove(object sender, MouseEventArgs e)
|
||||||
{
|
{
|
||||||
e.AllowedEffects = System.Windows.DragDropEffects.Copy;
|
if (e.LeftButton != MouseButtonState.Pressed) return;
|
||||||
DragDropPayloadManager.SetData(e.Data, DragFormat, descriptor);
|
|
||||||
e.DragVisual = new System.Windows.Controls.TextBlock
|
var pos = e.GetPosition(null);
|
||||||
|
var diff = pos - _dragStartPoint;
|
||||||
|
|
||||||
|
if (!_isDragging
|
||||||
|
&& (System.Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance
|
||||||
|
|| System.Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
|
||||||
{
|
{
|
||||||
Text = descriptor.DisplayName,
|
_isDragging = true;
|
||||||
Padding = new Thickness(6, 3, 6, 3),
|
|
||||||
Background = System.Windows.Media.Brushes.LightBlue,
|
// 从鼠标位置 HitTest 找到 OperatorDescriptor
|
||||||
FontFamily = new System.Windows.Media.FontFamily("Microsoft YaHei UI"),
|
var hitResult = System.Windows.Media.VisualTreeHelper.HitTest(
|
||||||
FontSize = 11
|
ToolboxListBox, e.GetPosition(ToolboxListBox));
|
||||||
};
|
|
||||||
}
|
OperatorDescriptor? descriptor = null;
|
||||||
else
|
var node = hitResult?.VisualHit as DependencyObject;
|
||||||
|
while (node != null)
|
||||||
{
|
{
|
||||||
e.Cancel = true;
|
if (node is FrameworkElement fe && fe.DataContext is OperatorDescriptor d)
|
||||||
|
{
|
||||||
|
descriptor = d;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
node = System.Windows.Media.VisualTreeHelper.GetParent(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (descriptor == null)
|
||||||
|
{
|
||||||
|
Log.Warning("拖拽初始化失败:HitTest 未命中算子项");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Information("开始拖拽算子:{OperatorKey} ({DisplayName})", descriptor.Key, descriptor.DisplayName);
|
||||||
|
var data = new DataObject(DragFormat, descriptor.Key);
|
||||||
|
DragDrop.DoDragDrop(ToolboxListBox, data, DragDropEffects.Copy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<FontFamily x:Key="CsdFont">Microsoft YaHei UI</FontFamily>
|
<FontFamily x:Key="CsdFont">Microsoft YaHei UI</FontFamily>
|
||||||
|
|
||||||
<!-- 节点项样式 -->
|
<!-- 节点项样式 -->
|
||||||
<Style x:Key="PipelineNodeItemStyle" TargetType="telerik:RadListBoxItem">
|
<Style x:Key="PipelineNodeItemStyle" TargetType="ListBoxItem">
|
||||||
<Setter Property="Padding" Value="0" />
|
<Setter Property="Padding" Value="0" />
|
||||||
<Setter Property="Margin" Value="0" />
|
<Setter Property="Margin" Value="0" />
|
||||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="36" />
|
<RowDefinition Height="40" />
|
||||||
<RowDefinition Height="2*" />
|
<RowDefinition Height="2*" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
@@ -110,19 +110,19 @@
|
|||||||
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
|
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
|
||||||
<Button
|
<Button
|
||||||
Command="{Binding NewPipelineCommand}"
|
Command="{Binding NewPipelineCommand}"
|
||||||
Content="新"
|
Content="新建"
|
||||||
Style="{StaticResource ToolbarBtn}"
|
Style="{StaticResource ToolbarBtn}"
|
||||||
ToolTip="新建流水线" />
|
ToolTip="新建流水线" />
|
||||||
<Button
|
<Button
|
||||||
Command="{Binding SavePipelineCommand}"
|
Command="{Binding SavePipelineCommand}"
|
||||||
Content="存"
|
Content="保存"
|
||||||
Style="{StaticResource ToolbarBtn}"
|
Style="{StaticResource ToolbarBtn}"
|
||||||
ToolTip="保存流水线" />
|
ToolTip="保存流水线" />
|
||||||
<Button
|
<Button
|
||||||
Command="{Binding SaveAsPipelineCommand}"
|
Command="{Binding SaveAsPipelineCommand}"
|
||||||
Content="另"
|
Content="另存为"
|
||||||
Style="{StaticResource ToolbarBtn}"
|
Style="{StaticResource ToolbarBtn}"
|
||||||
ToolTip="另存为" />
|
ToolTip="另存为" Width="43" />
|
||||||
<Button
|
<Button
|
||||||
Command="{Binding ExecutePipelineCommand}"
|
Command="{Binding ExecutePipelineCommand}"
|
||||||
Content="▶"
|
Content="▶"
|
||||||
@@ -145,83 +145,52 @@
|
|||||||
|
|
||||||
|
|
||||||
<!-- 流水线节点列表(拖拽目标) -->
|
<!-- 流水线节点列表(拖拽目标) -->
|
||||||
<telerik:RadListBox
|
<ListBox
|
||||||
x:Name="PipelineListBox"
|
x:Name="PipelineListBox"
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
BorderThickness="0"
|
BorderThickness="0"
|
||||||
ItemContainerStyle="{StaticResource PipelineNodeItemStyle}"
|
ItemContainerStyle="{StaticResource PipelineNodeItemStyle}"
|
||||||
ItemsSource="{Binding PipelineNodes}"
|
ItemsSource="{Binding PipelineNodes}"
|
||||||
SelectedItem="{Binding SelectedNode, Mode=TwoWay}">
|
SelectedItem="{Binding SelectedNode, Mode=TwoWay}"
|
||||||
<telerik:RadListBox.ItemTemplate>
|
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
|
||||||
|
<ListBox.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<Grid>
|
<Grid MinHeight="44">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="20" />
|
<ColumnDefinition Width="28" />
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<!-- 竖线连接 -->
|
<!-- 竖线连接 + 节点方块 -->
|
||||||
<Canvas Grid.Column="0" Width="20">
|
<Grid Grid.Column="0">
|
||||||
<Line
|
<Line Stroke="#5B9BD5" StrokeThickness="2"
|
||||||
Stroke="#5B9BD5"
|
X1="14" X2="14" Y1="0" Y2="44"
|
||||||
StrokeThickness="2"
|
HorizontalAlignment="Left" />
|
||||||
X1="10"
|
<Border Width="16" Height="16"
|
||||||
X2="10"
|
Background="White"
|
||||||
Y1="0"
|
BorderBrush="#5B9BD5" BorderThickness="1.5"
|
||||||
Y2="40" />
|
CornerRadius="2"
|
||||||
<Rectangle
|
VerticalAlignment="Center" HorizontalAlignment="Center" />
|
||||||
Canvas.Left="3"
|
</Grid>
|
||||||
Canvas.Top="12"
|
|
||||||
Width="14"
|
|
||||||
Height="14"
|
|
||||||
Fill="White"
|
|
||||||
RadiusX="2"
|
|
||||||
RadiusY="2"
|
|
||||||
Stroke="#5B9BD5"
|
|
||||||
StrokeThickness="1.5" />
|
|
||||||
</Canvas>
|
|
||||||
|
|
||||||
<!-- 节点行 -->
|
<!-- 节点行 -->
|
||||||
<Border
|
<Border Grid.Column="1" Padding="6,8">
|
||||||
Grid.Column="1"
|
|
||||||
Padding="6,8"
|
|
||||||
BorderBrush="Transparent"
|
|
||||||
BorderThickness="0">
|
|
||||||
<Border.Style>
|
|
||||||
<Style TargetType="Border">
|
|
||||||
<Setter Property="Background" Value="Transparent" />
|
|
||||||
<Style.Triggers>
|
|
||||||
<DataTrigger Binding="{Binding IsSelected}" Value="True">
|
|
||||||
<Setter Property="Background" Value="{StaticResource AccentBlue}" />
|
|
||||||
</DataTrigger>
|
|
||||||
</Style.Triggers>
|
|
||||||
</Style>
|
|
||||||
</Border.Style>
|
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<Border
|
<Border Width="28" Height="28" Margin="0,0,8,0"
|
||||||
Width="28"
|
Background="#E8F0FE" CornerRadius="3">
|
||||||
Height="28"
|
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||||
Margin="0,0,8,0"
|
FontSize="13" Text="⚙" />
|
||||||
Background="#E8F0FE"
|
|
||||||
CornerRadius="3">
|
|
||||||
<TextBlock
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
FontSize="13"
|
|
||||||
Text="⚙" />
|
|
||||||
</Border>
|
</Border>
|
||||||
<TextBlock
|
<TextBlock VerticalAlignment="Center"
|
||||||
VerticalAlignment="Center"
|
FontFamily="Microsoft YaHei UI" FontSize="12"
|
||||||
FontFamily="Microsoft YaHei UI"
|
|
||||||
FontSize="12"
|
|
||||||
Text="{Binding DisplayName}" />
|
Text="{Binding DisplayName}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</telerik:RadListBox.ItemTemplate>
|
</ListBox.ItemTemplate>
|
||||||
</telerik:RadListBox>
|
</ListBox>
|
||||||
<!-- 分隔线 -->
|
<!-- 分隔线 -->
|
||||||
<Rectangle
|
<Rectangle
|
||||||
Grid.Row="3"
|
Grid.Row="3"
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using Prism.Ioc;
|
using Prism.Ioc;
|
||||||
using Telerik.Windows.DragDrop;
|
using Serilog;
|
||||||
using XplorePlane.ViewModels;
|
using XplorePlane.ViewModels;
|
||||||
|
|
||||||
namespace XplorePlane.Views
|
namespace XplorePlane.Views
|
||||||
{
|
{
|
||||||
public partial class PipelineEditorView : UserControl
|
public partial class PipelineEditorView : UserControl
|
||||||
{
|
{
|
||||||
private const string DragFormat = "OperatorDescriptor";
|
|
||||||
|
|
||||||
public PipelineEditorView()
|
public PipelineEditorView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -21,19 +19,47 @@ namespace XplorePlane.Views
|
|||||||
if (DataContext == null)
|
if (DataContext == null)
|
||||||
DataContext = ContainerLocator.Container.Resolve<PipelineEditorViewModel>();
|
DataContext = ContainerLocator.Container.Resolve<PipelineEditorViewModel>();
|
||||||
|
|
||||||
// 启用拖拽目标 + 注册 Drop 事件
|
|
||||||
PipelineListBox.AllowDrop = true;
|
PipelineListBox.AllowDrop = true;
|
||||||
DragDropManager.AddDropHandler(PipelineListBox, OnOperatorDropped, true);
|
PipelineListBox.Drop += OnOperatorDropped;
|
||||||
|
PipelineListBox.DragOver += OnDragOver;
|
||||||
|
Log.Debug("PipelineEditorView 原生 Drop 目标已注册");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnOperatorDropped(object sender, Telerik.Windows.DragDrop.DragEventArgs e)
|
private void OnDragOver(object sender, DragEventArgs e)
|
||||||
{
|
{
|
||||||
if (DataContext is not PipelineEditorViewModel vm) return;
|
if (e.Data.GetDataPresent(OperatorToolboxView.DragFormat))
|
||||||
|
e.Effects = DragDropEffects.Copy;
|
||||||
|
else
|
||||||
|
e.Effects = DragDropEffects.None;
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
var descriptor = DragDropPayloadManager.GetDataFromObject(e.Data, DragFormat) as OperatorDescriptor;
|
private void OnOperatorDropped(object sender, DragEventArgs e)
|
||||||
if (descriptor == null) return;
|
{
|
||||||
|
if (DataContext is not PipelineEditorViewModel vm)
|
||||||
|
{
|
||||||
|
Log.Warning("Drop 事件触发但 DataContext 不是 PipelineEditorViewModel");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
vm.AddOperatorCommand.Execute(descriptor.Key);
|
if (!e.Data.GetDataPresent(OperatorToolboxView.DragFormat))
|
||||||
|
{
|
||||||
|
Log.Warning("Drop 事件触发但数据中无 {Format}", OperatorToolboxView.DragFormat);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var operatorKey = e.Data.GetData(OperatorToolboxView.DragFormat) as string;
|
||||||
|
if (string.IsNullOrEmpty(operatorKey))
|
||||||
|
{
|
||||||
|
Log.Warning("Drop 事件触发但 OperatorKey 为空");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Information("算子已放入流水线:{OperatorKey},VM HashCode={Hash},当前节点数(执行前)={Count}",
|
||||||
|
operatorKey, vm.GetHashCode(), vm.PipelineNodes.Count);
|
||||||
|
vm.AddOperatorCommand.Execute(operatorKey);
|
||||||
|
Log.Information("AddOperator 执行后节点数={Count},PipelineListBox.Items.Count={ItemsCount}",
|
||||||
|
vm.PipelineNodes.Count, PipelineListBox.Items.Count);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,4 +16,8 @@
|
|||||||
2、硬件层射线源的集成 √
|
2、硬件层射线源的集成 √
|
||||||
3、图像层集成,包括复刻一个示例界面,优化界面布局及算子中文 √
|
3、图像层集成,包括复刻一个示例界面,优化界面布局及算子中文 √
|
||||||
4、浮动图像处理工具箱调研 √
|
4、浮动图像处理工具箱调研 √
|
||||||
|
5、修复图像工具箱拖拽事件,流水线列表没有生成对应的控件
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
5、各窗体间数据流的传递,全局数据结构的设计
|
5、各窗体间数据流的传递,全局数据结构的设计
|
||||||
Reference in New Issue
Block a user