BGA辅助面板:气泡/焊球模式切换、VoidLimit面板调节、去掉标签右键编辑、未完成组气泡可拖拽调整

This commit is contained in:
李伟
2026-04-27 14:29:54 +08:00
parent f9a14b0771
commit fab0668da8
4 changed files with 188 additions and 5 deletions
@@ -394,6 +394,23 @@ namespace XP.ImageProcessing.RoiControl.Controls
public void SetBubbleBrushSize(int val) => _bubbleBrushSize = val;
public Rect? BubbleRoi => _bubbleRoi;
/// <summary>设置 BGA 测量的气泡/焊球绘制模式</summary>
public void SetBgaDrawBall(bool drawBall)
{
_bgaDrawBall = drawBall;
RaiseMeasureStatusChanged(drawBall ? "BGA - 画焊球模式" : "BGA - 画气泡模式");
}
/// <summary>设置所有 BGA 组的 VoidLimit 并刷新标签</summary>
public void SetBgaVoidLimit(double limit)
{
foreach (var g in _bgaGroups)
{
g.VoidLimit = limit;
g.UpdateLabel();
}
}
// 拖拽状态
private Ellipse _mDraggingDot;
private object _mDraggingOwner;
@@ -722,9 +739,7 @@ namespace XP.ImageProcessing.RoiControl.Controls
{
_bgaCurrent = new Models.BgaVoidGroup();
var currentGroup = _bgaCurrent; // 局部变量供闭包捕获
_bgaCurrent.Label = new TextBlock { FontSize = 13, FontWeight = FontWeights.Bold, Cursor = System.Windows.Input.Cursors.Hand, Visibility = Visibility.Collapsed };
_bgaCurrent.Label.SetValue(ContextMenuService.IsEnabledProperty, false);
_bgaCurrent.Label.PreviewMouseRightButtonUp += (s, ev) => { ShowBgaLimitEditor(currentGroup); ev.Handled = true; };
_bgaCurrent.Label = new TextBlock { FontSize = 13, FontWeight = FontWeights.Bold, IsHitTestVisible = false, Visibility = Visibility.Collapsed };
_measureOverlay.Children.Add(_bgaCurrent.Label);
_bgaDrawBall = false;
RaiseMeasureStatusChanged("BGA空隙 - 点击画气泡圆心(右键切换为画焊球)");
@@ -924,10 +939,12 @@ namespace XP.ImageProcessing.RoiControl.Controls
if (g.E4BH == dot) { _mDraggingOwner = g; _mDraggingRole = "E4B"; break; }
}
}
// 查找 BGA 组
// 查找 BGA 组(已完成的 + 正在编辑的)
if (_mDraggingOwner == null)
{
foreach (var g in _bgaGroups)
var allBga = new System.Collections.Generic.List<Models.BgaVoidGroup>(_bgaGroups);
if (_bgaCurrent != null) allBga.Add(_bgaCurrent);
foreach (var g in allBga)
{
if (g.Ball?.CenterDot == dot) { _mDraggingOwner = g; _mDraggingRole = "BallCenter"; break; }
if (g.Ball?.EdgeDot == dot) { _mDraggingOwner = g; _mDraggingRole = "BallEdge"; break; }
@@ -492,11 +492,29 @@ namespace XplorePlane.ViewModels
_eventAggregator.GetEvent<MeasurementToolEvent>().Publish(MeasurementToolMode.ThroughHoleFillRate);
}
private Window _bgaMeasurePanel;
private void ExecuteBgaVoidMeasure()
{
if (!CheckImageLoaded()) return;
_logger.Info("BGA空隙测量功能已触发");
_eventAggregator.GetEvent<MeasurementToolEvent>().Publish(MeasurementToolMode.BgaVoid);
if (_bgaMeasurePanel != null && _bgaMeasurePanel.IsVisible)
{
_bgaMeasurePanel.Activate();
return;
}
_bgaMeasurePanel = new Views.ImageProcessing.BgaMeasurePanel
{
Owner = System.Windows.Application.Current.MainWindow
};
_bgaMeasurePanel.Closed += (s, e) =>
{
_eventAggregator.GetEvent<MeasurementToolEvent>().Publish(MeasurementToolMode.None);
};
_bgaMeasurePanel.Show();
}
private Window _bubbleMeasurePanel;
@@ -0,0 +1,74 @@
<Window
x:Class="XplorePlane.Views.ImageProcessing.BgaMeasurePanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="BGA空隙测量"
Width="240" Height="260"
ResizeMode="NoResize"
WindowStartupLocation="CenterOwner"
Topmost="True"
ShowInTaskbar="False">
<Window.Resources>
<Style x:Key="ToolToggleStyle" TargetType="RadioButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<Border x:Name="Bd" Background="#EEEEEE" BorderBrush="#CCCCCC"
BorderThickness="1" CornerRadius="3" Padding="14,5" Cursor="Hand">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="Bd" Property="Background" Value="#0078D7" />
<Setter TargetName="Bd" Property="BorderBrush" Value="#005A9E" />
<Setter Property="Foreground" Value="White" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Bd" Property="Background" Value="#DDDDDD" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True" />
<Condition Property="IsChecked" Value="True" />
</MultiTrigger.Conditions>
<Setter TargetName="Bd" Property="Background" Value="#006CBE" />
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<StackPanel Margin="10">
<!-- 绘制模式 -->
<TextBlock Text="绘制模式" FontWeight="SemiBold" Margin="0,0,0,4" />
<WrapPanel>
<RadioButton x:Name="RbVoid" Content="画气泡" IsChecked="True" Margin="0,0,6,4"
Style="{StaticResource ToolToggleStyle}" />
<RadioButton x:Name="RbBall" Content="画焊球" Margin="0,0,0,4"
Style="{StaticResource ToolToggleStyle}" />
</WrapPanel>
<TextBlock Text="左键点两次画圆(圆心+半径),右键删除" FontSize="10" Foreground="Gray" Margin="0,2,0,0" />
<Separator Margin="0,8" />
<!-- VoidLimit -->
<TextBlock Text="VoidLimit(%)" Margin="0,0,0,4" />
<DockPanel>
<TextBox x:Name="TbVoidLimit" DockPanel.Dock="Right" Width="50" Text="25.0"
VerticalContentAlignment="Center" Margin="6,0,0,0" />
<Slider x:Name="SliderVoidLimit" Minimum="0" Maximum="100" Value="25"
VerticalAlignment="Center" />
</DockPanel>
<Separator Margin="0,8" />
<!-- 结果 -->
<TextBlock x:Name="TbResult" Text="空隙率: --" FontSize="14" FontWeight="SemiBold" Margin="0,0,0,8" />
<!-- 操作 -->
<WrapPanel HorizontalAlignment="Center">
<Button Content="完成" Padding="14,4" Click="Finish_Click" />
</WrapPanel>
</StackPanel>
</Window>
@@ -0,0 +1,74 @@
using System.Windows;
using XP.ImageProcessing.RoiControl.Controls;
namespace XplorePlane.Views.ImageProcessing
{
public partial class BgaMeasurePanel : Window
{
private PolygonRoiCanvas _canvas;
public BgaMeasurePanel()
{
InitializeComponent();
Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
try
{
var mainWin = Owner as MainWindow;
if (mainWin != null)
_canvas = FindChild<PolygonRoiCanvas>(mainWin);
}
catch { }
// 模式切换:通知 canvas 切换气泡/焊球
RbVoid.Checked += (s, ev) =>
{
if (_canvas != null) _canvas.SetBgaDrawBall(false);
};
RbBall.Checked += (s, ev) =>
{
if (_canvas != null) _canvas.SetBgaDrawBall(true);
};
// VoidLimit 同步
SliderVoidLimit.ValueChanged += (s, ev) =>
{
TbVoidLimit.Text = SliderVoidLimit.Value.ToString("F1");
_canvas?.SetBgaVoidLimit(SliderVoidLimit.Value);
};
// 监听测量完成事件更新结果
if (_canvas != null)
{
_canvas.MeasureCompleted += (s, ev) =>
{
if (ev is MeasureCompletedEventArgs args && args.MeasureType == "BgaVoid")
{
TbResult.Text = $"空隙率: {args.Distance:F1}%";
}
};
}
}
private void Finish_Click(object sender, RoutedEventArgs e)
{
Close();
}
private static T FindChild<T>(DependencyObject parent) where T : DependencyObject
{
int count = System.Windows.Media.VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < count; i++)
{
var child = System.Windows.Media.VisualTreeHelper.GetChild(parent, i);
if (child is T t) return t;
var result = FindChild<T>(child);
if (result != null) return result;
}
return null;
}
}
}