实现加载图像到实时图像区
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using Moq;
|
||||
using Prism.Events;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows.Media.Imaging;
|
||||
@@ -32,7 +33,7 @@ namespace XplorePlane.Tests.Pipeline
|
||||
}
|
||||
|
||||
private PipelineEditorViewModel CreateVm() =>
|
||||
new PipelineEditorViewModel(_mockImageSvc.Object, _mockExecSvc.Object, _mockPersistSvc.Object, _mockLogger.Object);
|
||||
new PipelineEditorViewModel(_mockImageSvc.Object, _mockExecSvc.Object, _mockPersistSvc.Object, new EventAggregator(), _mockLogger.Object);
|
||||
|
||||
// ── 6.1 AddOperatorCommand ────────────────────────────────────
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using FsCheck;
|
||||
using FsCheck.Fluent;
|
||||
using FsCheck.Xunit;
|
||||
using Moq;
|
||||
using Prism.Events;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -31,7 +32,7 @@ namespace XplorePlane.Tests.Pipeline
|
||||
var mockPersistSvc = new Mock<IPipelinePersistenceService>();
|
||||
var mockLogger = new Mock<ILoggerService>();
|
||||
mockLogger.Setup(l => l.ForModule<PipelineEditorViewModel>()).Returns(mockLogger.Object);
|
||||
return new PipelineEditorViewModel(mockImageSvc.Object, mockExecSvc.Object, mockPersistSvc.Object, mockLogger.Object);
|
||||
return new PipelineEditorViewModel(mockImageSvc.Object, mockExecSvc.Object, mockPersistSvc.Object, new EventAggregator(), mockLogger.Object);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
using Prism.Events;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace XplorePlane.Events
|
||||
{
|
||||
public sealed class ManualImageLoadedEvent : PubSubEvent<ManualImageLoadedPayload>
|
||||
{
|
||||
}
|
||||
|
||||
public sealed class ManualImageLoadedPayload
|
||||
{
|
||||
public ManualImageLoadedPayload(BitmapSource image, string filePath)
|
||||
{
|
||||
Image = image;
|
||||
FilePath = filePath;
|
||||
}
|
||||
|
||||
public BitmapSource Image { get; }
|
||||
public string FilePath { get; }
|
||||
public string FileName => System.IO.Path.GetFileName(FilePath);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.Win32;
|
||||
using Prism.Events;
|
||||
using Prism.Commands;
|
||||
using Prism.Mvvm;
|
||||
using System;
|
||||
@@ -9,6 +10,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media.Imaging;
|
||||
using XP.Common.Logging.Interfaces;
|
||||
using XplorePlane.Events;
|
||||
using XplorePlane.Models;
|
||||
using XplorePlane.Services;
|
||||
|
||||
@@ -22,6 +24,7 @@ namespace XplorePlane.ViewModels
|
||||
private readonly IImageProcessingService _imageProcessingService;
|
||||
private readonly IPipelineExecutionService _executionService;
|
||||
private readonly IPipelinePersistenceService _persistenceService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly ILoggerService _logger;
|
||||
|
||||
private PipelineNodeViewModel _selectedNode;
|
||||
@@ -40,11 +43,13 @@ namespace XplorePlane.ViewModels
|
||||
IImageProcessingService imageProcessingService,
|
||||
IPipelineExecutionService executionService,
|
||||
IPipelinePersistenceService persistenceService,
|
||||
IEventAggregator eventAggregator,
|
||||
ILoggerService logger)
|
||||
{
|
||||
_imageProcessingService = imageProcessingService ?? throw new ArgumentNullException(nameof(imageProcessingService));
|
||||
_executionService = executionService ?? throw new ArgumentNullException(nameof(executionService));
|
||||
_persistenceService = persistenceService ?? throw new ArgumentNullException(nameof(persistenceService));
|
||||
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
|
||||
_logger = logger?.ForModule<PipelineEditorViewModel>() ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
PipelineNodes = new ObservableCollection<PipelineNodeViewModel>();
|
||||
@@ -64,6 +69,9 @@ namespace XplorePlane.ViewModels
|
||||
OpenToolboxCommand = new DelegateCommand(OpenToolbox);
|
||||
MoveNodeUpCommand = new DelegateCommand<PipelineNodeViewModel>(MoveNodeUp);
|
||||
MoveNodeDownCommand = new DelegateCommand<PipelineNodeViewModel>(MoveNodeDown);
|
||||
|
||||
_eventAggregator.GetEvent<ManualImageLoadedEvent>()
|
||||
.Subscribe(OnManualImageLoaded);
|
||||
}
|
||||
|
||||
// ── State Properties ──────────────────────────────────────────
|
||||
@@ -306,6 +314,7 @@ namespace XplorePlane.ViewModels
|
||||
|
||||
PreviewImage = result;
|
||||
StatusMessage = "流水线执行完成";
|
||||
PublishPipelinePreviewUpdated(result, StatusMessage);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
@@ -362,6 +371,45 @@ namespace XplorePlane.ViewModels
|
||||
SourceImage = bitmap;
|
||||
PreviewImage = bitmap;
|
||||
StatusMessage = $"已加载图像:{Path.GetFileName(filePath)}";
|
||||
PublishManualImageLoaded(bitmap, filePath);
|
||||
}
|
||||
|
||||
internal void LoadImageFromBitmap(BitmapSource bitmap, string filePath, bool runPipeline = true)
|
||||
{
|
||||
if (bitmap == null)
|
||||
throw new ArgumentNullException(nameof(bitmap));
|
||||
|
||||
SourceImage = bitmap;
|
||||
PreviewImage = bitmap;
|
||||
StatusMessage = $"已加载图像:{Path.GetFileName(filePath)}";
|
||||
PublishManualImageLoaded(bitmap, filePath);
|
||||
|
||||
if (runPipeline)
|
||||
TriggerDebouncedExecution();
|
||||
}
|
||||
|
||||
private void PublishManualImageLoaded(BitmapSource bitmap, string filePath)
|
||||
{
|
||||
_eventAggregator.GetEvent<ManualImageLoadedEvent>()
|
||||
.Publish(new ManualImageLoadedPayload(bitmap, filePath));
|
||||
}
|
||||
|
||||
private void PublishPipelinePreviewUpdated(BitmapSource bitmap, string statusMessage)
|
||||
{
|
||||
if (bitmap == null) return;
|
||||
|
||||
_eventAggregator.GetEvent<PipelinePreviewUpdatedEvent>()
|
||||
.Publish(new PipelinePreviewUpdatedPayload(bitmap, statusMessage));
|
||||
}
|
||||
|
||||
private void OnManualImageLoaded(ManualImageLoadedPayload payload)
|
||||
{
|
||||
if (payload?.Image == null) return;
|
||||
if (ReferenceEquals(SourceImage, payload.Image)) return;
|
||||
|
||||
SourceImage = payload.Image;
|
||||
PreviewImage = payload.Image;
|
||||
StatusMessage = $"已加载图像:{payload.FileName}";
|
||||
}
|
||||
|
||||
private void CancelExecution()
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
using Prism.Commands;
|
||||
using Prism.Events;
|
||||
using Prism.Ioc;
|
||||
using Prism.Mvvm;
|
||||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Configuration;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Media.Imaging;
|
||||
using XplorePlane.Events;
|
||||
using XP.Common.Logging.Interfaces;
|
||||
using XP.Common.PdfViewer.Interfaces;
|
||||
using XP.Hardware.MotionControl.Abstractions;
|
||||
@@ -16,6 +20,7 @@ namespace XplorePlane.ViewModels
|
||||
{
|
||||
private readonly ILoggerService _logger;
|
||||
private readonly IContainerProvider _containerProvider;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private string _licenseInfo = "当前时间";
|
||||
|
||||
public string LicenseInfo
|
||||
@@ -36,6 +41,7 @@ namespace XplorePlane.ViewModels
|
||||
|
||||
// 窗口打开命令
|
||||
public DelegateCommand OpenImageProcessingCommand { get; }
|
||||
public DelegateCommand LoadImageCommand { get; }
|
||||
public DelegateCommand OpenPipelineEditorCommand { get; }
|
||||
public DelegateCommand OpenCncEditorCommand { get; }
|
||||
public DelegateCommand OpenMatrixEditorCommand { get; }
|
||||
@@ -64,10 +70,11 @@ namespace XplorePlane.ViewModels
|
||||
private Window _toolboxWindow;
|
||||
private Window _raySourceConfigWindow;
|
||||
|
||||
public MainViewModel(ILoggerService logger, IContainerProvider containerProvider)
|
||||
public MainViewModel(ILoggerService logger, IContainerProvider containerProvider, IEventAggregator eventAggregator)
|
||||
{
|
||||
_logger = logger?.ForModule<MainViewModel>() ?? throw new ArgumentNullException(nameof(logger));
|
||||
_containerProvider = containerProvider ?? throw new ArgumentNullException(nameof(containerProvider));
|
||||
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
|
||||
|
||||
NavigationTree = new ObservableCollection<object>();
|
||||
|
||||
@@ -81,6 +88,7 @@ namespace XplorePlane.ViewModels
|
||||
|
||||
// 窗口打开命令
|
||||
OpenImageProcessingCommand = new DelegateCommand(() => ShowWindow(new Views.ImageProcessingWindow(), "图像处理"));
|
||||
LoadImageCommand = new DelegateCommand(ExecuteLoadImage);
|
||||
OpenPipelineEditorCommand = new DelegateCommand(() => ShowWindow(new Views.PipelineEditorWindow(), "流水线编辑器"));
|
||||
OpenCncEditorCommand = new DelegateCommand(() => ShowWindow(new Views.Cnc.CncEditorWindow(), "CNC 编辑器"));
|
||||
OpenMatrixEditorCommand = new DelegateCommand(() => ShowWindow(new Views.Cnc.MatrixEditorWindow(), "矩阵编排"));
|
||||
@@ -260,6 +268,36 @@ namespace XplorePlane.ViewModels
|
||||
() => new XP.Hardware.RaySource.Views.RaySourceConfigWindow(), "射线源配置");
|
||||
}
|
||||
|
||||
private void ExecuteLoadImage()
|
||||
{
|
||||
var dialog = new OpenFileDialog
|
||||
{
|
||||
Title = "加载图像",
|
||||
Filter = "图像文件|*.bmp;*.png;*.jpg;*.jpeg;*.tif;*.tiff|所有文件|*.*"
|
||||
};
|
||||
|
||||
if (dialog.ShowDialog() != true)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
var bitmap = new BitmapImage();
|
||||
bitmap.BeginInit();
|
||||
bitmap.UriSource = new Uri(dialog.FileName, UriKind.Absolute);
|
||||
bitmap.CacheOption = BitmapCacheOption.OnLoad;
|
||||
bitmap.EndInit();
|
||||
bitmap.Freeze();
|
||||
|
||||
_eventAggregator.GetEvent<ManualImageLoadedEvent>()
|
||||
.Publish(new ManualImageLoadedPayload(bitmap, dialog.FileName));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(ex, "加载图像失败:{Path}", dialog.FileName);
|
||||
MessageBox.Show($"加载图像失败:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecuteWarmUp()
|
||||
{
|
||||
var messageBoxResult = MessageBox.Show("确认执行射线源暖机操作?", "暖机",
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Windows.Media.Imaging;
|
||||
using XP.Common.Logging.Interfaces;
|
||||
using XP.Hardware.Detector.Abstractions;
|
||||
using XP.Hardware.Detector.Abstractions.Events;
|
||||
using XplorePlane.Events;
|
||||
|
||||
namespace XplorePlane.ViewModels
|
||||
{
|
||||
@@ -39,6 +40,10 @@ namespace XplorePlane.ViewModels
|
||||
|
||||
eventAggregator.GetEvent<ImageCapturedEvent>()
|
||||
.Subscribe(OnImageCaptured, ThreadOption.BackgroundThread);
|
||||
eventAggregator.GetEvent<ManualImageLoadedEvent>()
|
||||
.Subscribe(OnManualImageLoaded, ThreadOption.UIThread);
|
||||
eventAggregator.GetEvent<PipelinePreviewUpdatedEvent>()
|
||||
.Subscribe(OnPipelinePreviewUpdated, ThreadOption.UIThread);
|
||||
}
|
||||
|
||||
private void OnImageCaptured(ImageCapturedEventArgs args)
|
||||
@@ -75,6 +80,22 @@ namespace XplorePlane.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
private void OnManualImageLoaded(ManualImageLoadedPayload payload)
|
||||
{
|
||||
if (payload?.Image == null) return;
|
||||
|
||||
ImageSource = payload.Image;
|
||||
ImageInfo = $"手动加载: {payload.FileName}";
|
||||
}
|
||||
|
||||
private void OnPipelinePreviewUpdated(PipelinePreviewUpdatedPayload payload)
|
||||
{
|
||||
if (payload?.Image == null) return;
|
||||
|
||||
ImageSource = payload.Image;
|
||||
ImageInfo = payload.StatusMessage;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 16 位灰度数据线性拉伸为 8 位 BitmapSource(委托给 XP.Common 通用转换器)
|
||||
/// </summary>
|
||||
|
||||
@@ -89,12 +89,14 @@
|
||||
Content="加载"
|
||||
Style="{StaticResource ToolbarBtn}"
|
||||
ToolTip="加载流水线" />
|
||||
<!--
|
||||
<Button
|
||||
Width="64"
|
||||
Command="{Binding LoadImageCommand}"
|
||||
Content="加载图像"
|
||||
Style="{StaticResource ToolbarBtn}"
|
||||
ToolTip="加载输入图像" />
|
||||
-->
|
||||
<Button
|
||||
Command="{Binding ExecutePipelineCommand}"
|
||||
Content="▶"
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace XplorePlane.Views
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (DataContext == null)
|
||||
if (DataContext is not PipelineEditorViewModel)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -136,7 +136,11 @@
|
||||
</StackPanel>
|
||||
</telerik:RadRibbonGroup>
|
||||
<telerik:RadRibbonGroup Header="快捷工具">
|
||||
|
||||
<telerik:RadRibbonButton
|
||||
Command="{Binding LoadImageCommand}"
|
||||
Size="Large"
|
||||
SmallImage="/Assets/Icons/open.png"
|
||||
Text="加载图像" />
|
||||
<!-- 快捷工具: 上下两列,带文字 -->
|
||||
<StackPanel>
|
||||
<telerik:RadRibbonButton
|
||||
@@ -384,7 +388,7 @@
|
||||
Size="Large"
|
||||
SmallImage="/Assets/Icons/spiral.png" />
|
||||
</telerik:RadRibbonGroup>
|
||||
|
||||
<!--
|
||||
<telerik:RadRibbonGroup Header="图像处理">
|
||||
<telerik:RadRibbonGroup.Variants>
|
||||
<telerik:GroupVariant Priority="0" Variant="Large" />
|
||||
@@ -395,6 +399,7 @@
|
||||
SmallImage="/Assets/Icons/workflow.png"
|
||||
Text="流水线编辑器" />
|
||||
</telerik:RadRibbonGroup>
|
||||
-->
|
||||
</telerik:RadRibbonTab>
|
||||
<telerik:RadRibbonTab Header="关于">
|
||||
<telerik:RadRibbonGroup Header="关于">
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
---------------------------------------------------------------
|
||||
__ __ _ _____ _
|
||||
\ \ / / | | | __ \| |
|
||||
\ V / _ __ | | ___ _ __ ___| |__) | | __ _ _ __ ___
|
||||
> < | '_ \| |/ _ \| '__/ _ \ ___/| |/ _` | '_ \ / _ \
|
||||
/ . \| |_) | | (_) | | | __/ | | | (_| | | | | __/
|
||||
/_/ \_\ .__/|_|\___/|_| \___|_| |_|\__,_|_| |_|\___|
|
||||
| |
|
||||
|_|
|
||||
---------------------------------------------------------------
|
||||
|
||||
|
||||
2026.3.14
|
||||
----------------------
|
||||
1、主页面的布局与拆分 √
|
||||
2、硬件层射线源的集成 √
|
||||
3、图像层集成,包括复刻一个示例界面,优化界面布局及算子中文 √
|
||||
4、浮动图像处理工具箱调研 √
|
||||
5、修复图像工具箱拖拽事件,流水线列表没有生成对应的控件 √
|
||||
|
||||
2026.3.16
|
||||
----------------------
|
||||
1、优化图像处理窗体的页面布局,简洁清晰 √
|
||||
2、新增打开图像工具箱(修复DataContext问题) √
|
||||
3、对主界面B方案进行优化 √
|
||||
|
||||
2026.3.17
|
||||
----------------------
|
||||
1、对界面设计进行优化 ,增加了扫描模式,移除了探测器设置,增加底部工具栏 √
|
||||
|
||||
|
||||
2026.3.18
|
||||
----------------------
|
||||
1、全局数据结构的考虑与设计(多个窗体可以调用公共的数据,如射线源状态,探测器状态,运动位置,图像等) √
|
||||
2、将计划窗体默认隐藏,只有CNC状态下展开 √
|
||||
|
||||
|
||||
2026.3.20
|
||||
----------------------
|
||||
1、软件主界面设计讨论,暂定初稿,给出效果图设计 √
|
||||
2、日志该用XP.Common库和多语言的学习 √
|
||||
|
||||
|
||||
2026.3.26
|
||||
----------------------
|
||||
1、各窗体间数据流的传递,全局数据结构的设计(包括一个基本的说明文档)√
|
||||
2、将telerik 升级到 2024.1.408.310;调整界面和主题;引入 硬件层依赖 √
|
||||
3、图像算子流程文件,保存文件后缀 .imw, image process workflow 缩写 √
|
||||
4、CNC保存文件后缀为.xp, 表示 XplorePlane CNC file 的缩写 √
|
||||
5、硬件层射线源控件的初步集成(采用库层面的自定义控件方式) √
|
||||
PrismBootstrapper 的执行顺序是:RegisterTypes() → ConfigureModuleCatalog() → InitializeModules() → CreateShell()
|
||||
|
||||
|
||||
2026.4.16
|
||||
----------------------
|
||||
CNC及矩阵功能的设计与评审,包含以下功能: √
|
||||
1、CNC功能设计与实现,包含以下功能:
|
||||
a. CNC状态的定义和管理
|
||||
b. CNC界面设计与实现
|
||||
c. CNC相关数据的传递和处理
|
||||
2、CNC相关的编排工具,如插入节点,插入位置,图像模块,等
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
2026.4.20
|
||||
----------------------
|
||||
1、图像算子工具箱的图标 √
|
||||
2、最新的图像算子集成到图像工具箱 √
|
||||
3、修复流程图编辑器界面及初步的功能 √
|
||||
4、主页面加载图像的功能
|
||||
5、
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
TO-DO
|
||||
----------------------
|
||||
Reference in New Issue
Block a user