#调整页面布局,新增操作 Tab; 图像算子改为Tab页选择;

This commit is contained in:
zhengxuan.zhang
2026-04-30 13:56:35 +08:00
parent 822d31665d
commit fd9784ecb6
14 changed files with 434 additions and 284 deletions
+1
View File
@@ -63,3 +63,4 @@ ExternalLibraries/Models/
XplorePlane/Tests/
ExternalLibraries/Telerik/
build_out.txt
XplorePlane/data/
@@ -1705,10 +1705,7 @@
"Telerik.UI.for.Wpf.NetCore.Xaml": "2024.1.408"
},
"runtime": {
"XP.Common.dll": {
"assemblyVersion": "1.4.16.1",
"fileVersion": "1.4.16.1"
}
"XP.Common.dll": {}
},
"resources": {
"en-US/XP.Common.resources.dll": {
@@ -10,7 +10,9 @@ using Moq;
using XP.Common.Logging.Interfaces;
using XplorePlane.Models;
using XplorePlane.Services.Cnc;
using XplorePlane.Services;
using XplorePlane.Services.InspectionResults;
using XplorePlane.Services.MainViewport;
using Xunit;
namespace XplorePlane.Tests.Services
@@ -175,6 +177,9 @@ internal sealed class SynchronousProgress<T> : IProgress<T>
{
var mockStore = new Mock<IInspectionResultStore>();
var mockLogger = new Mock<ILoggerService>();
var mockMainViewportService = new Mock<IMainViewportService>();
var mockPipelineExecutionService = new Mock<IPipelineExecutionService>();
var mockImageProcessingService = new Mock<IImageProcessingService>();
mockLogger.Setup(l => l.ForModule<CncExecutionService>()).Returns(mockLogger.Object);
mockStore.Setup(s => s.BeginRunAsync(
@@ -195,7 +200,12 @@ internal sealed class SynchronousProgress<T> : IProgress<T>
It.IsAny<DateTime?>()))
.Returns(Task.CompletedTask);
var service = new CncExecutionService(mockStore.Object, mockLogger.Object);
var service = new CncExecutionService(
mockStore.Object,
mockLogger.Object,
mockMainViewportService.Object,
mockPipelineExecutionService.Object,
mockImageProcessingService.Object);
return (service, mockStore, mockLogger);
}
+1 -1
View File
@@ -369,7 +369,7 @@ namespace XplorePlane
// 注册图像处理服务与视图
containerRegistry.RegisterSingleton<IImageProcessingService, ImageProcessingService>();
containerRegistry.Register<ImageProcessingViewModel>();
containerRegistry.RegisterForNavigation<ImageProcessingPanelView>();
// 注册流水线服务(单例,共享 IImageProcessingService
containerRegistry.RegisterSingleton<IPipelineExecutionService, PipelineExecutionService>();
+21 -52
View File
@@ -6,7 +6,6 @@ using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Imaging;
using XP.Common.GeneralForm.Views;
using XP.Common.Logging.Interfaces;
using XplorePlane.Models;
using XplorePlane.Services.InspectionResults;
@@ -42,7 +41,7 @@ namespace XplorePlane.Services.Cnc
public async Task ExecuteAsync(CncProgram program, IProgress<CncNodeExecutionProgress> progress, CancellationToken cancellationToken)
{
// Pre-cancellation check do NOT call BeginRunAsync if already cancelled
// Pre-cancellation check - do NOT call BeginRunAsync if already cancelled
if (cancellationToken.IsCancellationRequested)
return;
@@ -150,7 +149,10 @@ namespace XplorePlane.Services.Cnc
{
_logger.ForModule<CncExecutionService>().Error(ex,
"Unexpected error executing node '{0}' (Id={1})", node.Name, node.Id);
nodeSucceeded = false;
if (cancellationToken.IsCancellationRequested)
cancelled = true;
else
nodeSucceeded = false;
}
if (cancelled)
@@ -160,7 +162,7 @@ namespace XplorePlane.Services.Cnc
}
// InspectionModuleNode 完成时携带结果图像,供 ViewModel 缓存到节点上
var nodeResultImage = (node is InspectionModuleNode) ? lastResultImage : null;
var nodeResultImage = node is InspectionModuleNode ? lastResultImage : null;
var finalState = nodeSucceeded ? NodeExecutionState.Succeeded : NodeExecutionState.Failed;
progress?.Report(new CncNodeExecutionProgress(node.Id, finalState, nodeResultImage));
@@ -168,7 +170,7 @@ namespace XplorePlane.Services.Cnc
allSucceeded = false;
}
endLoop:
endLoop:
bool? overallPass = cancelled ? null : (bool?)allSucceeded;
@@ -211,10 +213,8 @@ namespace XplorePlane.Services.Cnc
};
}
// 构建资产列表
var assets = new System.Collections.Generic.List<InspectionAssetWriteRequest>();
// input.bmp — 当前源图像
if (sourceImage != null)
{
assets.Add(new InspectionAssetWriteRequest
@@ -227,7 +227,6 @@ namespace XplorePlane.Services.Cnc
});
}
// result_overlay.bmp — 执行流水线后的结果图像
BitmapSource resultImage = null;
if (_pipelineExecutionService != null && inspectionNode.Pipeline?.Nodes?.Count > 0 && sourceImage != null)
{
@@ -248,8 +247,6 @@ namespace XplorePlane.Services.Cnc
Height = resultImage.PixelHeight
});
nodeResult.Status = InspectionNodeStatus.Succeeded;
// 执行完立即更新主视口
_mainViewportService?.SetManualImage(resultImage, $"CNC节点:{inspectionNode.Name}");
}
}
@@ -265,21 +262,20 @@ namespace XplorePlane.Services.Cnc
return resultImage;
}
private System.Collections.Generic.IEnumerable<ViewModels.PipelineNodeViewModel> BuildPipelineNodeViewModels(PipelineModel pipeline)
private System.Collections.Generic.IEnumerable<PipelineNodeViewModel> BuildPipelineNodeViewModels(PipelineModel pipeline)
{
var nodes = new System.Collections.Generic.List<ViewModels.PipelineNodeViewModel>();
var nodes = new System.Collections.Generic.List<PipelineNodeViewModel>();
if (pipeline?.Nodes == null) return nodes;
foreach (var nodeModel in pipeline.Nodes.OrderBy(n => n.Order))
{
var displayName = _imageProcessingService?.GetProcessorDisplayName(nodeModel.OperatorKey) ?? nodeModel.OperatorKey;
var vm = new ViewModels.PipelineNodeViewModel(nodeModel.OperatorKey, displayName, string.Empty)
var vm = new PipelineNodeViewModel(nodeModel.OperatorKey, displayName, string.Empty)
{
Order = nodeModel.Order,
IsEnabled = nodeModel.IsEnabled
};
// 加载参数定义并恢复保存的值
if (_imageProcessingService != null)
{
var paramDefs = _imageProcessingService.GetProcessorParameters(nodeModel.OperatorKey);
@@ -287,7 +283,7 @@ namespace XplorePlane.Services.Cnc
{
foreach (var def in paramDefs)
{
var paramVm = new ViewModels.ProcessorParameterVM(def);
var paramVm = new ProcessorParameterVM(def);
if (nodeModel.Parameters != null && nodeModel.Parameters.TryGetValue(def.Name, out var saved))
paramVm.Value = ConvertSavedValue(saved, def.ValueType);
vm.Parameters.Add(paramVm);
@@ -300,19 +296,16 @@ namespace XplorePlane.Services.Cnc
return nodes;
}
/// <summary>
/// 将 JSON 反序列化后的 JsonElement 转换为参数所需的实际类型。
/// </summary>
private static object ConvertSavedValue(object savedValue, Type targetType)
{
if (savedValue is not System.Text.Json.JsonElement jsonElement)
if (savedValue is not JsonElement jsonElement)
return savedValue;
try
{
if (targetType == typeof(int)) return jsonElement.GetInt32();
if (targetType == typeof(int)) return jsonElement.GetInt32();
if (targetType == typeof(double)) return jsonElement.GetDouble();
if (targetType == typeof(bool)) return jsonElement.GetBoolean();
if (targetType == typeof(bool)) return jsonElement.GetBoolean();
if (targetType == typeof(string)) return jsonElement.GetString() ?? string.Empty;
return jsonElement.ToString();
}
@@ -338,40 +331,16 @@ namespace XplorePlane.Services.Cnc
return;
const int tickMs = 50;
ProgressWindow progressWindow = null;
await Application.Current.Dispatcher.InvokeAsync(() =>
int elapsed = 0;
while (elapsed < totalMs)
{
progressWindow = new ProgressWindow(
title: "延时等待",
message: $"节点:{waitNode.Name} 等待 {totalMs / 1000.0:F1} 秒",
isCancelable: false);
progressWindow.Owner = Application.Current.MainWindow;
progressWindow.Show();
});
cancellationToken.ThrowIfCancellationRequested();
try
{
int elapsed = 0;
while (elapsed < totalMs)
{
cancellationToken.ThrowIfCancellationRequested();
int remaining = totalMs - elapsed;
int delay = Math.Min(tickMs, remaining);
await Task.Delay(delay, cancellationToken);
elapsed += delay;
double pct = Math.Min(100.0 * elapsed / totalMs, 100.0);
double remainSec = Math.Max(0, (totalMs - elapsed) / 1000.0);
string msg = $"节点:{waitNode.Name} 剩余 {remainSec:F1} 秒";
progressWindow?.UpdateProgress(msg, pct);
}
}
finally
{
progressWindow?.Close();
int remaining = totalMs - elapsed;
int delay = Math.Min(tickMs, remaining);
await Task.Delay(delay, cancellationToken);
elapsed += delay;
}
}
}
+2 -2
View File
@@ -23,7 +23,7 @@ namespace XplorePlane.ViewModels
{
public class MainViewModel : BindableBase
{
private const double CncEditorHostWidth = 502d;
private const double CncEditorHostWidth = 402d;
private readonly ILoggerService _logger;
private readonly IContainerProvider _containerProvider;
@@ -203,7 +203,7 @@ namespace XplorePlane.ViewModels
ClearCommand = new DelegateCommand(OnClear);
EditPropertiesCommand = new DelegateCommand(OnEditProperties);
OpenImageProcessingCommand = new DelegateCommand(() => ShowWindow(new Views.ImageProcessingWindow(), "图像处理"));
LoadImageCommand = new DelegateCommand(ExecuteLoadImage);
OpenPipelineEditorCommand = new DelegateCommand(() => ShowWindow(new Views.PipelineEditorWindow(), "流水线编辑器"));
OpenCncEditorCommand = new DelegateCommand(ExecuteOpenCncEditor);
+3 -3
View File
@@ -10,7 +10,7 @@
xmlns:views="clr-namespace:XplorePlane.Views"
xmlns:vm="clr-namespace:XplorePlane.ViewModels.Cnc"
d:DesignHeight="760"
d:DesignWidth="502"
d:DesignWidth="402"
prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d">
@@ -99,8 +99,8 @@
</UserControl.Resources>
<Border
Width="502"
MinWidth="502"
Width="402"
MinWidth="402"
HorizontalAlignment="Left"
Background="{StaticResource PanelBg}"
BorderBrush="{StaticResource PanelBorder}"
@@ -4,22 +4,29 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:prism="http://prismlibrary.com/"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d"
d:DesignHeight="600" d:DesignWidth="280">
d:DesignHeight="600"
d:DesignWidth="500">
<UserControl.Resources>
<SolidColorBrush x:Key="PanelBg" Color="White" />
<SolidColorBrush x:Key="PanelBorder" Color="#cdcbcb" />
<SolidColorBrush x:Key="CategoryBg" Color="#F5F7FA" />
<SolidColorBrush x:Key="HoverBg" Color="#E8F0FE" />
<FontFamily x:Key="CsdFont">Microsoft YaHei UI</FontFamily>
<Style x:Key="OperatorToolboxTabItemStyle" TargetType="TabItem">
<Setter Property="Padding" Value="10,5" />
<Setter Property="Margin" Value="0,0,4,0" />
<Setter Property="FontFamily" Value="{StaticResource CsdFont}" />
<Setter Property="FontSize" Value="11" />
</Style>
</UserControl.Resources>
<Border Background="{StaticResource PanelBg}"
BorderBrush="{StaticResource PanelBorder}"
BorderThickness="1" CornerRadius="4">
BorderThickness="1"
CornerRadius="4">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
@@ -27,14 +34,24 @@
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- 标题(支持无边框窗口拖拽) -->
<Border x:Name="TitleBar" Grid.Row="0" Background="#0060A0" Padding="10,8">
<Border x:Name="TitleBar"
Grid.Row="0"
Background="#0060A0"
Padding="10,8">
<Grid>
<TextBlock Text="🧰 算子工具箱" FontFamily="{StaticResource CsdFont}"
FontWeight="Bold" FontSize="13" Foreground="White"
<TextBlock Text="算子工具箱"
FontFamily="{StaticResource CsdFont}"
FontWeight="Bold"
FontSize="13"
Foreground="White"
VerticalAlignment="Center" />
<Button x:Name="CloseBtn" HorizontalAlignment="Right" VerticalAlignment="Center"
Content="✕" FontSize="12" Foreground="White" Cursor="Hand"
<Button x:Name="CloseBtn"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Content="×"
FontSize="12"
Foreground="White"
Cursor="Hand"
Visibility="Collapsed"
ToolTip="关闭">
<Button.Style>
@@ -46,7 +63,8 @@
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
CornerRadius="3" Padding="{TemplateBinding Padding}">
CornerRadius="3"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
@@ -64,76 +82,61 @@
</Grid>
</Border>
<!-- 搜索框 -->
<Border Grid.Row="1" Padding="8,6" BorderBrush="{StaticResource PanelBorder}"
<Border Grid.Row="1"
Padding="8,6"
BorderBrush="{StaticResource PanelBorder}"
BorderThickness="0,0,0,1">
<TextBox Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}"
FontFamily="{StaticResource CsdFont}" FontSize="11"
Padding="6,4" BorderBrush="#cdcbcb" BorderThickness="1"
ToolTip="输入关键字搜索算子">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Text" Value="">
<Setter Property="Background">
<Setter.Value>
<VisualBrush AlignmentX="Left" AlignmentY="Center" Stretch="None">
<VisualBrush.Visual>
<TextBlock Text="🔍 搜索算子..." Foreground="#aaa"
FontFamily="Microsoft YaHei UI" FontSize="11"
Margin="4,0,0,0" />
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
FontFamily="{StaticResource CsdFont}"
FontSize="11"
Padding="6,4"
BorderBrush="#cdcbcb"
BorderThickness="1"
ToolTip="输入关键字搜索算子" />
</Border>
<!-- 分组算子列表 -->
<ScrollViewer x:Name="ToolboxListBox" Grid.Row="2" VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding FilteredGroups}">
<ItemsControl.ItemTemplate>
<Grid Grid.Row="2">
<TabControl x:Name="ToolboxListBox"
Margin="8"
ItemContainerStyle="{StaticResource OperatorToolboxTabItemStyle}"
ItemsSource="{Binding FilteredGroups}">
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,2">
<!-- 分类标题 -->
<Border Background="{StaticResource CategoryBg}"
BorderBrush="{StaticResource PanelBorder}"
BorderThickness="0,0,0,1"
Padding="10,6">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding CategoryIcon}" FontSize="13"
VerticalAlignment="Center" Margin="0,0,6,0" />
<TextBlock Text="{Binding CategoryName}"
FontFamily="Microsoft YaHei UI"
FontWeight="SemiBold" FontSize="12"
Foreground="#333" VerticalAlignment="Center" />
</StackPanel>
</Border>
<!-- 分类下的算子 -->
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding CategoryIcon}"
VerticalAlignment="Center" />
<TextBlock Text="{Binding CategoryName}"
Margin="5,0,0,0"
VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Operators}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Padding="12,5,8,5" Cursor="Hand"
<Border Padding="12,6,8,6"
Cursor="Hand"
Background="Transparent"
BorderBrush="Transparent"
BorderBrush="#ECECEC"
BorderThickness="0,0,0,1">
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#E8F0FE" />
<Setter Property="Background" Value="{StaticResource HoverBg}" />
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
<StackPanel Orientation="Horizontal">
<Border Width="26" Height="26"
<Border Width="28"
Height="28"
Background="#EEF2FF"
CornerRadius="4" Margin="0,0,8,0">
CornerRadius="4"
Margin="0,0,8,0">
<TextBlock Text="{Binding IconPath}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
@@ -154,11 +157,35 @@
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</TabControl.ContentTemplate>
</TabControl>
<Border Margin="8"
Padding="18"
Background="#FAFAFA"
BorderBrush="#E6E6E6"
BorderThickness="1"
CornerRadius="6">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=ToolboxListBox, Path=HasItems}" Value="False">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="没有匹配的算子"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontFamily="{StaticResource CsdFont}"
FontSize="12"
Foreground="#666666" />
</Border>
</Grid>
</Grid>
</Border>
</UserControl>
@@ -3,7 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:views="clr-namespace:XplorePlane.Views"
Title="算子工具箱"
Width="260" Height="500"
Width="560" Height="560"
WindowStartupLocation="CenterOwner"
ShowInTaskbar="False"
WindowStyle="None"
@@ -1,11 +1,12 @@
<UserControl
x:Class="XplorePlane.Views.PipelineEditorView"
x:Name="RootControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
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"
d:DesignHeight="700"
d:DesignWidth="350"
d:DesignWidth="300"
mc:Ignorable="d">
<UserControl.Resources>
@@ -16,7 +17,7 @@
<SolidColorBrush x:Key="DisabledNodeBg" Color="#F3F3F3" />
<SolidColorBrush x:Key="DisabledNodeLine" Color="#B9B9B9" />
<SolidColorBrush x:Key="DisabledNodeText" Color="#8A8A8A" />
<FontFamily x:Key="CsdFont">Microsoft YaHei UI</FontFamily>
<FontFamily x:Key="UiFont">Microsoft YaHei UI</FontFamily>
<Style x:Key="PipelineNodeItemStyle" TargetType="ListBoxItem">
<Setter Property="Padding" Value="0" />
@@ -36,16 +37,24 @@
</Style>
<Style x:Key="ToolbarBtn" TargetType="Button">
<Setter Property="Width" Value="52" />
<Setter Property="Height" Value="28" />
<Setter Property="MinWidth" Value="52" />
<Setter Property="Margin" Value="2,0" />
<Setter Property="Padding" Value="8,0" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="#CDCBCB" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="FontFamily" Value="Microsoft YaHei UI" />
<Setter Property="FontFamily" Value="{StaticResource UiFont}" />
<Setter Property="FontSize" Value="11" />
<Setter Property="Cursor" Value="Hand" />
</Style>
<Style x:Key="OperatorPickerTabItemStyle" TargetType="TabItem">
<Setter Property="Padding" Value="12,6" />
<Setter Property="Margin" Value="0,0,4,0" />
<Setter Property="FontFamily" Value="{StaticResource UiFont}" />
<Setter Property="FontSize" Value="11" />
</Style>
</UserControl.Resources>
<Border
@@ -59,7 +68,6 @@
<RowDefinition Height="4*" MinHeight="180" />
<RowDefinition Height="Auto" />
<RowDefinition Height="2*" MinHeight="80" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border
@@ -73,37 +81,38 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel
Grid.Row="0"
Orientation="Horizontal">
<Button
Command="{Binding NewPipelineCommand}"
Content="新建"
Style="{StaticResource ToolbarBtn}"
ToolTip="新建流水线" />
<Button
Command="{Binding SavePipelineCommand}"
Content="保存"
Style="{StaticResource ToolbarBtn}"
ToolTip="保存当前流水线" />
<Button
Width="60"
Command="{Binding SaveAsPipelineCommand}"
Content="另存为"
Style="{StaticResource ToolbarBtn}"
ToolTip="另存当前流水线" />
<Button
Width="52"
Command="{Binding LoadPipelineCommand}"
Content="加载"
Style="{StaticResource ToolbarBtn}"
ToolTip="加载流水线" />
<Button
Command="{Binding NewPipelineCommand}"
Content="新建"
Style="{StaticResource ToolbarBtn}"
ToolTip="新建流水线" />
<Button
Command="{Binding SavePipelineCommand}"
Content="保存"
Style="{StaticResource ToolbarBtn}"
ToolTip="保存当前流水线" />
<Button
Width="64"
Command="{Binding SaveAsPipelineCommand}"
Content="另存为"
Style="{StaticResource ToolbarBtn}"
ToolTip="另存当前流水线" />
<Button
Command="{Binding LoadPipelineCommand}"
Content="加载"
Style="{StaticResource ToolbarBtn}"
ToolTip="加载流水线" />
</StackPanel>
<TextBlock
Grid.Row="1"
Margin="2,4,2,0"
VerticalAlignment="Center"
FontFamily="{StaticResource CsdFont}"
FontFamily="{StaticResource UiFont}"
FontSize="11"
FontWeight="SemiBold"
Foreground="#333333"
@@ -182,13 +191,13 @@
<StackPanel Grid.Column="1" Margin="6,0,0,0" VerticalAlignment="Center">
<TextBlock
x:Name="NodeTitle"
FontFamily="Microsoft YaHei UI"
FontFamily="{StaticResource UiFont}"
FontSize="12"
Text="{Binding DisplayName}" />
<TextBlock
x:Name="NodeState"
Margin="0,2,0,0"
FontFamily="Microsoft YaHei UI"
FontFamily="{StaticResource UiFont}"
FontSize="10"
Foreground="#6E6E6E"
Text="已启用" />
@@ -240,7 +249,7 @@
<StackPanel Margin="8,6">
<TextBlock
Margin="0,0,0,4"
FontFamily="{StaticResource CsdFont}"
FontFamily="{StaticResource UiFont}"
FontSize="11"
FontWeight="Bold"
Foreground="#555"
@@ -263,25 +272,25 @@
<ColumnDefinition Width="100" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
VerticalAlignment="Center"
FontFamily="Microsoft YaHei UI"
FontFamily="{StaticResource UiFont}"
FontSize="11"
Text="{Binding DisplayName}"
TextTrimming="CharacterEllipsis" />
<TextBox
x:Name="TextValueEditor"
Grid.Column="1"
Padding="4,2"
BorderBrush="#CDCBCB"
BorderThickness="1"
FontFamily="Microsoft YaHei UI"
FontFamily="{StaticResource UiFont}"
FontSize="11"
Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}">
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="BorderBrush" Value="#CDCBCB" />
<Setter Property="Background" Value="White" />
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
@@ -295,13 +304,14 @@
</Style>
</TextBox.Style>
</TextBox>
<ComboBox
Grid.Column="1"
MinHeight="24"
Padding="4,1"
BorderBrush="#CDCBCB"
BorderThickness="1"
FontFamily="Microsoft YaHei UI"
FontFamily="{StaticResource UiFont}"
FontSize="11"
ItemsSource="{Binding Options}"
SelectedItem="{Binding SelectedOption, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
@@ -316,10 +326,11 @@
</Style>
</ComboBox.Style>
</ComboBox>
<CheckBox
Grid.Column="1"
VerticalAlignment="Center"
FontFamily="Microsoft YaHei UI"
FontFamily="{StaticResource UiFont}"
FontSize="11"
IsChecked="{Binding BoolValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<CheckBox.Style>
@@ -340,41 +351,188 @@
</StackPanel>
</ScrollViewer>
<Border
Grid.Row="4"
Height="24"
Padding="6,0"
BorderThickness="0,1,0,0">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="#F5F5F5" />
<Setter Property="BorderBrush" Value="{StaticResource PanelBorder}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsStatusError}" Value="True">
<Setter Property="Background" Value="#FFF1F1" />
<Setter Property="BorderBrush" Value="#D9534F" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock
VerticalAlignment="Center"
FontFamily="{StaticResource CsdFont}"
FontSize="11"
Text="{Binding StatusMessage, StringFormat='Status: {0}'}"
TextTrimming="CharacterEllipsis">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="#555" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsStatusError}" Value="True">
<Setter Property="Foreground" Value="#A12A2A" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Border>
<Grid
x:Name="OperatorPickerOverlay"
Grid.RowSpan="4"
Background="#66000000"
Panel.ZIndex="10"
Visibility="Collapsed"
MouseDown="OnOperatorPickerBackdropMouseDown">
<Border
Width="560"
MaxHeight="560"
Background="White"
BorderBrush="#CFCFCF"
BorderThickness="1"
CornerRadius="8"
MouseDown="OnOperatorPickerDialogMouseDown">
<Grid x:Name="OperatorPickerPanel">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border
Grid.Row="0"
Padding="14,10"
Background="#F6F8FB"
BorderBrush="#E5E5E5"
BorderThickness="0,0,0,1">
<DockPanel LastChildFill="False">
<StackPanel DockPanel.Dock="Left">
<TextBlock
FontFamily="{StaticResource UiFont}"
FontSize="14"
FontWeight="SemiBold"
Text="选择算子" />
<TextBlock
Margin="0,3,0,0"
FontFamily="{StaticResource UiFont}"
FontSize="11"
Foreground="#666666"
Text="按类别切换 Tab,单击即可添加到当前流水线" />
</StackPanel>
<Button
Width="28"
Height="28"
Margin="8,0,0,0"
Click="OnCloseOperatorPickerClick"
Content="×"
Cursor="Hand"
DockPanel.Dock="Right"
FontSize="14" />
</DockPanel>
</Border>
<Border
Grid.Row="1"
Padding="14,10"
BorderBrush="#E5E5E5"
BorderThickness="0,0,0,1">
<TextBox
x:Name="OperatorSearchBox"
Padding="8,5"
FontFamily="{StaticResource UiFont}"
FontSize="11"
Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}"
ToolTip="输入名称或分类过滤算子" />
</Border>
<Grid Grid.Row="2" Margin="14,10,14,14">
<TabControl
x:Name="OperatorCategoryTabs"
ItemContainerStyle="{StaticResource OperatorPickerTabItemStyle}"
ItemsSource="{Binding FilteredGroups}">
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding CategoryIcon}" />
<TextBlock Margin="6,0,0,0" Text="{Binding CategoryName}" />
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Operators}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button
Margin="0,0,0,8"
Padding="10,8"
Background="White"
BorderBrush="#D8DDE6"
BorderThickness="1"
Click="OnOperatorPickerItemClick"
Cursor="Hand"
HorizontalContentAlignment="Stretch"
Tag="{Binding Key}">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border
x:Name="OperatorCard"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="6"
Padding="{TemplateBinding Padding}">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="OperatorCard" Property="Background" Value="#EDF5FF" />
<Setter TargetName="OperatorCard" Property="BorderBrush" Value="#8EB8E8" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
<DockPanel LastChildFill="True">
<Border
Width="32"
Height="32"
Margin="0,0,10,0"
Background="#EEF4FF"
CornerRadius="5"
DockPanel.Dock="Left">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="15"
Text="{Binding IconPath}" />
</Border>
<StackPanel>
<TextBlock
FontFamily="{StaticResource UiFont}"
FontSize="12"
FontWeight="SemiBold"
Foreground="#1F1F1F"
Text="{Binding DisplayName}" />
<TextBlock
Margin="0,3,0,0"
FontFamily="Consolas"
FontSize="10"
Foreground="#7A7A7A"
Text="{Binding Key}" />
</StackPanel>
</DockPanel>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
<Border
Padding="18"
Background="#FAFAFA"
BorderBrush="#E6E6E6"
BorderThickness="1"
CornerRadius="6">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=OperatorCategoryTabs, Path=HasItems}" Value="False">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontFamily="{StaticResource UiFont}"
FontSize="12"
Foreground="#666666"
Text="没有匹配的算子" />
</Border>
</Grid>
</Grid>
</Border>
</Grid>
</Grid>
</Border>
</UserControl>
@@ -10,21 +10,11 @@
ShowInTaskbar="False">
<Grid Background="#F3F3F3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="240" MinWidth="200" />
<ColumnDefinition Width="*" MinWidth="400" />
<ColumnDefinition Width="250" MinWidth="250" />
<ColumnDefinition Width="320" MinWidth="300" />
</Grid.ColumnDefinitions>
<Border Grid.Column="0"
Margin="8"
Background="White"
BorderBrush="#D0D0D0"
BorderThickness="1"
CornerRadius="4">
<views:OperatorToolboxView />
</Border>
<Border Grid.Column="1"
Margin="8,8,4,8"
Background="White"
BorderBrush="#D0D0D0"
@@ -34,7 +24,7 @@
Background="White" />
</Border>
<Border Grid.Column="2"
<Border Grid.Column="1"
Margin="4,8,8,8"
Background="White"
BorderBrush="#D0D0D0"
+60 -68
View File
@@ -390,6 +390,10 @@
</telerik:RadRibbonGroup>
</telerik:RadRibbonTab>
<telerik:RadRibbonTab Header="操作">
</telerik:RadRibbonTab>
<telerik:RadRibbonTab Header="设置">
<telerik:RadRibbonGroup
telerik:ScreenTip.Description="Show the Alignment tab of the Format Cells dialog box."
@@ -407,22 +411,7 @@
<spreadsheetControls:RadVerticalAlignmentToBooleanConverter x:Key="verticalAlignmentToBooleanConverter" />
</telerik:RadRibbonGroup.Resources>
<StackPanel>
<telerik:RadRibbonToggleButton
telerik:ScreenTip.Description="暖机"
telerik:ScreenTip.Title="暖机"
Command="{Binding WarmUpCommand}"
Size="Medium"
SmallImage="/Assets/Icons/heat-engine.png"
Text="暖机" />
<telerik:RadRibbonToggleButton
telerik:ScreenTip.Description="轴复位"
telerik:ScreenTip.Title="轴复位"
Command="{Binding AxisResetCommand}"
Size="Medium"
SmallImage="/Assets/Icons/home.png"
Text="轴复位" />
</StackPanel>
<StackPanel/>
<StackPanel>
<telerik:RadRibbonToggleButton
@@ -433,56 +422,67 @@
SmallImage="/Assets/Icons/xray.png"
Text="射线源" />
<telerik:RadRibbonToggleButton
telerik:ScreenTip.Description="探测器控制"
telerik:ScreenTip.Title="探测器"
Command="{Binding OpenDetectorConfigCommand}"
Size="Medium"
SmallImage="/Assets/Icons/detector2.png"
Text="探测器" />
telerik:ScreenTip.Description="探测器控制"
telerik:ScreenTip.Title="探测器"
Command="{Binding OpenDetectorConfigCommand}"
Size="Medium"
SmallImage="/Assets/Icons/detector2.png"
Text="探测器" />
<telerik:RadRibbonToggleButton
telerik:ScreenTip.Description="运动控制"
telerik:ScreenTip.Title="运动控制"
Command="{Binding OpenMotionDebugCommand}"
Size="Medium"
SmallImage="/Assets/Icons/xyz.png"
Text="运动控制" />
telerik:ScreenTip.Description="运动控制"
telerik:ScreenTip.Title="运动控制"
Command="{Binding OpenMotionDebugCommand}"
Size="Medium"
SmallImage="/Assets/Icons/xyz.png"
Text="运动控制" />
</StackPanel>
<StackPanel>
<telerik:RadRibbonButton
telerik:ScreenTip.Description="打开相机参数设置窗口"
telerik:ScreenTip.Title="相机设置"
Command="{Binding OpenCameraSettingsCommand}"
Size="Medium"
SmallImage="/Assets/Icons/detector2.png"
Text="相机设置" />
telerik:ScreenTip.Description="打开相机参数设置窗口"
telerik:ScreenTip.Title="相机设置"
Command="{Binding OpenCameraSettingsCommand}"
Size="Medium"
SmallImage="/Assets/Icons/detector2.png"
Text="相机设置" />
<telerik:RadRibbonButton
telerik:ScreenTip.Description="打开 PLC 地址配置窗口"
telerik:ScreenTip.Title="PLC 地址配置"
Command="{Binding OpenPlcAddrConfigCommand}"
Size="Medium"
SmallImage="/Assets/Icons/tools.png"
Text="PLC 地址" />
telerik:ScreenTip.Description="打开 PLC 地址配置窗口"
telerik:ScreenTip.Title="PLC 地址配置"
Command="{Binding OpenPlcAddrConfigCommand}"
Size="Medium"
SmallImage="/Assets/Icons/tools.png"
Text="PLC 地址" />
</StackPanel>
</telerik:RadRibbonGroup>
<telerik:RadRibbonGroup Header="多语言">
<telerik:RadRibbonGroup.Variants>
<telerik:GroupVariant Priority="0" Variant="Large" />
</telerik:RadRibbonGroup.Variants>
<telerik:RadRibbonButton
telerik:ScreenTip.Description="切换应用程序显示语言"
telerik:ScreenTip.Title="多语言设置"
Size="Large"
SmallImage="/Assets/Icons/tools.png"
Command="{Binding OpenLanguageSwitcherCommand}"
Text="语言设置" />
telerik:ScreenTip.Description="切换应用程序显示语言"
telerik:ScreenTip.Title="多语言设置"
Size="Large"
SmallImage="/Assets/Icons/tools.png"
Command="{Binding OpenLanguageSwitcherCommand}"
Text="语言设置" />
</telerik:RadRibbonGroup>
<telerik:RadRibbonGroup Header="日志">
<telerik:RadRibbonGroup.Variants>
<telerik:GroupVariant Priority="0" Variant="Large" />
</telerik:RadRibbonGroup.Variants>
<telerik:RadRibbonButton
telerik:ScreenTip.Description="打开实时日志查看器"
telerik:ScreenTip.Title="查看日志"
Size="Large"
SmallImage="/Assets/Icons/message.png"
Command="{Binding OpenRealTimeLogViewerCommand}"
Text="查看日志" />
telerik:ScreenTip.Description="打开实时日志查看器"
telerik:ScreenTip.Title="查看日志"
Size="Large"
SmallImage="/Assets/Icons/message.png"
Command="{Binding OpenRealTimeLogViewerCommand}"
Text="查看日志" />
</telerik:RadRibbonGroup>
</telerik:RadRibbonTab>
<telerik:RadRibbonTab Header="关于">
@@ -493,26 +493,18 @@
<telerik:RadRibbonButton
Size="Large"
SmallImage="/Assets/Icons/message.png"
Command="{Binding OpenUserManualCommand}"
Text="帮助文档" />
Size="Large"
SmallImage="/Assets/Icons/message.png"
Command="{Binding OpenUserManualCommand}"
Text="帮助文档" />
<telerik:RadRibbonButton
Size="Large"
SmallImage="/Assets/Icons/tools.png"
Command="{Binding OpenLibraryVersionsCommand}"
Text="关于" />
Size="Large"
SmallImage="/Assets/Icons/tools.png"
Command="{Binding OpenLibraryVersionsCommand}"
Text="关于" />
</telerik:RadRibbonGroup>
</telerik:RadRibbonTab>
<telerik:RadRibbonView.ContextualGroups>
<telerik:RadRibbonContextualGroup
x:Name="PictureTools"
Header="Picture Tools"
IsActive="{Binding Path=PictureToolsTab.IsEnabled, Mode=OneWay}" />
</telerik:RadRibbonView.ContextualGroups>
</telerik:RadRibbonView>
<Grid
@@ -26,7 +26,7 @@
VerticalAlignment="Center"
FontWeight="SemiBold"
Foreground="#333333"
Text="相机预览" />
Text="导航" />
</Border>
<!-- 相机图像显示区域 -->
+6
View File
@@ -15,7 +15,10 @@
<ItemGroup>
<Page Remove="MainWindow.xaml" />
<Page Remove="Views\ImageProcessing\ImageProcessingPanelView.xaml" />
<Page Remove="Views\ImageProcessing\ImageProcessingWindow.xaml" />
<Page Remove="Views\Main\MainWindowB.xaml" />
<Page Remove="Views\Main\NavigationPanelView.xaml" />
</ItemGroup>
<!-- NuGet 包引用 -->
@@ -146,7 +149,10 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>Libs\Hardware\zh-TW\%(Filename)%(Extension)</Link>
</None>
<Compile Remove="Views\ImageProcessing\ImageProcessingPanelView.xaml.cs" />
<Compile Remove="Views\ImageProcessing\ImageProcessingWindow.xaml.cs" />
<Compile Remove="Views\Main\MainWindowB.xaml.cs" />
<Compile Remove="Views\Main\NavigationPanelView.xaml.cs" />
<Content Include="XplorerPlane.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>