4597 lines
212 KiB
C#
4597 lines
212 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.ComponentModel;
|
||
using System.Data;
|
||
using System.Drawing;
|
||
using System.Drawing.Drawing2D;
|
||
using System.Drawing.Imaging;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Runtime.InteropServices;
|
||
using System.Text;
|
||
using System.Windows.Forms;
|
||
|
||
namespace JoyD.Windows.CS
|
||
{
|
||
public partial class Setting : Form
|
||
{
|
||
// 创建并显示检测配置窗口
|
||
private static Setting _formInstance;
|
||
public static Setting Form
|
||
{
|
||
get
|
||
{
|
||
if (_formInstance == null || _formInstance.IsDisposed)
|
||
{
|
||
_formInstance = new Setting();
|
||
}
|
||
return _formInstance;
|
||
}
|
||
}
|
||
|
||
// 定时器字段
|
||
private Timer _timer;
|
||
|
||
// 绘制模式标志
|
||
private bool _isDrawingMode = false;
|
||
|
||
// 温差图绘制模式标志
|
||
private bool _isTempDiffDrawingMode = false;
|
||
|
||
// 矩形绘制相关变量
|
||
private Point _startPoint;
|
||
private Rectangle _currentRectangle = Rectangle.Empty;
|
||
private bool _isDrawing = false;
|
||
private List<RegionInfo> _drawnRectangles = new List<RegionInfo>();
|
||
private Color _selectedColor = Color.Red;
|
||
private int _regionCounter = 0;
|
||
// 叠加层图像 - 用于存储已完成绘制的矩形
|
||
private Image _rectangleOverlayImage = null;
|
||
// 温差层图像 - 用于存储温差图绘制内容
|
||
private Image _tempDiffOverlayImage = null;
|
||
private int _hoveredRegionIndex = -1; // 当前悬停的区域索引(-1表示没有悬停在任何区域上)
|
||
// 当前选中的区域索引
|
||
private int _selectedRegionIndex = -1;
|
||
|
||
// 句柄调整相关变量
|
||
private enum ResizeHandle { None, TopLeft, Top, TopRight, Right, BottomRight, Bottom, BottomLeft, Left }
|
||
private ResizeHandle _currentHandle = ResizeHandle.None;
|
||
private bool _isResizing = false;
|
||
private Point _resizeStartPoint;
|
||
private Rectangle _originalRectangle;
|
||
|
||
// 区域移动相关变量
|
||
private bool _isMoving = false;
|
||
private Point _startMovePoint;
|
||
|
||
// 画笔大小相关变量
|
||
private int _currentBrushSize = 1; // 默认画笔大小为1像素
|
||
// 温差图绘制相关变量
|
||
private Point _lastDrawPoint = Point.Empty; // 上一个绘制点的位置
|
||
private bool _isDrawingRectangle = false; // 是否正在绘制矩形
|
||
private Point _rectangleStartPoint = Point.Empty; // 矩形起始点
|
||
private bool _isEraseMode = false; // 擦除模式标志
|
||
private Rectangle _tempDiffTempRectangle = Rectangle.Empty; // 临时温差图矩形预览
|
||
|
||
public Setting()
|
||
{
|
||
InitializeComponent();
|
||
|
||
// 设置按钮图标
|
||
SetButtonIcon();
|
||
|
||
// 初始化定时器
|
||
_timer = new Timer { Interval = 1000 };
|
||
_timer.Tick += Timer_Tick;
|
||
|
||
// 添加窗体大小变化事件处理
|
||
this.Resize += new EventHandler(Setting_Resize);
|
||
|
||
// 初始调整在Setting_Shown中进行,此处不需要
|
||
|
||
// 初始隐藏颜色选择按钮
|
||
btnSelectColor.Visible = false;
|
||
|
||
// 初始隐藏删除按钮
|
||
try
|
||
{
|
||
btnDeleteRegion.Visible = false;
|
||
// 初始状态/就绪状态下显示温差图按钮
|
||
btnDrawTempDiff.Visible = true;
|
||
// 初始状态下隐藏添加和删除温差图例按钮
|
||
btnAddTempDiff.Visible = false;
|
||
btnDeleteTempDiff.Visible = false;
|
||
// 初始化擦除按钮并设置为隐藏
|
||
if (btnEraseTempDiff != null)
|
||
{
|
||
btnEraseTempDiff.Visible = false;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("删除按钮初始化失败: " + ex.Message);
|
||
}
|
||
|
||
// 初始化温差图例DataGridView
|
||
InitializeTempDiffDataGridView();
|
||
|
||
// 初始状态下隐藏温差图例表格
|
||
dataGridViewTempDiff.Visible = false;
|
||
|
||
// 在初始化完成后调用UpdateButtonsVisibility设置初始状态,确保所有按钮按照状态要求显示/隐藏
|
||
UpdateButtonsVisibility(0);
|
||
|
||
// 调整在Setting_Shown中进行,此处不需要
|
||
}
|
||
|
||
/// <summary>
|
||
/// 1像素画笔大小按钮点击事件
|
||
/// </summary>
|
||
private void BtnBrushSize1_Click(object sender, EventArgs e)
|
||
{
|
||
_currentBrushSize = 1;
|
||
// 更新按钮选中状态
|
||
UpdateBrushSizeButtonSelection(1);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 3像素画笔大小按钮点击事件
|
||
/// </summary>
|
||
private void BtnBrushSize3_Click(object sender, EventArgs e)
|
||
{
|
||
_currentBrushSize = 3;
|
||
// 更新按钮选中状态
|
||
UpdateBrushSizeButtonSelection(3);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 5像素画笔大小按钮点击事件
|
||
/// </summary>
|
||
private void BtnBrushSize5_Click(object sender, EventArgs e)
|
||
{
|
||
_currentBrushSize = 5;
|
||
// 更新按钮选中状态
|
||
UpdateBrushSizeButtonSelection(5);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 10像素画笔大小按钮点击事件
|
||
/// </summary>
|
||
private void BtnBrushSize10_Click(object sender, EventArgs e)
|
||
{
|
||
_currentBrushSize = 10;
|
||
// 更新按钮选中状态
|
||
UpdateBrushSizeButtonSelection(10);
|
||
|
||
// 如果当前是擦除模式,更新光标大小
|
||
if (_isEraseMode)
|
||
{
|
||
try
|
||
{
|
||
// 使用白色作为擦除模式的光标颜色,以便在各种背景下都可见
|
||
Cursor customCursor = CreateCustomBrushCursor(_currentBrushSize, Color.White);
|
||
|
||
// 确保不重复设置相同的光标(避免资源泄漏)
|
||
if (picBoxTemp.Cursor != customCursor)
|
||
{
|
||
// 获取旧光标
|
||
Cursor oldCursor = picBoxTemp.Cursor;
|
||
|
||
// 先设置新光标
|
||
picBoxTemp.Cursor = customCursor;
|
||
|
||
// 只释放自定义光标,不释放系统光标
|
||
if (oldCursor != null && oldCursor != Cursors.Default &&
|
||
oldCursor != Cursors.Cross && oldCursor != Cursors.Hand &&
|
||
oldCursor != Cursors.IBeam && oldCursor != Cursors.WaitCursor)
|
||
{
|
||
oldCursor.Dispose();
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("更新擦除模式光标大小时发生异常: " + ex.Message);
|
||
// 出现异常时设置为十字光标
|
||
try
|
||
{
|
||
if (picBoxTemp.Cursor != Cursors.Cross)
|
||
{
|
||
picBoxTemp.Cursor = Cursors.Cross;
|
||
}
|
||
}
|
||
catch {}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 15像素画笔大小按钮点击事件
|
||
/// </summary>
|
||
private void BtnBrushSize15_Click(object sender, EventArgs e)
|
||
{
|
||
_currentBrushSize = 15;
|
||
// 更新按钮选中状态
|
||
UpdateBrushSizeButtonSelection(15);
|
||
|
||
// 如果当前是擦除模式,更新光标大小
|
||
if (_isEraseMode)
|
||
{
|
||
try
|
||
{
|
||
// 使用白色作为擦除模式的光标颜色,以便在各种背景下都可见
|
||
Cursor customCursor = CreateCustomBrushCursor(_currentBrushSize, Color.White);
|
||
|
||
// 确保不重复设置相同的光标(避免资源泄漏)
|
||
if (picBoxTemp.Cursor != customCursor)
|
||
{
|
||
// 获取旧光标
|
||
Cursor oldCursor = picBoxTemp.Cursor;
|
||
|
||
// 先设置新光标
|
||
picBoxTemp.Cursor = customCursor;
|
||
|
||
// 只释放自定义光标,不释放系统光标
|
||
if (oldCursor != null && oldCursor != Cursors.Default &&
|
||
oldCursor != Cursors.Cross && oldCursor != Cursors.Hand &&
|
||
oldCursor != Cursors.IBeam && oldCursor != Cursors.WaitCursor)
|
||
{
|
||
oldCursor.Dispose();
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("更新擦除模式光标大小时发生异常: " + ex.Message);
|
||
// 出现异常时设置为十字光标
|
||
try
|
||
{
|
||
if (picBoxTemp.Cursor != Cursors.Cross)
|
||
{
|
||
picBoxTemp.Cursor = Cursors.Cross;
|
||
}
|
||
}
|
||
catch {}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 25像素画笔大小按钮点击事件
|
||
/// </summary>
|
||
private void BtnBrushSize25_Click(object sender, EventArgs e)
|
||
{
|
||
_currentBrushSize = 25;
|
||
// 更新按钮选中状态
|
||
UpdateBrushSizeButtonSelection(25);
|
||
|
||
// 如果当前是擦除模式,更新光标大小
|
||
if (_isEraseMode)
|
||
{
|
||
try
|
||
{
|
||
// 使用白色作为擦除模式的光标颜色,以便在各种背景下都可见
|
||
Cursor customCursor = CreateCustomBrushCursor(_currentBrushSize, Color.White);
|
||
|
||
// 确保不重复设置相同的光标(避免资源泄漏)
|
||
if (picBoxTemp.Cursor != customCursor)
|
||
{
|
||
// 获取旧光标
|
||
Cursor oldCursor = picBoxTemp.Cursor;
|
||
|
||
// 先设置新光标
|
||
picBoxTemp.Cursor = customCursor;
|
||
|
||
// 只释放自定义光标,不释放系统光标
|
||
if (oldCursor != null && oldCursor != Cursors.Default &&
|
||
oldCursor != Cursors.Cross && oldCursor != Cursors.Hand &&
|
||
oldCursor != Cursors.IBeam && oldCursor != Cursors.WaitCursor)
|
||
{
|
||
oldCursor.Dispose();
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("更新擦除模式光标大小时发生异常: " + ex.Message);
|
||
// 出现异常时设置为十字光标
|
||
try
|
||
{
|
||
if (picBoxTemp.Cursor != Cursors.Cross)
|
||
{
|
||
picBoxTemp.Cursor = Cursors.Cross;
|
||
}
|
||
}
|
||
catch {}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 擦除温差图按钮点击事件
|
||
/// </summary>
|
||
private void BtnEraseTempDiff_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
// 切换擦除模式
|
||
_isEraseMode = !_isEraseMode;
|
||
btnEraseTempDiff.Checked = _isEraseMode;
|
||
|
||
if (_isEraseMode)
|
||
{
|
||
btnEraseTempDiff.ToolTipText = "擦除模式已启用,点击图片区域进行擦除(点击关闭)";
|
||
|
||
// 在擦除模式下,更新光标以匹配当前画笔大小
|
||
try
|
||
{
|
||
// 使用白色作为擦除模式的光标颜色,以便在各种背景下都可见
|
||
Cursor customCursor = CreateCustomBrushCursor(_currentBrushSize, Color.White);
|
||
|
||
// 确保不重复设置相同的光标(避免资源泄漏)
|
||
if (picBoxTemp.Cursor != customCursor)
|
||
{
|
||
// 获取旧光标
|
||
Cursor oldCursor = picBoxTemp.Cursor;
|
||
|
||
// 先设置新光标
|
||
picBoxTemp.Cursor = customCursor;
|
||
|
||
// 只释放自定义光标,不释放系统光标
|
||
if (oldCursor != null && oldCursor != Cursors.Default &&
|
||
oldCursor != Cursors.Cross && oldCursor != Cursors.Hand &&
|
||
oldCursor != Cursors.IBeam && oldCursor != Cursors.WaitCursor)
|
||
{
|
||
oldCursor.Dispose();
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("设置擦除模式光标时发生异常: " + ex.Message);
|
||
// 出现异常时设置为十字光标
|
||
try
|
||
{
|
||
if (picBoxTemp.Cursor != Cursors.Cross)
|
||
{
|
||
picBoxTemp.Cursor = Cursors.Cross;
|
||
}
|
||
}
|
||
catch {}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
btnEraseTempDiff.ToolTipText = "使用透明色擦除温差图";
|
||
// 退出擦除模式时,可以恢复默认光标或其他光标
|
||
try
|
||
{
|
||
if (picBoxTemp.Cursor != Cursors.Default)
|
||
{
|
||
Cursor oldCursor = picBoxTemp.Cursor;
|
||
picBoxTemp.Cursor = Cursors.Default;
|
||
|
||
// 只释放自定义光标,不释放系统光标
|
||
if (oldCursor != null && oldCursor != Cursors.Default &&
|
||
oldCursor != Cursors.Cross && oldCursor != Cursors.Hand &&
|
||
oldCursor != Cursors.IBeam && oldCursor != Cursors.WaitCursor)
|
||
{
|
||
oldCursor.Dispose();
|
||
}
|
||
}
|
||
}
|
||
catch {}
|
||
}
|
||
|
||
// 重置上一个绘制点,确保下一次绘制/擦除是新的起点
|
||
_lastDrawPoint = Point.Empty;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("擦除模式切换失败: " + ex.Message);
|
||
_isEraseMode = false;
|
||
btnEraseTempDiff.Checked = false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建表示画笔大小的图标
|
||
/// </summary>
|
||
/// <param name="size">画笔大小(像素)</param>
|
||
/// <param name="color">画笔颜色</param>
|
||
/// <returns>表示画笔大小的图标</returns>
|
||
private System.Drawing.Bitmap CreateBrushSizeImage(int size, System.Drawing.Color color)
|
||
{
|
||
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(20, 20);
|
||
using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bmp))
|
||
{
|
||
// 填充背景为透明色
|
||
g.Clear(System.Drawing.Color.Magenta);
|
||
// 计算正方形位置,使其居中
|
||
int x = (20 - size) / 2;
|
||
int y = (20 - size) / 2;
|
||
// 绘制正方形表示画笔大小
|
||
g.FillRectangle(new System.Drawing.SolidBrush(color), x, y, size, size);
|
||
// 添加边框
|
||
g.DrawRectangle(new System.Drawing.Pen(System.Drawing.Color.Black), x, y, size - 1, size - 1);
|
||
}
|
||
return bmp;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新画笔大小按钮的选中状态
|
||
/// </summary>
|
||
/// <param name="selectedSize">当前选中的画笔大小</param>
|
||
private void UpdateBrushSizeButtonSelection(int selectedSize)
|
||
{
|
||
try
|
||
{
|
||
// 清除所有按钮的选中状态
|
||
btnBrushSize1.Checked = false;
|
||
btnBrushSize3.Checked = false;
|
||
btnBrushSize5.Checked = false;
|
||
btnBrushSize10.Checked = false;
|
||
btnBrushSize15.Checked = false;
|
||
btnBrushSize25.Checked = false;
|
||
|
||
// 设置当前选中按钮的选中状态
|
||
switch (selectedSize)
|
||
{
|
||
case 1:
|
||
btnBrushSize1.Checked = true;
|
||
break;
|
||
case 3:
|
||
btnBrushSize3.Checked = true;
|
||
break;
|
||
case 5:
|
||
btnBrushSize5.Checked = true;
|
||
break;
|
||
case 10:
|
||
btnBrushSize10.Checked = true;
|
||
break;
|
||
case 15:
|
||
btnBrushSize15.Checked = true;
|
||
break;
|
||
case 25:
|
||
btnBrushSize25.Checked = true;
|
||
break;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("更新画笔大小按钮状态失败: " + ex.Message);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 鼠标按下事件 - 处理右击退出绘制状态、左击开始绘制矩形和开始调整区域大小
|
||
/// </summary>
|
||
// 存储温差值和对应颜色的数据结构
|
||
private List<Dictionary<string, object>> tempDiffData = new List<Dictionary<string, object>>();
|
||
|
||
private void InitializeTempDiffDataGridView()
|
||
{
|
||
// 设置整个DataGridView为可编辑,以便特定列可以进入编辑状态
|
||
dataGridViewTempDiff.ReadOnly = false;
|
||
// 禁止用户添加新行
|
||
dataGridViewTempDiff.AllowUserToAddRows = false;
|
||
// 禁止用户调整行高
|
||
dataGridViewTempDiff.AllowUserToResizeRows = false;
|
||
|
||
// 添加列:温差值
|
||
DataGridViewTextBoxColumn columnTempDiff = new DataGridViewTextBoxColumn
|
||
{
|
||
Name = "tempDiffValue",
|
||
HeaderText = "温差值",
|
||
Width = 80,
|
||
ReadOnly = false, // 设置为可编辑
|
||
DefaultCellStyle = new DataGridViewCellStyle { Alignment = DataGridViewContentAlignment.MiddleCenter }
|
||
};
|
||
|
||
// 添加列:颜色
|
||
DataGridViewTextBoxColumn columnColor = new DataGridViewTextBoxColumn
|
||
{
|
||
Name = "colorColumn",
|
||
HeaderText = "颜色",
|
||
Width = 80,
|
||
ReadOnly = true,
|
||
CellTemplate = new DataGridViewTextBoxCell()
|
||
};
|
||
|
||
// 添加列到DataGridView
|
||
dataGridViewTempDiff.Columns.Clear();
|
||
dataGridViewTempDiff.Columns.Add(columnTempDiff);
|
||
dataGridViewTempDiff.Columns.Add(columnColor);
|
||
|
||
// 设置编辑模式为编程控制,这样只有通过代码(如双击事件)才能进入编辑状态
|
||
dataGridViewTempDiff.EditMode = DataGridViewEditMode.EditProgrammatically;
|
||
|
||
// 添加单元格绘制事件用于显示颜色块
|
||
dataGridViewTempDiff.CellPainting += new DataGridViewCellPaintingEventHandler(DataGridViewTempDiff_CellPainting);
|
||
// 添加点击事件用于选择颜色和处理温差值列
|
||
dataGridViewTempDiff.CellClick += new DataGridViewCellEventHandler(DataGridViewTempDiff_CellClick);
|
||
// 添加单元格验证事件用于验证温差值输入
|
||
dataGridViewTempDiff.CellValidating += new DataGridViewCellValidatingEventHandler(DataGridViewTempDiff_CellValidating);
|
||
// 添加单元格格式化事件用于显示摄氏度符号
|
||
dataGridViewTempDiff.CellFormatting += new DataGridViewCellFormattingEventHandler(DataGridViewTempDiff_CellFormatting);
|
||
// 添加编辑控件显示事件用于设置编辑控件属性
|
||
dataGridViewTempDiff.EditingControlShowing += new DataGridViewEditingControlShowingEventHandler(DataGridViewTempDiff_EditingControlShowing);
|
||
// 添加单元格双击事件用于触发编辑模式
|
||
dataGridViewTempDiff.CellDoubleClick += new DataGridViewCellEventHandler(DataGridViewTempDiff_CellDoubleClick);
|
||
// 添加选择变更事件用于控制画笔大小按钮的显示
|
||
dataGridViewTempDiff.SelectionChanged += new EventHandler(DataGridViewTempDiff_SelectionChanged);
|
||
|
||
// 默认不添加示例数据,保持温差图例为空
|
||
}
|
||
|
||
private void AddSampleTempDiffData()
|
||
{
|
||
// 清除现有数据
|
||
tempDiffData.Clear();
|
||
dataGridViewTempDiff.Rows.Clear();
|
||
|
||
// 添加示例数据并设置默认颜色
|
||
AddTempDiffRow("10°C", Color.Blue);
|
||
AddTempDiffRow("20°C", Color.Green);
|
||
AddTempDiffRow("30°C", Color.Red);
|
||
}
|
||
|
||
private void AddTempDiffRow(string tempDiffValue, Color color)
|
||
{
|
||
// 添加到数据列表
|
||
Dictionary<string, object> rowData = new Dictionary<string, object>
|
||
{
|
||
{ "tempDiffValue", tempDiffValue },
|
||
{ "color", color }
|
||
};
|
||
tempDiffData.Add(rowData);
|
||
|
||
// 添加到DataGridView
|
||
dataGridViewTempDiff.Rows.Add(tempDiffValue, "");
|
||
|
||
// 添加完图例后设置删除按钮可见
|
||
btnDeleteTempDiff.Visible = true;
|
||
}
|
||
|
||
private void DataGridViewTempDiff_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
|
||
{
|
||
// 检查是否是温差值列
|
||
if (dataGridViewTempDiff.CurrentCell.ColumnIndex == dataGridViewTempDiff.Columns["tempDiffValue"].Index)
|
||
{
|
||
// 获取TextBox编辑控件 - 使用模式匹配
|
||
if (e.Control is TextBox tb)
|
||
{
|
||
// 清除之前的处理程序
|
||
tb.KeyPress -= new KeyPressEventHandler(TempDiffValueTextBox_KeyPress);
|
||
// 添加新的KeyPress处理程序
|
||
tb.KeyPress += new KeyPressEventHandler(TempDiffValueTextBox_KeyPress);
|
||
|
||
// 去掉°C符号,只保留数值部分
|
||
string currentValue = dataGridViewTempDiff.CurrentCell.Value.ToString();
|
||
if (currentValue.Contains("°C"))
|
||
{
|
||
tb.Text = currentValue.Replace("°C", "");
|
||
tb.SelectionStart = tb.Text.Length;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private void TempDiffValueTextBox_KeyPress(object sender, KeyPressEventArgs e)
|
||
{
|
||
// 允许输入数字、小数点和退格键
|
||
if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar) && e.KeyChar != '.')
|
||
{
|
||
e.Handled = true;
|
||
}
|
||
|
||
// 只允许一个小数点
|
||
TextBox tb = sender as TextBox;
|
||
if (e.KeyChar == '.' && tb != null && tb.Text.Contains("."))
|
||
{
|
||
e.Handled = true;
|
||
}
|
||
|
||
// 检查小数点后最多一位
|
||
if (tb != null && tb.Text.Contains(".") &&
|
||
tb.Text.Substring(tb.Text.IndexOf(".")).Length > 1 &&
|
||
!char.IsControl(e.KeyChar))
|
||
{
|
||
e.Handled = true;
|
||
}
|
||
}
|
||
|
||
private void DataGridViewTempDiff_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
|
||
{
|
||
// 只验证温差值列
|
||
if (e.ColumnIndex == dataGridViewTempDiff.Columns["tempDiffValue"].Index && e.RowIndex >= 0)
|
||
{
|
||
string value = e.FormattedValue.ToString().Trim();
|
||
|
||
// 去掉单位,只保留数字部分
|
||
string numericValue = System.Text.RegularExpressions.Regex.Replace(value, @"[^0-9.-]", "");
|
||
|
||
// 尝试解析输入为浮点数
|
||
if (!float.TryParse(numericValue, out float tempValue))
|
||
{
|
||
dataGridViewTempDiff.Rows[e.RowIndex].ErrorText = "请输入有效的温度值";
|
||
e.Cancel = true;
|
||
return;
|
||
}
|
||
|
||
// 验证温度范围(0-100度)
|
||
if (tempValue < 0 || tempValue > 100)
|
||
{
|
||
dataGridViewTempDiff.Rows[e.RowIndex].ErrorText = "温度值必须在0到100度之间";
|
||
e.Cancel = true;
|
||
return;
|
||
}
|
||
|
||
// 检查温度值是否已存在(排除当前正在编辑的行)
|
||
string tempString = $"{tempValue}°C";
|
||
if (IsTempValueExists(tempString, e.RowIndex))
|
||
{
|
||
dataGridViewTempDiff.Rows[e.RowIndex].ErrorText = "该温度值已存在";
|
||
e.Cancel = true;
|
||
return;
|
||
}
|
||
|
||
// 清除错误信息
|
||
dataGridViewTempDiff.Rows[e.RowIndex].ErrorText = string.Empty;
|
||
|
||
// 更新数据模型
|
||
if (e.RowIndex < tempDiffData.Count)
|
||
{
|
||
tempDiffData[e.RowIndex]["tempDiffValue"] = tempString;
|
||
}
|
||
}
|
||
}
|
||
|
||
private void DataGridViewTempDiff_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
|
||
{
|
||
// 只处理温差值列
|
||
if (e.ColumnIndex == dataGridViewTempDiff.Columns["tempDiffValue"].Index && e.RowIndex >= 0)
|
||
{
|
||
// 确保值不为空
|
||
if (e.Value != null && !string.IsNullOrEmpty(e.Value.ToString()))
|
||
{
|
||
string value = e.Value.ToString();
|
||
// 确保值包含°C符号
|
||
if (!value.Contains("°C"))
|
||
{
|
||
e.Value = $"{value}°C";
|
||
e.FormattingApplied = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private void DataGridViewTempDiff_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
|
||
{
|
||
// 只处理颜色列
|
||
if (e.ColumnIndex == dataGridViewTempDiff.Columns["colorColumn"].Index && e.RowIndex >= 0)
|
||
{
|
||
// 清空单元格内容
|
||
e.PaintBackground(e.CellBounds, true);
|
||
|
||
// 确保行索引有效
|
||
if (e.RowIndex < tempDiffData.Count)
|
||
{
|
||
// 获取当前行的颜色
|
||
Color color = (Color)tempDiffData[e.RowIndex]["color"];
|
||
|
||
// 创建颜色块的绘制区域(居中显示,留出边距)
|
||
Rectangle colorBlock = new Rectangle(
|
||
e.CellBounds.X + 10,
|
||
e.CellBounds.Y + 3,
|
||
e.CellBounds.Width - 20,
|
||
e.CellBounds.Height - 6
|
||
);
|
||
|
||
// 绘制颜色块
|
||
using (SolidBrush brush = new SolidBrush(color))
|
||
{
|
||
e.Graphics.FillRectangle(brush, colorBlock);
|
||
}
|
||
|
||
// 添加边框
|
||
using (Pen pen = new Pen(Color.Black))
|
||
{
|
||
e.Graphics.DrawRectangle(pen, colorBlock);
|
||
}
|
||
}
|
||
|
||
// 标记单元格已绘制
|
||
e.Handled = true;
|
||
}
|
||
}
|
||
|
||
private void DataGridViewTempDiff_CellClick(object sender, DataGridViewCellEventArgs e)
|
||
{
|
||
// 移除颜色列点击处理逻辑,颜色列修改改为双击触发
|
||
// 温差值列点击不再直接进入编辑模式,改为双击进入
|
||
}
|
||
|
||
/// <summary>
|
||
/// 温差图例选择变更事件处理
|
||
/// </summary>
|
||
private void DataGridViewTempDiff_SelectionChanged(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
// 获取工具栏分隔条引用
|
||
ToolStripSeparator firstSeparator = null; // 删除温差图例按钮和画笔大小按钮之间的分隔条
|
||
ToolStripSeparator secondSeparator = null; // 画笔大小按钮和txtRegionNumber之间的分隔条
|
||
ToolStripSeparator thirdSeparator = null; // txtRegionNumber后面的分隔条
|
||
|
||
// 查找分隔条
|
||
for (int i = 0; i < toolStrip.Items.Count - 1; i++)
|
||
{
|
||
// 查找第一个分隔条(在删除温差图例按钮和画笔大小按钮之间)
|
||
if (toolStrip.Items[i] == btnDeleteTempDiff && toolStrip.Items[i + 1] is ToolStripSeparator)
|
||
{
|
||
firstSeparator = toolStrip.Items[i + 1] as ToolStripSeparator;
|
||
}
|
||
// 查找第二个分隔条(在画笔大小按钮和txtRegionNumber之间)
|
||
else if (toolStrip.Items[i] == btnBrushSize25 && toolStrip.Items[i + 1] is ToolStripSeparator)
|
||
{
|
||
secondSeparator = toolStrip.Items[i + 1] as ToolStripSeparator;
|
||
}
|
||
// 查找第三个分隔条(在txtRegionNumber后面)
|
||
else if (toolStrip.Items[i] == txtRegionNumber && toolStrip.Items[i + 1] is ToolStripSeparator)
|
||
{
|
||
thirdSeparator = toolStrip.Items[i + 1] as ToolStripSeparator;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
// 检查当前是否在温差图绘制状态下
|
||
// 同时检查选中行和选中单元格,确保点击单元格时也能显示画笔按钮
|
||
if (_isTempDiffDrawingMode && (dataGridViewTempDiff.SelectedRows.Count > 0 || dataGridViewTempDiff.SelectedCells.Count > 0) && dataGridViewTempDiff.Visible)
|
||
{
|
||
// 显示所有画笔大小按钮
|
||
btnBrushSize1.Visible = true;
|
||
btnBrushSize3.Visible = true;
|
||
btnBrushSize5.Visible = true;
|
||
btnBrushSize10.Visible = true;
|
||
btnBrushSize15.Visible = true;
|
||
btnBrushSize25.Visible = true;
|
||
|
||
// 获取选中行的索引
|
||
int selectedRowIndex = -1;
|
||
if (dataGridViewTempDiff.SelectedRows.Count > 0)
|
||
{
|
||
selectedRowIndex = dataGridViewTempDiff.SelectedRows[0].Index;
|
||
}
|
||
else if (dataGridViewTempDiff.SelectedCells.Count > 0)
|
||
{
|
||
selectedRowIndex = dataGridViewTempDiff.SelectedCells[0].RowIndex;
|
||
}
|
||
|
||
// 如果选中行索引有效,更新画笔按钮颜色
|
||
if (selectedRowIndex >= 0 && selectedRowIndex < tempDiffData.Count)
|
||
{
|
||
Color selectedColor = (Color)tempDiffData[selectedRowIndex]["color"];
|
||
|
||
// 更新所有画笔按钮的颜色
|
||
btnBrushSize1.Image = CreateBrushSizeImage(1, selectedColor);
|
||
btnBrushSize3.Image = CreateBrushSizeImage(3, selectedColor);
|
||
btnBrushSize5.Image = CreateBrushSizeImage(5, selectedColor);
|
||
btnBrushSize10.Image = CreateBrushSizeImage(10, selectedColor);
|
||
btnBrushSize15.Image = CreateBrushSizeImage(15, selectedColor);
|
||
btnBrushSize25.Image = CreateBrushSizeImage(25, selectedColor);
|
||
}
|
||
|
||
// 确保当前选中的画笔大小按钮处于选中状态
|
||
UpdateBrushSizeButtonSelection(_currentBrushSize);
|
||
|
||
// 控制分隔条可见性:分隔条只有在前后都有可见按钮时才显示
|
||
// 与UpdateButtonsVisibility方法保持一致的逻辑
|
||
bool hasVisibleBrushButtons = btnBrushSize1.Visible || btnBrushSize3.Visible || btnBrushSize5.Visible ||
|
||
btnBrushSize10.Visible || btnBrushSize15.Visible || btnBrushSize25.Visible;
|
||
bool hasVisibleNewButtons = btnNewTempRegion.Visible || btnLoadTempRegion.Visible || btnSaveTempRegion.Visible ||
|
||
btnNewTempDiff.Visible || btnLoadTempDiff.Visible || btnSaveTempDiff.Visible;
|
||
|
||
if (firstSeparator != null)
|
||
{
|
||
firstSeparator.Visible = btnDeleteTempDiff.Visible && hasVisibleBrushButtons;
|
||
}
|
||
if (secondSeparator != null)
|
||
{
|
||
secondSeparator.Visible = hasVisibleBrushButtons && hasVisibleNewButtons;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 其他所有情况都隐藏画笔大小按钮,确保在就绪状态下正确隐藏
|
||
btnBrushSize1.Visible = false;
|
||
btnBrushSize3.Visible = false;
|
||
btnBrushSize5.Visible = false;
|
||
btnBrushSize10.Visible = false;
|
||
btnBrushSize15.Visible = false;
|
||
btnBrushSize25.Visible = false;
|
||
|
||
// 隐藏分隔条
|
||
if (firstSeparator != null)
|
||
{
|
||
firstSeparator.Visible = false;
|
||
}
|
||
if (secondSeparator != null)
|
||
{
|
||
secondSeparator.Visible = false;
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("更新画笔大小按钮可见性失败: " + ex.Message);
|
||
}
|
||
finally
|
||
{
|
||
// 按钮可见性变化后,重新调整toolStrip尺寸
|
||
AdjustToolStripDimensions();
|
||
}
|
||
}
|
||
|
||
private void DataGridViewTempDiff_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
|
||
{
|
||
if (e.RowIndex >= 0 && e.RowIndex < tempDiffData.Count)
|
||
{
|
||
// 处理温差值列双击 - 双击时启动编辑模式
|
||
if (e.ColumnIndex == dataGridViewTempDiff.Columns["tempDiffValue"].Index)
|
||
{
|
||
dataGridViewTempDiff.BeginEdit(true);
|
||
}
|
||
// 处理颜色列双击 - 双击时打开颜色选择对话框,但仅在非只读模式下
|
||
else if (e.ColumnIndex == dataGridViewTempDiff.Columns["colorColumn"].Index && !dataGridViewTempDiff.ReadOnly)
|
||
{
|
||
using (ColorDialog colorDialog = new ColorDialog())
|
||
{
|
||
// 设置初始颜色为当前行的颜色
|
||
Color oldColor = (Color)tempDiffData[e.RowIndex]["color"];
|
||
colorDialog.Color = oldColor;
|
||
|
||
if (colorDialog.ShowDialog() == DialogResult.OK)
|
||
{
|
||
Color newColor = colorDialog.Color;
|
||
|
||
// 检查颜色是否已存在(排除当前正在编辑的行)
|
||
if (IsColorExists(newColor, e.RowIndex))
|
||
{
|
||
MessageBox.Show("该颜色已存在,请选择其他颜色", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||
return;
|
||
}
|
||
|
||
// 更新颜色数据
|
||
tempDiffData[e.RowIndex]["color"] = newColor;
|
||
// 刷新单元格以显示新颜色
|
||
dataGridViewTempDiff.Refresh();
|
||
|
||
// 检查当前修改的行是否为选中行,如果是则更新画笔按钮颜色
|
||
int selectedRowIndex = -1;
|
||
if (dataGridViewTempDiff.SelectedRows.Count > 0)
|
||
{
|
||
selectedRowIndex = dataGridViewTempDiff.SelectedRows[0].Index;
|
||
}
|
||
else if (dataGridViewTempDiff.SelectedCells.Count > 0)
|
||
{
|
||
selectedRowIndex = dataGridViewTempDiff.SelectedCells[0].RowIndex;
|
||
}
|
||
|
||
// 如果修改的行是当前选中的行,同步更新画笔按钮颜色
|
||
if (selectedRowIndex == e.RowIndex)
|
||
{
|
||
// 更新所有画笔按钮的颜色
|
||
btnBrushSize1.Image = CreateBrushSizeImage(1, newColor);
|
||
btnBrushSize3.Image = CreateBrushSizeImage(3, newColor);
|
||
btnBrushSize5.Image = CreateBrushSizeImage(5, newColor);
|
||
btnBrushSize10.Image = CreateBrushSizeImage(10, newColor);
|
||
btnBrushSize15.Image = CreateBrushSizeImage(15, newColor);
|
||
btnBrushSize25.Image = CreateBrushSizeImage(25, newColor);
|
||
}
|
||
|
||
// 更新温差层图像中所有原颜色的像素为新颜色
|
||
if (_tempDiffOverlayImage != null)
|
||
{
|
||
UpdateTempDiffOverlayPixelsColor(oldColor, newColor);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private void PicBoxTemp_MouseDown(object sender, MouseEventArgs e)
|
||
{
|
||
// 检查是否处于温差图绘制状态且右击鼠标
|
||
if (_isTempDiffDrawingMode && e.Button == MouseButtons.Right)
|
||
{
|
||
// 退出温差图绘制状态,返回到就绪状态
|
||
ExitTempDiffDrawingMode();
|
||
}
|
||
// 温差图绘制/擦除模式下,按住Ctrl键并按下左键开始绘制矩形
|
||
else if ((_isTempDiffDrawingMode || _isEraseMode) && e.Button == MouseButtons.Left && (ModifierKeys & Keys.Control) == Keys.Control)
|
||
{
|
||
// 初始化温差层图像(如果不存在或尺寸不匹配)
|
||
if (_tempDiffOverlayImage == null ||
|
||
_tempDiffOverlayImage.Width != picBoxTemp.Image.Width ||
|
||
_tempDiffOverlayImage.Height != picBoxTemp.Image.Height)
|
||
{
|
||
InitializeTempDiffOverlayImage();
|
||
}
|
||
|
||
// 获取相对于图像的坐标作为矩形起始点
|
||
Point imagePoint = ControlPointToImagePoint(e.Location);
|
||
|
||
// 画笔大小应该基于鼠标光标的实际视觉大小
|
||
// 在控件坐标系中,画笔大小就是用户感知的光标大小
|
||
// 需要将控件坐标系统中的画笔大小转换为图像坐标系统
|
||
// 计算反向缩放比例:将控件坐标转换为图像坐标的比例
|
||
float scaleX = (float)picBoxTemp.Image.Width / picBoxTemp.ClientSize.Width;
|
||
float scaleY = (float)picBoxTemp.Image.Height / picBoxTemp.ClientSize.Height;
|
||
|
||
// 按下鼠标时初始框以鼠标位置为中心,大小为画笔大小(根据图像缩放比例调整)
|
||
// 这样无论图像如何缩放,画笔大小在用户视觉上始终保持与光标大小一致
|
||
float scaledHalfBrushSizeX = _currentBrushSize / 2 * scaleX;
|
||
float scaledHalfBrushSizeY = _currentBrushSize / 2 * scaleY;
|
||
|
||
_rectangleStartPoint = new Point(
|
||
(int)(imagePoint.X - scaledHalfBrushSizeX),
|
||
(int)(imagePoint.Y - scaledHalfBrushSizeY)
|
||
);
|
||
_isDrawingRectangle = true;
|
||
return;
|
||
}
|
||
// 检查是否处于绘制状态且右击鼠标
|
||
else if (_isDrawingMode && e.Button == MouseButtons.Right)
|
||
{
|
||
// 退出绘制状态
|
||
_isDrawingMode = false;
|
||
btnDrawRegion.Checked = false;
|
||
|
||
// 重置鼠标光标
|
||
picBoxTemp.Cursor = Cursors.Default;
|
||
|
||
// 清除临时绘制
|
||
_currentRectangle = Rectangle.Empty;
|
||
_isDrawing = false;
|
||
|
||
// 按就绪状态设置按钮可见性
|
||
UpdateButtonsVisibility(0); // 0表示初始状态/就绪状态
|
||
|
||
// 更新按钮提示文本
|
||
btnDrawRegion.ToolTipText = "绘制区域(点击开启)";
|
||
|
||
// 刷新绘制
|
||
picBoxTemp.Invalidate();
|
||
|
||
|
||
}
|
||
// 处理选中区域的调整大小或移动
|
||
else if (e.Button == MouseButtons.Left && !_isDrawingMode && _selectedRegionIndex != -1)
|
||
{
|
||
RegionInfo selectedRegion = _drawnRectangles.FirstOrDefault(r => r.Index == _selectedRegionIndex);
|
||
if (selectedRegion != null)
|
||
{
|
||
Rectangle controlRectangle = ImageRectangleToControlRectangle(selectedRegion.ImageRectangle);
|
||
|
||
// 检查是否点击在句柄上
|
||
_currentHandle = GetHoveredHandle(controlRectangle, e.Location);
|
||
|
||
if (_currentHandle != ResizeHandle.None)
|
||
{
|
||
// 开始调整大小
|
||
_isResizing = true;
|
||
_resizeStartPoint = e.Location;
|
||
_originalRectangle = controlRectangle;
|
||
}
|
||
else if (controlRectangle.Contains(e.Location))
|
||
{
|
||
// 开始移动区域
|
||
_isMoving = true;
|
||
_startMovePoint = e.Location;
|
||
picBoxTemp.Cursor = Cursors.SizeAll;
|
||
}
|
||
}
|
||
}
|
||
// 处理左击开始绘制矩形
|
||
else if (e.Button == MouseButtons.Left && _isDrawingMode)
|
||
{
|
||
_startPoint = e.Location;
|
||
_isDrawing = true;
|
||
_currentRectangle = new Rectangle(e.X, e.Y, 0, 0);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取根据图像缩放比例调整后的画笔大小
|
||
/// </summary>
|
||
/// <returns>调整后的画笔大小</returns>
|
||
private int GetAdjustedBrushSize()
|
||
{ int adjustedBrushSize = _currentBrushSize;
|
||
if (picBoxTemp.Image != null)
|
||
{ // 计算控件到图像的缩放比例(图像实际大小与控件显示大小的比例)
|
||
float scaleX = (float)picBoxTemp.Image.Width / picBoxTemp.ClientSize.Width;
|
||
float scaleY = (float)picBoxTemp.Image.Height / picBoxTemp.ClientSize.Height;
|
||
|
||
// 使用缩放比例调整画笔大小,使绘制区域在视觉上与光标块一致
|
||
adjustedBrushSize = (int)(_currentBrushSize * Math.Min(scaleX, scaleY));
|
||
|
||
// 确保调整后的画笔大小不会太小或太大
|
||
adjustedBrushSize = Math.Max(adjustedBrushSize, 1); // 最小1像素
|
||
adjustedBrushSize = Math.Min(adjustedBrushSize, 50); // 最大50像素
|
||
}
|
||
return adjustedBrushSize;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 控制分隔条显示状态的通用方法
|
||
/// 从第一个按钮开始遍历,当分隔条前或后没有可见按钮时将其设为隐藏
|
||
/// </summary>
|
||
private void UpdateSeparatorsVisibility()
|
||
{
|
||
try
|
||
{
|
||
// 遍历所有工具条项
|
||
for (int i = 0; i < toolStrip.Items.Count; i++)
|
||
{
|
||
// 找到分隔条
|
||
if (toolStrip.Items[i] is ToolStripSeparator separator)
|
||
{
|
||
bool hasVisibleItemBefore = false;
|
||
bool hasVisibleItemAfter = false;
|
||
|
||
// 向前查找可见的项目
|
||
for (int j = i - 1; j >= 0; j--)
|
||
{
|
||
if ((toolStrip.Items[j] is ToolStripButton btn && btn.Visible) ||
|
||
(toolStrip.Items[j] is ToolStripTextBox tb && tb.Visible) ||
|
||
(toolStrip.Items[j] is ToolStripComboBox cb && cb.Visible))
|
||
{
|
||
hasVisibleItemBefore = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 向后查找可见的项目
|
||
for (int j = i + 1; j < toolStrip.Items.Count; j++)
|
||
{
|
||
if ((toolStrip.Items[j] is ToolStripButton btn && btn.Visible) ||
|
||
(toolStrip.Items[j] is ToolStripTextBox tb && tb.Visible) ||
|
||
(toolStrip.Items[j] is ToolStripComboBox cb && cb.Visible))
|
||
{
|
||
hasVisibleItemAfter = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 当分隔条前或后没有可见按钮时,将其设为隐藏
|
||
// 只有当前后都有可见按钮时,分隔条才显示
|
||
separator.Visible = hasVisibleItemBefore && hasVisibleItemAfter;
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("更新分隔条可见性失败: " + ex.Message);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据状态设置按钮可见性
|
||
/// </summary>
|
||
/// <param name="state">状态类型:0-初始/就绪状态,1-选中区域状态,2-绘制状态,3-温差图绘制状态</param>
|
||
private void UpdateButtonsVisibility(int state)
|
||
{
|
||
try
|
||
{
|
||
|
||
|
||
switch (state)
|
||
{
|
||
case 0: // 初始状态/就绪状态
|
||
btnDrawRegion.Visible = true; // 显示绘制区域按钮
|
||
btnDrawTempDiff.Visible = true; // 显示温差图按钮
|
||
btnSelectColor.Visible = false; // 隐藏颜色选择按钮
|
||
btnDeleteRegion.Visible = false; // 隐藏删除区域按钮
|
||
// 显示六个新按钮
|
||
btnNewTempRegion.Visible = true;
|
||
btnLoadTempRegion.Visible = true;
|
||
btnSaveTempRegion.Visible = true;
|
||
btnNewTempDiff.Visible = true;
|
||
btnLoadTempDiff.Visible = true;
|
||
btnSaveTempDiff.Visible = true;
|
||
dataGridViewTempDiff.Visible = true; // 显示温差图例表格
|
||
dataGridViewTempDiff.ReadOnly = true; // 初始状态下设置为只读
|
||
btnAddTempDiff.Visible = false; // 隐藏添加温差图例按钮
|
||
btnDeleteTempDiff.Visible = false; // 隐藏删除温差图例按钮
|
||
btnEraseTempDiff.Visible = false; // 隐藏擦除按钮
|
||
// 隐藏所有画笔大小按钮
|
||
btnBrushSize1.Visible = false;
|
||
btnBrushSize3.Visible = false;
|
||
btnBrushSize5.Visible = false;
|
||
btnBrushSize10.Visible = false;
|
||
btnBrushSize15.Visible = false;
|
||
btnBrushSize25.Visible = false;
|
||
// 隐藏区域编号设置控件
|
||
txtRegionNumber.Visible = false;
|
||
txtRegionNumber.Text = ""; // 清空文本框
|
||
// 调用通用方法控制分隔条可见性
|
||
UpdateSeparatorsVisibility();
|
||
break;
|
||
|
||
case 2: // 绘制状态
|
||
btnSelectColor.Visible = true; // 显示颜色选择按钮
|
||
btnDrawRegion.Visible = true; // 显示绘制区域按钮
|
||
btnDeleteRegion.Visible = false; // 隐藏删除区域按钮
|
||
btnDrawTempDiff.Visible = false; // 隐藏温差图按钮
|
||
dataGridViewTempDiff.Visible = false; // 隐藏温差图例表格
|
||
dataGridViewTempDiff.ReadOnly = true; // 绘制状态下设置为只读
|
||
btnAddTempDiff.Visible = false; // 隐藏添加温差图例按钮
|
||
btnDeleteTempDiff.Visible = false; // 隐藏删除温差图例按钮
|
||
btnEraseTempDiff.Visible = false; // 隐藏擦除按钮
|
||
// 隐藏六个新按钮
|
||
btnNewTempRegion.Visible = false;
|
||
btnLoadTempRegion.Visible = false;
|
||
btnSaveTempRegion.Visible = false;
|
||
btnNewTempDiff.Visible = false;
|
||
btnLoadTempDiff.Visible = false;
|
||
btnSaveTempDiff.Visible = false;
|
||
// 显示区域编号设置控件
|
||
txtRegionNumber.Visible = true;
|
||
// 在绘制状态下,显示下一个测温区的编号(当前所有区域编号最大值 + 1)
|
||
int nextRegionNumber = _drawnRectangles.Count > 0 ? _drawnRectangles.Max(r => r.Index) + 1 : 1;
|
||
txtRegionNumber.Text = nextRegionNumber.ToString();
|
||
// 同步更新_regionCounter,确保与显示的下一个编号一致
|
||
_regionCounter = nextRegionNumber - 1;
|
||
// 隐藏所有画笔大小按钮
|
||
btnBrushSize1.Visible = false;
|
||
btnBrushSize3.Visible = false;
|
||
btnBrushSize5.Visible = false;
|
||
btnBrushSize10.Visible = false;
|
||
btnBrushSize15.Visible = false;
|
||
btnBrushSize25.Visible = false;
|
||
// 调用通用方法控制分隔条可见性
|
||
UpdateSeparatorsVisibility();
|
||
break;
|
||
case 1: // 选中区域状态
|
||
btnDrawRegion.Visible = false; // 隐藏绘制区域按钮
|
||
btnDrawTempDiff.Visible = false; // 隐藏温差图按钮
|
||
btnSelectColor.Visible = true; // 显示颜色选择按钮
|
||
btnDeleteRegion.Visible = true; // 显示删除区域按钮
|
||
dataGridViewTempDiff.Visible = false; // 隐藏温差图例表格
|
||
btnAddTempDiff.Visible = false; // 隐藏添加温差图例按钮
|
||
btnDeleteTempDiff.Visible = false; // 隐藏删除温差图例按钮
|
||
btnEraseTempDiff.Visible = false; // 隐藏擦除按钮
|
||
// 隐藏六个新按钮
|
||
btnNewTempRegion.Visible = false;
|
||
btnLoadTempRegion.Visible = false;
|
||
btnSaveTempRegion.Visible = false;
|
||
btnNewTempDiff.Visible = false;
|
||
btnLoadTempDiff.Visible = false;
|
||
btnSaveTempDiff.Visible = false;
|
||
// 显示区域编号设置控件
|
||
txtRegionNumber.Visible = true;
|
||
// 更新文本框的值为当前选中区域的编号
|
||
if (_selectedRegionIndex != -1)
|
||
{
|
||
var selectedRegion = _drawnRectangles.FirstOrDefault(r => r.Index == _selectedRegionIndex);
|
||
if (selectedRegion != null)
|
||
{
|
||
txtRegionNumber.Text = selectedRegion.Index.ToString();
|
||
}
|
||
}
|
||
// 隐藏所有画笔大小按钮
|
||
btnBrushSize1.Visible = false;
|
||
btnBrushSize3.Visible = false;
|
||
btnBrushSize5.Visible = false;
|
||
btnBrushSize10.Visible = false;
|
||
btnBrushSize15.Visible = false;
|
||
btnBrushSize25.Visible = false;
|
||
// 调用通用方法控制分隔条可见性
|
||
UpdateSeparatorsVisibility();
|
||
break;
|
||
|
||
case 3: // 温差图绘制状态
|
||
btnDrawTempDiff.Visible = true; // 显示温差图按钮
|
||
btnDrawRegion.Visible = false; // 隐藏绘制区域按钮
|
||
btnSelectColor.Visible = false; // 隐藏颜色选择按钮
|
||
btnDeleteRegion.Visible = false; // 隐藏删除区域按钮
|
||
dataGridViewTempDiff.Visible = true; // 显示温差图例表格
|
||
dataGridViewTempDiff.ReadOnly = false; // 温差图绘制状态下可编辑
|
||
btnAddTempDiff.Visible = true; // 显示添加温差图例按钮
|
||
btnDeleteTempDiff.Visible = tempDiffData.Count > 0; // 当有温差图例时显示删除按钮
|
||
btnEraseTempDiff.Visible = true; // 显示擦除按钮
|
||
// 隐藏六个新按钮
|
||
btnNewTempRegion.Visible = false;
|
||
btnLoadTempRegion.Visible = false;
|
||
btnSaveTempRegion.Visible = false;
|
||
btnNewTempDiff.Visible = false;
|
||
btnLoadTempDiff.Visible = false;
|
||
btnSaveTempDiff.Visible = false;
|
||
// 隐藏区域编号设置控件
|
||
txtRegionNumber.Visible = false;
|
||
// 初始隐藏画笔大小按钮,等待用户选择温差图例后在SelectionChanged事件中显示
|
||
btnBrushSize1.Visible = false;
|
||
btnBrushSize3.Visible = false;
|
||
btnBrushSize5.Visible = false;
|
||
btnBrushSize10.Visible = false;
|
||
btnBrushSize15.Visible = false;
|
||
btnBrushSize25.Visible = false;
|
||
// 调用通用方法控制分隔条可见性
|
||
UpdateSeparatorsVisibility();
|
||
break;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("更新按钮可见性失败: " + ex.Message);
|
||
}
|
||
finally
|
||
{
|
||
// 按钮可见性变化后,重新调整toolStrip尺寸
|
||
AdjustToolStripDimensions();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 绘制温差图按钮点击事件
|
||
/// </summary>
|
||
private void BtnDrawTempDiff_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
// 设置温差图绘制模式标志
|
||
_isTempDiffDrawingMode = !_isTempDiffDrawingMode;
|
||
btnDrawTempDiff.Checked = _isTempDiffDrawingMode;
|
||
|
||
if (_isTempDiffDrawingMode)
|
||
{
|
||
// 进入温差图绘制状态
|
||
// 暂时使用十字光标,实际光标会在MouseMove事件中根据画笔大小设置
|
||
picBoxTemp.Cursor = Cursors.Cross;
|
||
btnDrawTempDiff.ToolTipText = "温差图绘制模式已启用,点击图片区域进行绘制(点击关闭)";
|
||
|
||
// 调用按钮可见性更新方法设置为温差图绘制状态
|
||
UpdateButtonsVisibility(3); // 3表示温差图绘制状态
|
||
|
||
// 确保温差图例有数据且默认选中第一行
|
||
if (dataGridViewTempDiff.Rows.Count > 0)
|
||
{
|
||
// 清除当前选择
|
||
dataGridViewTempDiff.ClearSelection();
|
||
// 选中第一行
|
||
dataGridViewTempDiff.Rows[0].Selected = true;
|
||
// 手动触发SelectionChanged事件处理逻辑
|
||
// 这里不直接调用事件,而是直接更新画笔按钮可见性和颜色
|
||
btnBrushSize1.Visible = true;
|
||
btnBrushSize3.Visible = true;
|
||
btnBrushSize5.Visible = true;
|
||
btnBrushSize10.Visible = true;
|
||
btnBrushSize15.Visible = true;
|
||
|
||
// 更新画笔按钮颜色为第一行温差图例的颜色
|
||
if (tempDiffData.Count > 0)
|
||
{
|
||
Color selectedColor = (Color)tempDiffData[0]["color"];
|
||
|
||
// 更新所有画笔按钮的颜色
|
||
btnBrushSize1.Image = CreateBrushSizeImage(1, selectedColor);
|
||
btnBrushSize3.Image = CreateBrushSizeImage(3, selectedColor);
|
||
btnBrushSize5.Image = CreateBrushSizeImage(5, selectedColor);
|
||
btnBrushSize10.Image = CreateBrushSizeImage(10, selectedColor);
|
||
btnBrushSize15.Image = CreateBrushSizeImage(15, selectedColor);
|
||
}
|
||
|
||
// 确保当前选中的画笔大小按钮处于选中状态
|
||
UpdateBrushSizeButtonSelection(_currentBrushSize);
|
||
|
||
// 控制分隔条可见性,与UpdateButtonsVisibility方法保持一致的逻辑
|
||
ToolStripSeparator firstSeparator = null; // 删除温差图例按钮和画笔大小按钮之间的分隔条
|
||
ToolStripSeparator secondSeparator = null; // 画笔大小按钮和新按钮组之间的分隔条
|
||
|
||
// 查找分隔条
|
||
for (int i = 0; i < toolStrip.Items.Count - 1; i++)
|
||
{
|
||
// 查找第一个分隔条
|
||
if (toolStrip.Items[i] == btnDeleteTempDiff && toolStrip.Items[i + 1] is ToolStripSeparator)
|
||
{
|
||
firstSeparator = toolStrip.Items[i + 1] as ToolStripSeparator;
|
||
}
|
||
// 查找第二个分隔条
|
||
else if (toolStrip.Items[i] == btnBrushSize25 && toolStrip.Items[i + 1] is ToolStripSeparator)
|
||
{
|
||
secondSeparator = toolStrip.Items[i + 1] as ToolStripSeparator;
|
||
}
|
||
}
|
||
|
||
// 更新分隔条可见性
|
||
if (firstSeparator != null)
|
||
{
|
||
bool hasVisibleBrushButtons = btnBrushSize1.Visible || btnBrushSize3.Visible || btnBrushSize5.Visible ||
|
||
btnBrushSize10.Visible || btnBrushSize15.Visible || btnBrushSize25.Visible;
|
||
firstSeparator.Visible = btnDeleteTempDiff.Visible && hasVisibleBrushButtons;
|
||
}
|
||
if (secondSeparator != null)
|
||
{
|
||
bool hasVisibleBrushButtons = btnBrushSize1.Visible || btnBrushSize3.Visible || btnBrushSize5.Visible ||
|
||
btnBrushSize10.Visible || btnBrushSize15.Visible || btnBrushSize25.Visible;
|
||
bool hasVisibleNewButtons = btnNewTempRegion.Visible || btnLoadTempRegion.Visible || btnSaveTempRegion.Visible ||
|
||
btnNewTempDiff.Visible || btnLoadTempDiff.Visible || btnSaveTempDiff.Visible;
|
||
secondSeparator.Visible = hasVisibleBrushButtons && hasVisibleNewButtons;
|
||
}
|
||
|
||
// 显示btnBrushSize25(确保所有画笔大小按钮都可见)
|
||
btnBrushSize25.Visible = true;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 退出温差图绘制状态,返回到就绪状态
|
||
ExitTempDiffDrawingMode();
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("温差图绘制模式切换失败: " + ex.Message);
|
||
ExitTempDiffDrawingMode();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 退出温差图绘制模式
|
||
/// </summary>
|
||
private void ExitTempDiffDrawingMode()
|
||
{
|
||
// 重置上一个绘制点
|
||
_lastDrawPoint = Point.Empty;
|
||
_isTempDiffDrawingMode = false;
|
||
btnDrawTempDiff.Checked = false;
|
||
_isEraseMode = false; // 重置擦除模式
|
||
btnEraseTempDiff.Checked = false;
|
||
|
||
// 重置鼠标光标并安全释放自定义光标资源
|
||
try
|
||
{
|
||
Cursor currentCursor = picBoxTemp.Cursor;
|
||
// 只释放自定义光标,不释放系统光标
|
||
if (currentCursor != null && currentCursor != Cursors.Default &&
|
||
currentCursor != Cursors.Cross && currentCursor != Cursors.Hand &&
|
||
currentCursor != Cursors.IBeam && currentCursor != Cursors.WaitCursor)
|
||
{
|
||
// 先设置新光标,再释放旧光标
|
||
picBoxTemp.Cursor = Cursors.Default;
|
||
currentCursor.Dispose();
|
||
}
|
||
else if (currentCursor != Cursors.Default)
|
||
{
|
||
// 如果是系统光标,直接设置为默认光标
|
||
picBoxTemp.Cursor = Cursors.Default;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("重置光标资源时发生异常: " + ex.Message);
|
||
}
|
||
|
||
// 更新按钮提示文本
|
||
try
|
||
{
|
||
btnDrawTempDiff.ToolTipText = "绘制温差图";
|
||
btnEraseTempDiff.ToolTipText = "使用透明色擦除温差图";
|
||
// 确保光标设置为默认值
|
||
picBoxTemp.Cursor = Cursors.Default;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("更新按钮提示文本时发生异常: " + ex.Message);
|
||
}
|
||
|
||
// 更新按钮提示文本
|
||
btnDrawTempDiff.ToolTipText = "绘制温差图";
|
||
|
||
// 按就绪状态设置按钮可见性
|
||
UpdateButtonsVisibility(0); // 0表示初始状态/就绪状态
|
||
|
||
// 刷新绘制
|
||
picBoxTemp.Invalidate();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 绘制区域按钮点击事件
|
||
/// </summary>
|
||
/// <summary>
|
||
/// 添加温差图例按钮点击事件
|
||
/// </summary>
|
||
private void BtnAddTempDiff_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
// 查找可用的温度值,确保不重复
|
||
int tempValue = 10;
|
||
while (true)
|
||
{
|
||
string tempString = $"{tempValue}°C";
|
||
if (!IsTempValueExists(tempString))
|
||
break;
|
||
tempValue += 10;
|
||
}
|
||
|
||
// 查找可用的颜色,确保不重复
|
||
int colorIndex = 0;
|
||
Color defaultColor;
|
||
do
|
||
{
|
||
defaultColor = GetNextDefaultColor(colorIndex);
|
||
colorIndex++;
|
||
} while (IsColorExists(defaultColor));
|
||
|
||
// 添加新的温差图例
|
||
AddTempDiffRow($"{tempValue}°C", defaultColor);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("添加温差图例失败: " + ex.Message);
|
||
}
|
||
}
|
||
|
||
// 检查温度值是否已存在(忽略单位,只比较数值部分)
|
||
private bool IsTempValueExists(string tempValue, int excludeRowIndex = -1)
|
||
{
|
||
// 提取数值部分
|
||
string numericValue = System.Text.RegularExpressions.Regex.Replace(tempValue, @"[^0-9.-]", "");
|
||
if (!float.TryParse(numericValue, out float currentTemp))
|
||
return false;
|
||
|
||
// 遍历所有数据行进行比较
|
||
for (int i = 0; i < tempDiffData.Count; i++)
|
||
{
|
||
// 跳过被排除的行(用于修改操作)
|
||
if (i == excludeRowIndex) continue;
|
||
|
||
string existingTempValue = tempDiffData[i]["tempDiffValue"].ToString();
|
||
string existingNumericValue = System.Text.RegularExpressions.Regex.Replace(existingTempValue, @"[^0-9.-]", "");
|
||
if (float.TryParse(existingNumericValue, out float existingTemp))
|
||
{
|
||
if (Math.Abs(currentTemp - existingTemp) < 0.001) // 浮点数比较,使用容差
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
// 检查颜色是否已存在
|
||
private bool IsColorExists(Color color, int excludeRowIndex = -1)
|
||
{
|
||
for (int i = 0; i < tempDiffData.Count; i++)
|
||
{
|
||
// 跳过被排除的行(用于修改操作)
|
||
if (i == excludeRowIndex) continue;
|
||
|
||
Color existingColor = (Color)tempDiffData[i]["color"];
|
||
if (existingColor.ToArgb() == color.ToArgb())
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
// 获取下一个默认颜色
|
||
private Color GetNextDefaultColor(int index)
|
||
{
|
||
Color[] colors = { Color.Blue, Color.Green, Color.Red, Color.Yellow, Color.Purple, Color.Orange, Color.Pink, Color.Cyan };
|
||
return colors[index % colors.Length];
|
||
}
|
||
|
||
/// <summary>
|
||
/// 删除温差图例按钮点击事件
|
||
/// </summary>
|
||
private void BtnDeleteTempDiff_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
Color colorToRemove = Color.Empty;
|
||
|
||
// 检查是否有选中的行
|
||
if (dataGridViewTempDiff.SelectedRows.Count > 0)
|
||
{
|
||
// 获取选中行的索引
|
||
int selectedRowIndex = dataGridViewTempDiff.SelectedRows[0].Index;
|
||
|
||
// 从数据集合中删除该行数据
|
||
if (selectedRowIndex >= 0 && selectedRowIndex < tempDiffData.Count)
|
||
{
|
||
// 获取要删除的图例对应的颜色
|
||
colorToRemove = (Color)tempDiffData[selectedRowIndex]["color"];
|
||
|
||
tempDiffData.RemoveAt(selectedRowIndex);
|
||
// 从DataGridView中删除该行
|
||
dataGridViewTempDiff.Rows.RemoveAt(selectedRowIndex);
|
||
}
|
||
}
|
||
// 额外检查:如果没有选中的行但有当前单元格(显示小三角的情况)
|
||
else if (dataGridViewTempDiff.CurrentCell != null && dataGridViewTempDiff.CurrentCell.RowIndex >= 0)
|
||
{
|
||
// 获取当前单元格所在行的索引
|
||
int currentRowIndex = dataGridViewTempDiff.CurrentCell.RowIndex;
|
||
|
||
// 从数据集合中删除该行数据
|
||
if (currentRowIndex >= 0 && currentRowIndex < tempDiffData.Count)
|
||
{
|
||
// 获取要删除的图例对应的颜色
|
||
colorToRemove = (Color)tempDiffData[currentRowIndex]["color"];
|
||
|
||
tempDiffData.RemoveAt(currentRowIndex);
|
||
// 从DataGridView中删除该行
|
||
dataGridViewTempDiff.Rows.RemoveAt(currentRowIndex);
|
||
}
|
||
}
|
||
|
||
// 将温差层中对应颜色的像素改为透明色
|
||
if (colorToRemove != Color.Empty && _tempDiffOverlayImage != null)
|
||
{
|
||
UpdateTempDiffOverlayPixelsColor(colorToRemove, Color.Transparent);
|
||
picBoxTemp.Invalidate(); // 刷新显示
|
||
}
|
||
|
||
// 删除图例后更新删除按钮可见性:当没有图例时隐藏按钮
|
||
btnDeleteTempDiff.Visible = tempDiffData.Count > 0;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("删除温差图例失败: " + ex.Message);
|
||
}
|
||
}
|
||
|
||
private void BtnDrawRegion_Click(object sender, EventArgs e)
|
||
{
|
||
_isDrawingMode = btnDrawRegion.Checked;
|
||
|
||
if (_isDrawingMode)
|
||
{
|
||
// 启用绘制模式
|
||
picBoxTemp.Cursor = Cursors.Cross;
|
||
btnDrawRegion.ToolTipText = "绘制模式已启用,点击图片区域绘制矩形框(点击关闭)";
|
||
|
||
// 调用按钮可见性更新方法设置为绘制状态
|
||
UpdateButtonsVisibility(2); // 2表示绘制状态
|
||
}
|
||
else
|
||
{
|
||
// 禁用绘制模式
|
||
picBoxTemp.Cursor = Cursors.Default;
|
||
_currentRectangle = Rectangle.Empty;
|
||
btnDrawRegion.ToolTipText = "绘制温度检测区域(点击开启/关闭)";
|
||
// 重绘以清除临时矩形
|
||
picBoxTemp.Invalidate();
|
||
|
||
// 调用按钮可见性更新方法设置为就绪状态
|
||
UpdateButtonsVisibility(0); // 0表示初始状态/就绪状态
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将控件坐标转换为图像坐标
|
||
/// </summary>
|
||
private Point ControlPointToImagePoint(Point controlPoint)
|
||
{
|
||
if (picBoxTemp.Image == null)
|
||
return controlPoint;
|
||
|
||
// 计算图像在控件中的缩放比例
|
||
float scaleX = (float)picBoxTemp.Image.Width / picBoxTemp.ClientSize.Width;
|
||
float scaleY = (float)picBoxTemp.Image.Height / picBoxTemp.ClientSize.Height;
|
||
|
||
// 计算图像坐标
|
||
return new Point(
|
||
(int)(controlPoint.X * scaleX),
|
||
(int)(controlPoint.Y * scaleY)
|
||
);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将图像坐标转换为控件坐标
|
||
/// </summary>
|
||
private Point ImagePointToControlPoint(Point imagePoint)
|
||
{
|
||
if (picBoxTemp.Image == null)
|
||
return imagePoint;
|
||
|
||
// 计算图像在控件中的缩放比例
|
||
float scaleX = (float)picBoxTemp.ClientSize.Width / picBoxTemp.Image.Width;
|
||
float scaleY = (float)picBoxTemp.ClientSize.Height / picBoxTemp.Image.Height;
|
||
|
||
// 计算控件坐标
|
||
return new Point(
|
||
(int)(imagePoint.X * scaleX),
|
||
(int)(imagePoint.Y * scaleY)
|
||
);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将图像矩形转换为控件矩形
|
||
/// </summary>
|
||
private Rectangle ImageRectangleToControlRectangle(Rectangle imageRectangle)
|
||
{
|
||
if (picBoxTemp.Image == null)
|
||
return imageRectangle;
|
||
|
||
// 计算图像在控件中的缩放比例
|
||
float scaleX = (float)picBoxTemp.ClientSize.Width / picBoxTemp.Image.Width;
|
||
float scaleY = (float)picBoxTemp.ClientSize.Height / picBoxTemp.Image.Height;
|
||
|
||
// 计算控件矩形
|
||
return new Rectangle(
|
||
(int)(imageRectangle.X * scaleX),
|
||
(int)(imageRectangle.Y * scaleY),
|
||
(int)(imageRectangle.Width * scaleX),
|
||
(int)(imageRectangle.Height * scaleY)
|
||
);
|
||
}
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// 创建与画笔大小一致的自定义光标
|
||
/// </summary>
|
||
/// <param name="size">画笔大小</param>
|
||
/// <param name="color">画笔颜色</param>
|
||
/// <returns>自定义光标</returns>
|
||
private Cursor CreateCustomBrushCursor(int size, Color color)
|
||
{
|
||
try
|
||
{
|
||
// 对于最小的画笔(1像素),使用更明显的笔形光标
|
||
if (size <= 1)
|
||
{
|
||
// 使用系统笔形光标,更加明显易见
|
||
return Cursors.Cross; // 或者可以尝试使用Cursors.IBeam或其他合适的光标
|
||
}
|
||
|
||
// 直接使用原始size绘制正方形光标,不考虑缩放比例
|
||
int cursorSize = Math.Max(size, 16); // 最小16x16
|
||
Bitmap cursorBitmap = new Bitmap(cursorSize, cursorSize);
|
||
|
||
using (Graphics g = Graphics.FromImage(cursorBitmap))
|
||
{
|
||
// 清除背景为透明色
|
||
g.Clear(Color.Magenta); // Magenta作为透明色
|
||
|
||
// 计算正方形位置,使其居中
|
||
int x = (cursorSize - size) / 2;
|
||
int y = (cursorSize - size) / 2;
|
||
|
||
// 绘制正方形表示画笔大小
|
||
using (SolidBrush brush = new SolidBrush(color))
|
||
{
|
||
g.FillRectangle(brush, x, y, size, size);
|
||
}
|
||
|
||
// 添加边框以提高可见性
|
||
using (Pen pen = new Pen(Color.Black))
|
||
{
|
||
g.DrawRectangle(pen, x, y, size - 1, size - 1);
|
||
}
|
||
}
|
||
|
||
// 设置透明色
|
||
cursorBitmap.MakeTransparent(Color.Magenta);
|
||
|
||
// 创建光标,热点位于正方形中心
|
||
return new Cursor(cursorBitmap.GetHicon());
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("创建自定义光标失败: " + ex.Message);
|
||
// 失败时返回默认光标
|
||
return Cursors.Cross;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 鼠标移动事件 - 更新矩形大小、移动区域、检测鼠标悬停区域或更新光标
|
||
/// </summary>
|
||
private void PicBoxTemp_MouseMove(object sender, MouseEventArgs e)
|
||
{
|
||
// 温差图绘制模式下,使用自定义画笔光标
|
||
if (_isTempDiffDrawingMode)
|
||
{
|
||
// 检查是否有选中的温差图例
|
||
int selectedRowIndex = -1;
|
||
bool hasSelectedLegend = false;
|
||
|
||
// 尝试获取选中行索引
|
||
if (dataGridViewTempDiff.SelectedRows.Count > 0)
|
||
{
|
||
selectedRowIndex = dataGridViewTempDiff.SelectedRows[0].Index;
|
||
}
|
||
else if (dataGridViewTempDiff.SelectedCells.Count > 0)
|
||
{
|
||
selectedRowIndex = dataGridViewTempDiff.SelectedCells[0].RowIndex;
|
||
}
|
||
|
||
// 检查选中行索引是否有效
|
||
hasSelectedLegend = selectedRowIndex >= 0 && selectedRowIndex < tempDiffData.Count;
|
||
|
||
// 只有在有选中的温差图例时才设置自定义光标
|
||
if (hasSelectedLegend)
|
||
{
|
||
// 获取当前选中的温差图例颜色
|
||
Color selectedColor = (Color)tempDiffData[selectedRowIndex]["color"];
|
||
|
||
try
|
||
{
|
||
// 创建并设置自定义光标
|
||
Cursor customCursor = CreateCustomBrushCursor(_currentBrushSize, selectedColor);
|
||
|
||
// 确保不重复设置相同的光标(避免资源泄漏)
|
||
if (picBoxTemp.Cursor != customCursor)
|
||
{
|
||
// 获取旧光标
|
||
Cursor oldCursor = picBoxTemp.Cursor;
|
||
|
||
// 先设置新光标
|
||
picBoxTemp.Cursor = customCursor;
|
||
|
||
// 只释放自定义光标,不释放系统光标
|
||
if (oldCursor != null && oldCursor != Cursors.Default &&
|
||
oldCursor != Cursors.Cross && oldCursor != Cursors.Hand &&
|
||
oldCursor != Cursors.IBeam && oldCursor != Cursors.WaitCursor)
|
||
{
|
||
oldCursor.Dispose();
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("设置自定义光标时发生异常: " + ex.Message);
|
||
// 出现异常时设置为十字光标
|
||
try
|
||
{
|
||
if (picBoxTemp.Cursor != Cursors.Cross)
|
||
{
|
||
picBoxTemp.Cursor = Cursors.Cross;
|
||
}
|
||
}
|
||
catch {}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 没有选中的温差图例时,使用默认光标
|
||
try
|
||
{
|
||
if (picBoxTemp.Cursor != Cursors.Default)
|
||
{
|
||
// 释放旧光标
|
||
Cursor oldCursor = picBoxTemp.Cursor;
|
||
picBoxTemp.Cursor = Cursors.Default;
|
||
|
||
// 只释放自定义光标,不释放系统光标
|
||
if (oldCursor != null && oldCursor != Cursors.Default &&
|
||
oldCursor != Cursors.Cross && oldCursor != Cursors.Hand &&
|
||
oldCursor != Cursors.IBeam && oldCursor != Cursors.WaitCursor)
|
||
{
|
||
oldCursor.Dispose();
|
||
}
|
||
}
|
||
}
|
||
catch {}
|
||
}
|
||
|
||
// 处理矩形绘制/擦除(按住Ctrl键)- 只有在有选中的温差图例时才进行绘制
|
||
if (_isDrawingRectangle && e.Button == MouseButtons.Left && picBoxTemp.Image != null && hasSelectedLegend)
|
||
{
|
||
// 获取相对于图像的当前坐标
|
||
Point currentImagePoint = ControlPointToImagePoint(e.Location);
|
||
|
||
// 计算矩形参数:左上点是起笔方块的左上点,右下点是当前鼠标位置+画笔一半的宽高(基于光标大小)
|
||
// 使用反向缩放比例:将控件坐标中的光标大小转换为图像坐标
|
||
float scaleX = (float)picBoxTemp.Image.Width / picBoxTemp.ClientSize.Width;
|
||
float scaleY = (float)picBoxTemp.Image.Height / picBoxTemp.ClientSize.Height;
|
||
float scaledHalfBrushSizeX = _currentBrushSize / 2 * scaleX;
|
||
float scaledHalfBrushSizeY = _currentBrushSize / 2 * scaleY;
|
||
|
||
// 计算光标块的左上点和右下点
|
||
Point cursorTopLeft = new Point(
|
||
(int)(currentImagePoint.X - scaledHalfBrushSizeX),
|
||
(int)(currentImagePoint.Y - scaledHalfBrushSizeY)
|
||
);
|
||
Point cursorBottomRight = new Point(
|
||
(int)(currentImagePoint.X + scaledHalfBrushSizeX),
|
||
(int)(currentImagePoint.Y + scaledHalfBrushSizeY)
|
||
);
|
||
|
||
// 起笔方块的右下点
|
||
Point startBottomRight = new Point(
|
||
_rectangleStartPoint.X + (int)(_currentBrushSize * scaleX),
|
||
_rectangleStartPoint.Y + (int)(_currentBrushSize * scaleY)
|
||
);
|
||
|
||
// 确保矩形区有左上和右下两个点
|
||
// 矩形区左上点=min(矩形区左上点,光标块的左上点)
|
||
Point rectTopLeft = new Point(
|
||
Math.Min(_rectangleStartPoint.X, cursorTopLeft.X),
|
||
Math.Min(_rectangleStartPoint.Y, cursorTopLeft.Y)
|
||
);
|
||
|
||
// 矩形区右下点=max(矩形区右下点,光标块的右下点)
|
||
Point rectBottomRight = new Point(
|
||
Math.Max(startBottomRight.X, cursorBottomRight.X),
|
||
Math.Max(startBottomRight.Y, cursorBottomRight.Y)
|
||
);
|
||
|
||
// 计算矩形坐标和大小
|
||
int x = rectTopLeft.X;
|
||
int y = rectTopLeft.Y;
|
||
int width = rectBottomRight.X - rectTopLeft.X;
|
||
int height = rectBottomRight.Y - rectTopLeft.Y;
|
||
|
||
// 存储临时矩形,用于在Paint事件中绘制预览
|
||
_tempDiffTempRectangle = new Rectangle(x, y, width, height);
|
||
|
||
// 触发重绘,让Paint事件绘制临时矩形
|
||
picBoxTemp.Invalidate();
|
||
}
|
||
// 普通绘制/擦除操作(未按住Ctrl键)
|
||
else if (e.Button == MouseButtons.Left && !_isDrawingRectangle && picBoxTemp.Image != null)
|
||
{
|
||
// 初始化温差层图像(如果不存在或尺寸不匹配)
|
||
if (_tempDiffOverlayImage == null ||
|
||
_tempDiffOverlayImage.Width != picBoxTemp.Image.Width ||
|
||
_tempDiffOverlayImage.Height != picBoxTemp.Image.Height)
|
||
{
|
||
InitializeTempDiffOverlayImage();
|
||
}
|
||
|
||
// 获取相对于图像的坐标
|
||
Point imagePoint = ControlPointToImagePoint(e.Location);
|
||
|
||
// 在温差层图像上绘制/擦除
|
||
using (Graphics g = Graphics.FromImage(_tempDiffOverlayImage))
|
||
{
|
||
// 取消抗锯齿,使用最近邻插值,获得边界清晰的图像
|
||
g.SmoothingMode = SmoothingMode.None;
|
||
g.InterpolationMode = InterpolationMode.NearestNeighbor;
|
||
|
||
if (_isEraseMode)
|
||
{
|
||
// 擦除模式:使用透明色填充,设置CompositingMode为清除
|
||
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
|
||
|
||
// 计算考虑图像缩放比例的画笔大小
|
||
// 由于我们需要让实际擦除区域在视觉上与光标块大小一致
|
||
// 这里需要根据图像缩放比例来调整画笔大小
|
||
int adjustedBrushSize = _currentBrushSize;
|
||
if (picBoxTemp.Image != null)
|
||
{
|
||
// 计算控件到图像的缩放比例(图像实际大小与控件显示大小的比例)
|
||
float scaleX = (float)picBoxTemp.Image.Width / picBoxTemp.ClientSize.Width;
|
||
float scaleY = (float)picBoxTemp.Image.Height / picBoxTemp.ClientSize.Height;
|
||
|
||
// 使用缩放比例调整画笔大小,使擦除区域在视觉上与光标块一致
|
||
adjustedBrushSize = (int)(_currentBrushSize * Math.Min(scaleX, scaleY));
|
||
|
||
// 确保调整后的画笔大小不会太小或太大
|
||
adjustedBrushSize = Math.Max(adjustedBrushSize, 1); // 最小1像素
|
||
adjustedBrushSize = Math.Min(adjustedBrushSize, 50); // 最大50像素
|
||
}
|
||
|
||
// 如果是首次擦除或上一个点无效,记录当前点作为起点
|
||
if (_lastDrawPoint == Point.Empty)
|
||
{
|
||
_lastDrawPoint = imagePoint;
|
||
// 绘制起始点的圆形(擦除区域)
|
||
int radius = adjustedBrushSize / 2;
|
||
g.FillEllipse(Brushes.Transparent,
|
||
imagePoint.X - radius,
|
||
imagePoint.Y - radius,
|
||
adjustedBrushSize,
|
||
adjustedBrushSize);
|
||
}
|
||
else
|
||
{
|
||
// 使用透明色绘制粗线条进行擦除,使用调整后的画笔大小
|
||
using (Pen pen = new Pen(Color.Transparent, adjustedBrushSize))
|
||
{
|
||
pen.StartCap = LineCap.Round;
|
||
pen.EndCap = LineCap.Round;
|
||
pen.LineJoin = LineJoin.Round;
|
||
g.DrawLine(pen, _lastDrawPoint, imagePoint);
|
||
}
|
||
// 更新上一个点
|
||
_lastDrawPoint = imagePoint;
|
||
}
|
||
|
||
// 恢复默认的合成模式
|
||
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
|
||
}
|
||
else
|
||
{
|
||
// 普通绘制模式
|
||
// 重新获取选中的温差图例颜色(因为之前的selectedColor变量超出了作用域)
|
||
Color selectedColor = Color.White; // 默认颜色
|
||
if (hasSelectedLegend && selectedRowIndex >= 0 && selectedRowIndex < tempDiffData.Count)
|
||
{
|
||
selectedColor = (Color)tempDiffData[selectedRowIndex]["color"];
|
||
}
|
||
|
||
// 计算考虑图像缩放比例的画笔大小
|
||
int adjustedBrushSize = _currentBrushSize;
|
||
if (picBoxTemp.Image != null)
|
||
{
|
||
// 计算控件到图像的缩放比例(图像实际大小与控件显示大小的比例)
|
||
float scaleX = (float)picBoxTemp.Image.Width / picBoxTemp.ClientSize.Width;
|
||
float scaleY = (float)picBoxTemp.Image.Height / picBoxTemp.ClientSize.Height;
|
||
|
||
// 使用缩放比例调整画笔大小,使绘制区域在视觉上与光标块一致
|
||
adjustedBrushSize = (int)(_currentBrushSize * Math.Min(scaleX, scaleY));
|
||
|
||
// 确保调整后的画笔大小不会太小或太大
|
||
adjustedBrushSize = Math.Max(adjustedBrushSize, 1); // 最小1像素
|
||
adjustedBrushSize = Math.Min(adjustedBrushSize, 50); // 最大50像素
|
||
}
|
||
|
||
// 确保1像素画笔在任何缩放比例下都至少保持1像素宽
|
||
// 对于大于1像素的画笔,继续使用缩放调整
|
||
int finalBrushSize = _currentBrushSize == 1 ? 1 : adjustedBrushSize;
|
||
|
||
using (Pen pen = new Pen(selectedColor, finalBrushSize))
|
||
{
|
||
pen.StartCap = LineCap.Round;
|
||
pen.EndCap = LineCap.Round;
|
||
pen.LineJoin = LineJoin.Round;
|
||
|
||
// 如果是首次绘制或上一个点无效,记录当前点作为起点
|
||
if (_lastDrawPoint == Point.Empty)
|
||
{
|
||
_lastDrawPoint = imagePoint;
|
||
// 绘制起始点的圆形
|
||
int radius = finalBrushSize / 2;
|
||
g.FillEllipse(new SolidBrush(selectedColor),
|
||
imagePoint.X - radius,
|
||
imagePoint.Y - radius,
|
||
finalBrushSize,
|
||
finalBrushSize);
|
||
}
|
||
else
|
||
{
|
||
// 计算两点之间的距离
|
||
int deltaX = Math.Abs(imagePoint.X - _lastDrawPoint.X);
|
||
int deltaY = Math.Abs(imagePoint.Y - _lastDrawPoint.Y);
|
||
int distance = (int)Math.Sqrt(deltaX * deltaX + deltaY * deltaY);
|
||
|
||
// 如果距离较大,添加中间点以确保线条连续
|
||
// 对于1像素画笔,我们需要更密集的点来确保线条流畅
|
||
if (distance > finalBrushSize * 2 || (_currentBrushSize == 1 && distance > 2))
|
||
{
|
||
// 使用 Bresenham 算法的简化版本来绘制连续的线条
|
||
int steps = Math.Max(deltaX, deltaY);
|
||
float xIncrement = (float)(imagePoint.X - _lastDrawPoint.X) / steps;
|
||
float yIncrement = (float)(imagePoint.Y - _lastDrawPoint.Y) / steps;
|
||
|
||
Point currentPoint = _lastDrawPoint;
|
||
|
||
for (int i = 1; i <= steps; i++)
|
||
{
|
||
int nextX = (int)(_lastDrawPoint.X + xIncrement * i);
|
||
int nextY = (int)(_lastDrawPoint.Y + yIncrement * i);
|
||
Point nextPoint = new Point(nextX, nextY);
|
||
|
||
g.DrawLine(pen, currentPoint, nextPoint);
|
||
currentPoint = nextPoint;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 距离较小时,直接绘制连线
|
||
g.DrawLine(pen, _lastDrawPoint, imagePoint);
|
||
}
|
||
|
||
// 更新上一个点
|
||
_lastDrawPoint = imagePoint;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 触发重绘
|
||
picBoxTemp.Invalidate();
|
||
}
|
||
|
||
return; // 温差图绘制模式下,不执行其他鼠标移动逻辑
|
||
}
|
||
|
||
// 处理调整大小
|
||
if (_isResizing && !_isDrawingMode && _selectedRegionIndex != -1)
|
||
{
|
||
RegionInfo selectedRegion = _drawnRectangles.FirstOrDefault(r => r.Index == _selectedRegionIndex);
|
||
if (selectedRegion != null)
|
||
{
|
||
// 计算新的矩形大小
|
||
Rectangle newRect = CalculateNewRectangle(_originalRectangle, _resizeStartPoint, e.Location, _currentHandle);
|
||
|
||
// 确保矩形有最小尺寸
|
||
if (newRect.Width > 10 && newRect.Height > 10)
|
||
{
|
||
// 转换为图像坐标并更新区域
|
||
Point imageTopLeft = ControlPointToImagePoint(new Point(newRect.Left, newRect.Top));
|
||
Point imageBottomRight = ControlPointToImagePoint(new Point(newRect.Right, newRect.Bottom));
|
||
|
||
selectedRegion.ImageRectangle = new Rectangle(
|
||
imageTopLeft.X,
|
||
imageTopLeft.Y,
|
||
imageBottomRight.X - imageTopLeft.X,
|
||
imageBottomRight.Y - imageTopLeft.Y
|
||
);
|
||
|
||
// 重新创建叠加层以反映变化
|
||
CreateRectangleOverlayImage();
|
||
|
||
// 触发重绘
|
||
picBoxTemp.Invalidate();
|
||
}
|
||
}
|
||
}
|
||
// 处理移动区域
|
||
else if (_isMoving && !_isDrawingMode && _selectedRegionIndex != -1)
|
||
{
|
||
RegionInfo selectedRegion = _drawnRectangles.FirstOrDefault(r => r.Index == _selectedRegionIndex);
|
||
if (selectedRegion != null)
|
||
{
|
||
// 计算移动距离
|
||
int deltaX = e.Location.X - _startMovePoint.X;
|
||
int deltaY = e.Location.Y - _startMovePoint.Y;
|
||
|
||
// 转换为图像坐标的移动距离
|
||
float scaleX = (float)picBoxTemp.Image.Width / picBoxTemp.ClientSize.Width;
|
||
float scaleY = (float)picBoxTemp.Image.Height / picBoxTemp.ClientSize.Height;
|
||
int imageDeltaX = (int)(deltaX * scaleX);
|
||
int imageDeltaY = (int)(deltaY * scaleY);
|
||
|
||
// 计算新的矩形位置
|
||
Rectangle newRect = new Rectangle(
|
||
selectedRegion.ImageRectangle.X + imageDeltaX,
|
||
selectedRegion.ImageRectangle.Y + imageDeltaY,
|
||
selectedRegion.ImageRectangle.Width,
|
||
selectedRegion.ImageRectangle.Height
|
||
);
|
||
|
||
// 确保矩形不会移出图像边界
|
||
if (newRect.Left >= 0 && newRect.Top >= 0 &&
|
||
newRect.Right <= picBoxTemp.Image.Width &&
|
||
newRect.Bottom <= picBoxTemp.Image.Height)
|
||
{
|
||
selectedRegion.ImageRectangle = newRect;
|
||
|
||
// 更新起始点,为下一次移动做准备
|
||
_startMovePoint = e.Location;
|
||
|
||
// 重新创建叠加层以反映变化
|
||
CreateRectangleOverlayImage();
|
||
|
||
// 触发重绘
|
||
picBoxTemp.Invalidate();
|
||
}
|
||
}
|
||
}
|
||
// 处理绘制新矩形
|
||
else if (_isDrawing && _isDrawingMode)
|
||
{
|
||
int width = e.X - _startPoint.X;
|
||
int height = e.Y - _startPoint.Y;
|
||
|
||
_currentRectangle = new Rectangle(
|
||
width > 0 ? _startPoint.X : _startPoint.X + width,
|
||
height > 0 ? _startPoint.Y : _startPoint.Y + height,
|
||
Math.Abs(width),
|
||
Math.Abs(height));
|
||
|
||
picBoxTemp.Invalidate();
|
||
}
|
||
// 处理鼠标悬停(更新光标或检测悬停区域)
|
||
else if (!_isDrawingMode && !_isDrawing && !_isResizing) // 就绪状态
|
||
{
|
||
// 如果有选中的区域,检查是否悬停在句柄上
|
||
if (_selectedRegionIndex != -1)
|
||
{
|
||
RegionInfo selectedRegion = _drawnRectangles.FirstOrDefault(r => r.Index == _selectedRegionIndex);
|
||
if (selectedRegion != null)
|
||
{
|
||
Rectangle controlRectangle = ImageRectangleToControlRectangle(selectedRegion.ImageRectangle);
|
||
ResizeHandle hoveredHandle = GetHoveredHandle(controlRectangle, e.Location);
|
||
|
||
if (hoveredHandle != ResizeHandle.None)
|
||
{
|
||
// 设置相应的光标
|
||
SetCursorByHandle(hoveredHandle);
|
||
return; // 不需要检查悬停区域
|
||
}
|
||
else
|
||
{
|
||
// 重置光标
|
||
picBoxTemp.Cursor = Cursors.Default;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 如果没有悬停在句柄上,检查是否悬停在区域上
|
||
if (_currentHandle == ResizeHandle.None && !_isMoving)
|
||
{
|
||
// 将鼠标坐标转换为图像坐标
|
||
Point imagePoint = ControlPointToImagePoint(e.Location);
|
||
|
||
// 检查鼠标是否在某个区域内,选择索引号最大的区域
|
||
int newHoveredRegionIndex = -1;
|
||
int maxIndex = -1;
|
||
foreach (RegionInfo region in _drawnRectangles)
|
||
{
|
||
if (region.ImageRectangle.Contains(imagePoint) && region.Index > maxIndex)
|
||
{
|
||
maxIndex = region.Index;
|
||
newHoveredRegionIndex = region.Index;
|
||
}
|
||
}
|
||
|
||
// 如果悬停的区域发生变化,更新并触发重绘
|
||
if (newHoveredRegionIndex != _hoveredRegionIndex)
|
||
{
|
||
_hoveredRegionIndex = newHoveredRegionIndex;
|
||
picBoxTemp.Invalidate();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 存储区域信息的类,包含矩形框(图像坐标)、颜色和序号
|
||
/// </summary>
|
||
private class RegionInfo
|
||
{
|
||
// 存储相对于图像的矩形坐标
|
||
public Rectangle ImageRectangle { get; set; }
|
||
public Color Color { get; set; }
|
||
public int Index { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建或更新叠加层图像(完全重绘)
|
||
/// 仅在必要时调用,如图像尺寸改变或需要完全重绘
|
||
/// 使用图像坐标绘制矩形
|
||
/// </summary>
|
||
private void CreateRectangleOverlayImage()
|
||
{
|
||
// 如果PictureBox没有图像或者尺寸为0,则不创建叠加层
|
||
if (picBoxTemp.Image == null || picBoxTemp.Image.Width == 0 || picBoxTemp.Image.Height == 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
// 如果叠加层图像不存在或尺寸不匹配,创建新的
|
||
if (_rectangleOverlayImage == null ||
|
||
_rectangleOverlayImage.Width != picBoxTemp.Image.Width ||
|
||
_rectangleOverlayImage.Height != picBoxTemp.Image.Height)
|
||
{
|
||
// 释放旧的叠加层图像资源
|
||
if (_rectangleOverlayImage != null)
|
||
{
|
||
_rectangleOverlayImage.Dispose();
|
||
}
|
||
|
||
// 创建新的叠加层图像
|
||
_rectangleOverlayImage = new Bitmap(picBoxTemp.Image.Width, picBoxTemp.Image.Height);
|
||
|
||
// 清除背景为透明
|
||
using (Graphics g = Graphics.FromImage(_rectangleOverlayImage))
|
||
{
|
||
g.Clear(Color.Transparent);
|
||
}
|
||
|
||
// 由于创建了新图像,需要重新绘制所有矩形
|
||
foreach (RegionInfo region in _drawnRectangles)
|
||
{
|
||
DrawRegionToOverlay(region);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 清除现有叠加层图像
|
||
using (Graphics g = Graphics.FromImage(_rectangleOverlayImage))
|
||
{
|
||
g.Clear(Color.Transparent);
|
||
}
|
||
|
||
// 重绘所有矩形
|
||
foreach (RegionInfo region in _drawnRectangles)
|
||
{
|
||
DrawRegionToOverlay(region);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化温差层图像
|
||
/// 创建与原图大小相同的透明图像用于温差绘制
|
||
/// </summary>
|
||
private void InitializeTempDiffOverlayImage()
|
||
{
|
||
if (picBoxTemp.Image == null || picBoxTemp.Image.Width == 0 || picBoxTemp.Image.Height == 0)
|
||
return;
|
||
|
||
// 释放旧的温差层图像资源
|
||
if (_tempDiffOverlayImage != null)
|
||
{
|
||
_tempDiffOverlayImage.Dispose();
|
||
_tempDiffOverlayImage = null;
|
||
}
|
||
|
||
// 创建新的温差层图像
|
||
_tempDiffOverlayImage = new Bitmap(picBoxTemp.Image.Width, picBoxTemp.Image.Height);
|
||
|
||
// 清除背景为透明
|
||
using (Graphics g = Graphics.FromImage(_tempDiffOverlayImage))
|
||
{
|
||
g.Clear(Color.Transparent);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将单个区域绘制到叠加层图像
|
||
/// 用于增量绘制,避免每次都重绘所有矩形
|
||
/// 使用图像坐标绘制矩形
|
||
/// </summary>
|
||
/// <param name="region">要绘制的区域信息</param>
|
||
private void DrawRegionToOverlay(RegionInfo region)
|
||
{
|
||
if (_rectangleOverlayImage == null)
|
||
return;
|
||
|
||
using (Graphics g = Graphics.FromImage(_rectangleOverlayImage))
|
||
{
|
||
// 设置高质量绘图
|
||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||
|
||
// 使用每个区域自己的颜色和固定线条粗细绘制矩形
|
||
g.DrawRectangle(new Pen(region.Color, 2), region.ImageRectangle);
|
||
|
||
// 绘制区域序号
|
||
using (Font font = new Font("Arial", 12, FontStyle.Bold))
|
||
using (SolidBrush brush = new SolidBrush(region.Color))
|
||
{
|
||
// 在矩形左上角绘制序号
|
||
Point textPosition = new Point(region.ImageRectangle.X + 5, region.ImageRectangle.Y - 15);
|
||
// 确保文本不超出图像边界
|
||
if (textPosition.Y < 0)
|
||
textPosition.Y = 5;
|
||
|
||
g.DrawString(region.Index.ToString(), font, brush, textPosition);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 鼠标释放事件 - 完成矩形绘制、调整大小或移动
|
||
/// 使用增量绘制,避免每次都重绘所有矩形
|
||
/// 确保矩形坐标相对于图像而非控件
|
||
/// </summary>
|
||
private void PicBoxTemp_MouseUp(object sender, MouseEventArgs e)
|
||
{
|
||
// 温差图绘制模式下的矩形绘制结束
|
||
if (_isTempDiffDrawingMode || _isEraseMode)
|
||
{
|
||
_lastDrawPoint = Point.Empty;
|
||
|
||
// 结束矩形绘制/擦除(Ctrl+鼠标绘制模式)
|
||
if (_isDrawingRectangle && picBoxTemp.Image != null)
|
||
{
|
||
// 使用临时矩形进行绘制或擦除操作
|
||
if (!_tempDiffTempRectangle.IsEmpty && _tempDiffTempRectangle.Width > 0 && _tempDiffTempRectangle.Height > 0)
|
||
{
|
||
using (Graphics g = Graphics.FromImage(_tempDiffOverlayImage))
|
||
{
|
||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||
|
||
if (_isEraseMode)
|
||
{
|
||
// 擦除模式:使用透明色填充,设置CompositingMode为清除
|
||
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
|
||
|
||
// 计算考虑图像缩放比例的画笔大小
|
||
// 由于我们需要让实际擦除区域在视觉上与光标块大小一致
|
||
int adjustedBrushSize = _currentBrushSize;
|
||
if (picBoxTemp.Image != null)
|
||
{
|
||
// 计算控件到图像的缩放比例(图像实际大小与控件显示大小的比例)
|
||
float scaleX = (float)picBoxTemp.Image.Width / picBoxTemp.ClientSize.Width;
|
||
float scaleY = (float)picBoxTemp.Image.Height / picBoxTemp.ClientSize.Height;
|
||
|
||
// 使用缩放比例调整画笔大小,使擦除区域在视觉上与光标块一致
|
||
adjustedBrushSize = (int)(_currentBrushSize * Math.Min(scaleX, scaleY));
|
||
|
||
// 确保调整后的画笔大小不会太小或太大
|
||
adjustedBrushSize = Math.Max(adjustedBrushSize, 1); // 最小1像素
|
||
adjustedBrushSize = Math.Min(adjustedBrushSize, 50); // 最大50像素
|
||
}
|
||
|
||
// 使用调整后的画笔大小填充矩形
|
||
// 为了保持与光标一致的视觉效果,我们需要使用画笔大小来调整擦除行为
|
||
// 这里我们直接填充整个矩形,但使用与画笔大小相匹配的方式
|
||
g.FillRectangle(Brushes.Transparent, _tempDiffTempRectangle);
|
||
|
||
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
|
||
}
|
||
else
|
||
{
|
||
// 获取当前选中的温差图例颜色
|
||
Color selectedColor = Color.Black; // 默认颜色
|
||
int selectedRowIndex = -1;
|
||
|
||
// 尝试获取选中行的颜色
|
||
if (dataGridViewTempDiff.SelectedRows.Count > 0)
|
||
{
|
||
selectedRowIndex = dataGridViewTempDiff.SelectedRows[0].Index;
|
||
}
|
||
else if (dataGridViewTempDiff.SelectedCells.Count > 0)
|
||
{
|
||
selectedRowIndex = dataGridViewTempDiff.SelectedCells[0].RowIndex;
|
||
}
|
||
|
||
// 如果选中行索引有效,获取对应的颜色
|
||
if (selectedRowIndex >= 0 && selectedRowIndex < tempDiffData.Count)
|
||
{
|
||
selectedColor = (Color)tempDiffData[selectedRowIndex]["color"];
|
||
}
|
||
|
||
// 创建不透明的填充颜色
|
||
Color fillColor = selectedColor; // 直接使用选中的颜色,保持不透明
|
||
|
||
// 绘制填充矩形
|
||
g.FillRectangle(new SolidBrush(fillColor), _tempDiffTempRectangle);
|
||
|
||
// 绘制矩形边框,线径固定为1以更精确控制绘制逻辑
|
||
using (Pen pen = new Pen(selectedColor, 1))
|
||
{
|
||
g.DrawRectangle(pen, _tempDiffTempRectangle);
|
||
}
|
||
}
|
||
}
|
||
// 触发重绘
|
||
picBoxTemp.Invalidate();
|
||
}
|
||
|
||
// 清空临时矩形
|
||
_tempDiffTempRectangle = Rectangle.Empty;
|
||
_isDrawingRectangle = false;
|
||
_rectangleStartPoint = Point.Empty;
|
||
}
|
||
}
|
||
|
||
// 结束调整大小
|
||
if (_isResizing && e.Button == MouseButtons.Left)
|
||
{
|
||
_isResizing = false;
|
||
_currentHandle = ResizeHandle.None;
|
||
picBoxTemp.Cursor = Cursors.Default;
|
||
return;
|
||
}
|
||
|
||
// 结束移动区域
|
||
if (_isMoving && e.Button == MouseButtons.Left)
|
||
{
|
||
_isMoving = false;
|
||
picBoxTemp.Cursor = Cursors.Default;
|
||
return;
|
||
}
|
||
|
||
// 结束绘制新矩形
|
||
if (_isDrawing && _isDrawingMode && e.Button == MouseButtons.Left && picBoxTemp.Image != null)
|
||
{
|
||
_isDrawing = false;
|
||
|
||
// 获取相对于图像的矩形坐标
|
||
Point imageStartPoint = ControlPointToImagePoint(new Point(_currentRectangle.X, _currentRectangle.Y));
|
||
Point imageEndPoint = ControlPointToImagePoint(new Point(_currentRectangle.Right, _currentRectangle.Bottom));
|
||
|
||
// 确保矩形有一定大小才添加(转换为图像坐标后检查)
|
||
int imageWidth = Math.Abs(imageEndPoint.X - imageStartPoint.X);
|
||
int imageHeight = Math.Abs(imageEndPoint.Y - imageStartPoint.Y);
|
||
|
||
if (imageWidth > 5 && imageHeight > 5)
|
||
{
|
||
// 计算图像坐标的矩形(确保左上角为起始点)
|
||
int imageX = Math.Min(imageStartPoint.X, imageEndPoint.X);
|
||
int imageY = Math.Min(imageStartPoint.Y, imageEndPoint.Y);
|
||
|
||
// 增加计数器并创建新的区域信息(存储图像坐标)
|
||
// 保持简单的递增方式,因为在UpdateButtonsVisibility方法中已经正确设置了下一个应该使用的编号
|
||
_regionCounter++;
|
||
RegionInfo regionInfo = new RegionInfo
|
||
{
|
||
ImageRectangle = new Rectangle(imageX, imageY, imageWidth, imageHeight),
|
||
Color = _selectedColor,
|
||
Index = _regionCounter
|
||
};
|
||
|
||
_drawnRectangles.Add(regionInfo);
|
||
|
||
// 添加区域后更新区域编号显示(使用绘制状态)
|
||
UpdateButtonsVisibility(2);
|
||
|
||
// 检查是否需要完全重建叠加层
|
||
bool needFullRebuild = false;
|
||
|
||
// 如果叠加层不存在或尺寸不匹配,需要完全重建
|
||
if (_rectangleOverlayImage == null ||
|
||
_rectangleOverlayImage.Width != picBoxTemp.Image.Width ||
|
||
_rectangleOverlayImage.Height != picBoxTemp.Image.Height)
|
||
{
|
||
needFullRebuild = true;
|
||
}
|
||
|
||
if (needFullRebuild)
|
||
{
|
||
// 完全重建叠加层
|
||
CreateRectangleOverlayImage();
|
||
}
|
||
else
|
||
{
|
||
// 仅绘制新添加的矩形(增量绘制)
|
||
DrawRegionToOverlay(regionInfo);
|
||
}
|
||
|
||
// 显示绘制完成的提示
|
||
ToolStripStatusLabel statusLabel = new ToolStripStatusLabel
|
||
{
|
||
Text = string.Format("已添加检测区域{0}: 图像坐标 - X={1}, Y={2}, 宽={3}, 高={4}",
|
||
_regionCounter,
|
||
imageX, imageY, imageWidth, imageHeight)
|
||
};
|
||
// 如果有状态栏,可以添加到状态栏显示
|
||
}
|
||
|
||
_currentRectangle = Rectangle.Empty;
|
||
picBoxTemp.Invalidate();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 绘制事件 - 显示矩形框(实现图像合并机制)
|
||
/// 处理叠加层图像的缩放,确保与控件尺寸匹配
|
||
/// 实现鼠标悬停区域填充半透明色功能
|
||
/// 实现选中区域显示八个句柄功能
|
||
/// </summary>
|
||
private void PicBoxTemp_Paint(object sender, PaintEventArgs e)
|
||
{
|
||
// 设置高质量绘图
|
||
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
||
e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||
|
||
// 图像合并机制:
|
||
// 1. 先绘制温差层(根据控件尺寸进行缩放)
|
||
if (_tempDiffOverlayImage != null && picBoxTemp.Image != null)
|
||
{
|
||
// 计算缩放后的目标矩形
|
||
Rectangle destRect = new Rectangle(0, 0, picBoxTemp.ClientSize.Width, picBoxTemp.ClientSize.Height);
|
||
// 设置绘制质量为无抗锯齿,确保边界清晰
|
||
e.Graphics.SmoothingMode = SmoothingMode.None;
|
||
e.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
|
||
// 绘制缩放后的温差层
|
||
e.Graphics.DrawImage(_tempDiffOverlayImage, destRect, 0, 0, _tempDiffOverlayImage.Width, _tempDiffOverlayImage.Height, GraphicsUnit.Pixel);
|
||
}
|
||
|
||
// 2. 然后绘制叠加层(根据控件尺寸进行缩放)
|
||
if (_rectangleOverlayImage != null && picBoxTemp.Image != null)
|
||
{
|
||
// 计算缩放后的目标矩形
|
||
Rectangle destRect = new Rectangle(0, 0, picBoxTemp.ClientSize.Width, picBoxTemp.ClientSize.Height);
|
||
// 绘制缩放后的叠加层
|
||
e.Graphics.DrawImage(_rectangleOverlayImage, destRect, 0, 0, _rectangleOverlayImage.Width, _rectangleOverlayImage.Height, GraphicsUnit.Pixel);
|
||
}
|
||
|
||
// 绘制选中区域的半透明填充和八个句柄
|
||
if (!_isDrawingMode && _selectedRegionIndex != -1)
|
||
{
|
||
// 查找当前选中的区域
|
||
RegionInfo selectedRegion = _drawnRectangles.FirstOrDefault(r => r.Index == _selectedRegionIndex);
|
||
if (selectedRegion != null)
|
||
{
|
||
// 将图像坐标转换为控件坐标
|
||
Rectangle controlRectangle = ImageRectangleToControlRectangle(selectedRegion.ImageRectangle);
|
||
|
||
// 创建半透明的填充颜色(使用区域的颜色,但设置透明度)
|
||
Color semiTransparentColor = Color.FromArgb(100, selectedRegion.Color);
|
||
|
||
// 填充半透明矩形
|
||
using (SolidBrush brush = new SolidBrush(semiTransparentColor))
|
||
{
|
||
e.Graphics.FillRectangle(brush, controlRectangle);
|
||
}
|
||
|
||
// 绘制八个句柄
|
||
DrawHandles(e.Graphics, controlRectangle);
|
||
}
|
||
}
|
||
// 绘制悬停区域的半透明填充(就绪状态且没有选中区域时)
|
||
else if (!_isDrawingMode && _hoveredRegionIndex != -1 && _selectedRegionIndex == -1)
|
||
{
|
||
// 查找当前悬停的区域
|
||
RegionInfo hoveredRegion = _drawnRectangles.FirstOrDefault(r => r.Index == _hoveredRegionIndex);
|
||
if (hoveredRegion != null)
|
||
{
|
||
// 将图像坐标转换为控件坐标
|
||
Rectangle controlRectangle = ImageRectangleToControlRectangle(hoveredRegion.ImageRectangle);
|
||
|
||
// 创建半透明的填充颜色(使用区域的颜色,但设置透明度)
|
||
Color semiTransparentColor = Color.FromArgb(100, hoveredRegion.Color);
|
||
|
||
// 填充半透明矩形
|
||
using (SolidBrush brush = new SolidBrush(semiTransparentColor))
|
||
{
|
||
e.Graphics.FillRectangle(brush, controlRectangle);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 绘制温差图模式下的临时矩形预览
|
||
if (!_tempDiffTempRectangle.IsEmpty && _isTempDiffDrawingMode && _isDrawingRectangle)
|
||
{
|
||
// 将图像坐标转换为控件坐标
|
||
Rectangle controlRect = ImageRectangleToControlRectangle(_tempDiffTempRectangle);
|
||
|
||
// 获取当前选中的温差图例颜色
|
||
Color selectedColor = Color.Black; // 默认颜色
|
||
int selectedRowIndex = -1;
|
||
|
||
// 尝试获取选中行的颜色
|
||
if (dataGridViewTempDiff.SelectedRows.Count > 0)
|
||
{
|
||
selectedRowIndex = dataGridViewTempDiff.SelectedRows[0].Index;
|
||
}
|
||
else if (dataGridViewTempDiff.SelectedCells.Count > 0)
|
||
{
|
||
selectedRowIndex = dataGridViewTempDiff.SelectedCells[0].RowIndex;
|
||
}
|
||
|
||
// 如果选中行索引有效,获取对应的颜色
|
||
if (selectedRowIndex >= 0 && selectedRowIndex < tempDiffData.Count)
|
||
{
|
||
selectedColor = (Color)tempDiffData[selectedRowIndex]["color"];
|
||
}
|
||
|
||
if (_isEraseMode)
|
||
{
|
||
// 擦除模式使用白色虚线框
|
||
using (Pen dashedPen = new Pen(Color.White, 1))
|
||
{
|
||
dashedPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
|
||
e.Graphics.DrawRectangle(dashedPen, controlRect);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 绘制模式使用半透明填充和边框
|
||
using (Pen dashedPen = new Pen(selectedColor, 1))
|
||
{
|
||
dashedPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
|
||
// 创建半透明的填充颜色
|
||
Color fillColor = Color.FromArgb(64, selectedColor);
|
||
// 绘制半透明填充
|
||
e.Graphics.FillRectangle(new SolidBrush(fillColor), controlRect);
|
||
// 绘制虚线边框
|
||
e.Graphics.DrawRectangle(dashedPen, controlRect);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 再绘制临时矩形(当前正在绘制的矩形,使用控件坐标)
|
||
if (!_currentRectangle.IsEmpty && _isDrawingMode && _isDrawing)
|
||
{
|
||
using (Pen dashedPen = new Pen(Color.Blue, 2))
|
||
{
|
||
dashedPen.DashPattern = new float[] { 5, 2 };
|
||
e.Graphics.DrawRectangle(dashedPen, _currentRectangle);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置绘制区域按钮的图标
|
||
/// </summary>
|
||
private void SetButtonIcon()
|
||
{
|
||
// 创建一个表示绘制区域的图标
|
||
Bitmap icon = new Bitmap(32, 32);
|
||
using (Graphics g = Graphics.FromImage(icon))
|
||
{
|
||
// 设置高质量绘图
|
||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||
|
||
// 清除背景为透明
|
||
g.Clear(Color.Transparent);
|
||
|
||
// 绘制外矩形
|
||
g.DrawRectangle(new Pen(Color.Black, 2.5f), 4, 4, 24, 24);
|
||
|
||
// 绘制虚线内矩形
|
||
using (Pen dashedPen = new Pen(Color.Black, 1.5f))
|
||
{
|
||
dashedPen.DashPattern = new float[] { 2, 1 };
|
||
g.DrawRectangle(dashedPen, 7, 7, 18, 18);
|
||
}
|
||
|
||
// 绘制四个角的控制点
|
||
SolidBrush brush = new SolidBrush(Color.Black);
|
||
g.FillEllipse(brush, 8, 8, 4, 4); // 左上角
|
||
g.FillEllipse(brush, 20, 8, 4, 4); // 右上角
|
||
g.FillEllipse(brush, 8, 20, 4, 4); // 左下角
|
||
g.FillEllipse(brush, 20, 20, 4, 4); // 右下角
|
||
}
|
||
|
||
// 设置按钮图标并设置透明色
|
||
btnDrawRegion.Image = icon;
|
||
btnDrawRegion.ImageTransparentColor = Color.Transparent;
|
||
|
||
// 设置删除按钮的图标
|
||
try
|
||
{
|
||
Bitmap deleteIcon = new Bitmap(32, 32);
|
||
using (Graphics g = Graphics.FromImage(deleteIcon))
|
||
{
|
||
// 设置高质量绘图
|
||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||
|
||
// 清除背景为透明
|
||
g.Clear(Color.Transparent);
|
||
|
||
// 绘制一个红色的叉号作为删除图标
|
||
Pen deletePen = new Pen(Color.Red, 4);
|
||
g.DrawLine(deletePen, 8, 8, 24, 24); // 从左上角到右下角的线
|
||
g.DrawLine(deletePen, 24, 8, 8, 24); // 从右上角到左下角的线
|
||
}
|
||
|
||
btnDeleteRegion.Image = deleteIcon;
|
||
btnDeleteRegion.ImageTransparentColor = Color.Transparent;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("删除按钮图标设置失败: " + ex.Message);
|
||
}
|
||
|
||
// 设置颜色选择按钮的图标
|
||
UpdateColorButtonIcon();
|
||
|
||
// 设置绘制温差图按钮的图标
|
||
try
|
||
{
|
||
Bitmap tempDiffIcon = new Bitmap(32, 32);
|
||
using (Graphics g = Graphics.FromImage(tempDiffIcon))
|
||
{
|
||
// 设置高质量绘图
|
||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||
|
||
// 清除背景为透明
|
||
g.Clear(Color.Transparent);
|
||
|
||
// 绘制表示温差的图标 - 使用渐变效果
|
||
// 绘制底部蓝色(低温)和顶部红色(高温)的矩形条
|
||
using (LinearGradientBrush gradientBrush = new LinearGradientBrush(
|
||
new Rectangle(10, 6, 12, 20),
|
||
Color.Blue, // 低温端
|
||
Color.Red, // 高温端
|
||
LinearGradientMode.Vertical))
|
||
{
|
||
g.FillRectangle(gradientBrush, 10, 6, 12, 20);
|
||
}
|
||
|
||
// 添加边框
|
||
g.DrawRectangle(new Pen(Color.Black, 1.5f), 10, 6, 12, 20);
|
||
}
|
||
|
||
btnDrawTempDiff.Image = tempDiffIcon;
|
||
btnDrawTempDiff.ImageTransparentColor = Color.Transparent;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("温差图按钮图标设置失败: " + ex.Message);
|
||
}
|
||
|
||
// 设置擦除按钮的图标
|
||
try
|
||
{
|
||
Bitmap eraseTempDiffIcon = new Bitmap(32, 32);
|
||
using (Graphics g = Graphics.FromImage(eraseTempDiffIcon))
|
||
{
|
||
// 设置高质量绘图
|
||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||
|
||
// 清除背景为透明
|
||
g.Clear(Color.Transparent);
|
||
|
||
// 绘制现代化的橡皮擦图标
|
||
// 绘制橡皮擦主体(圆角矩形)
|
||
using (Pen pen = new Pen(Color.Black, 2.5f))
|
||
{
|
||
g.DrawRectangle(pen, 8, 10, 16, 14);
|
||
}
|
||
// 填充橡皮擦主体为浅灰色
|
||
using (SolidBrush brush = new SolidBrush(Color.LightGray))
|
||
{
|
||
g.FillRectangle(brush, 10, 11, 12, 9);
|
||
}
|
||
// 绘制橡皮擦手柄
|
||
using (Pen pen = new Pen(Color.Black, 2.5f))
|
||
{
|
||
g.DrawLine(pen, 16, 9, 16, 5);
|
||
g.DrawLine(pen, 12, 5, 20, 5);
|
||
}
|
||
// 添加擦拭痕迹效果
|
||
using (Pen pen = new Pen(Color.Gray, 1.5f))
|
||
{
|
||
pen.DashStyle = DashStyle.Dot;
|
||
g.DrawLine(pen, 5, 16, 27, 16);
|
||
g.DrawLine(pen, 6, 19, 24, 19);
|
||
}
|
||
}
|
||
|
||
btnEraseTempDiff.Image = eraseTempDiffIcon;
|
||
btnEraseTempDiff.ImageTransparentColor = Color.Transparent;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("擦除按钮图标设置失败: " + ex.Message);
|
||
}
|
||
|
||
// 设置新建测温区按钮的图标
|
||
try
|
||
{
|
||
Bitmap newTempRegionIcon = new Bitmap(32, 32);
|
||
using (Graphics g = Graphics.FromImage(newTempRegionIcon))
|
||
{
|
||
// 设置高质量绘图
|
||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||
|
||
// 清除背景为透明
|
||
g.Clear(Color.Transparent);
|
||
|
||
// 绘制现代化的新建测温区图标
|
||
// 绘制绿色矩形
|
||
using (Pen pen = new Pen(Color.LimeGreen, 3))
|
||
{
|
||
g.DrawRectangle(pen, 8, 8, 16, 16);
|
||
}
|
||
// 在矩形右上角绘制加号
|
||
using (Pen pen = new Pen(Color.LimeGreen, 3))
|
||
{
|
||
g.DrawLine(pen, 20, 10, 20, 22); // 垂直线
|
||
g.DrawLine(pen, 17, 16, 23, 16); // 水平线
|
||
}
|
||
// 添加高温指示点
|
||
using (SolidBrush brush = new SolidBrush(Color.Red))
|
||
{
|
||
g.FillEllipse(brush, 21, 11, 3, 3);
|
||
}
|
||
}
|
||
|
||
btnNewTempRegion.Image = newTempRegionIcon;
|
||
btnNewTempRegion.ImageTransparentColor = Color.Transparent;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("新建测温区按钮图标设置失败: " + ex.Message);
|
||
}
|
||
|
||
// 设置加载测温区按钮的图标
|
||
try
|
||
{
|
||
Bitmap loadTempRegionIcon = new Bitmap(32, 32);
|
||
using (Graphics g = Graphics.FromImage(loadTempRegionIcon))
|
||
{
|
||
// 设置高质量绘图
|
||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||
|
||
// 清除背景为透明
|
||
g.Clear(Color.Transparent);
|
||
|
||
// 绘制现代化的加载测温区图标
|
||
// 绘制蓝色文件夹
|
||
using (SolidBrush brush = new SolidBrush(Color.DeepSkyBlue))
|
||
{
|
||
// 文件夹主体
|
||
g.FillRectangle(brush, 8, 14, 16, 12);
|
||
// 文件夹顶部
|
||
g.FillPolygon(brush, new Point[] { new Point(8, 14), new Point(12, 9), new Point(24, 9), new Point(24, 14) });
|
||
}
|
||
// 文件夹轮廓
|
||
using (Pen pen = new Pen(Color.DarkBlue, 2))
|
||
{
|
||
g.DrawLine(pen, 8, 14, 12, 9);
|
||
g.DrawLine(pen, 12, 9, 24, 9);
|
||
g.DrawLine(pen, 24, 9, 24, 26);
|
||
g.DrawLine(pen, 24, 26, 8, 26);
|
||
g.DrawLine(pen, 8, 26, 8, 14);
|
||
}
|
||
// 绘制向下的箭头
|
||
using (Pen pen = new Pen(Color.DarkBlue, 2.5f))
|
||
{
|
||
g.DrawLine(pen, 16, 16, 16, 20);
|
||
g.DrawLine(pen, 13, 18, 16, 21);
|
||
g.DrawLine(pen, 19, 18, 16, 21);
|
||
}
|
||
}
|
||
|
||
btnLoadTempRegion.Image = loadTempRegionIcon;
|
||
btnLoadTempRegion.ImageTransparentColor = Color.Transparent;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("加载测温区按钮图标设置失败: " + ex.Message);
|
||
}
|
||
|
||
// 设置保存测温区按钮的图标
|
||
try
|
||
{
|
||
Bitmap saveTempRegionIcon = new Bitmap(32, 32);
|
||
using (Graphics g = Graphics.FromImage(saveTempRegionIcon))
|
||
{
|
||
// 设置高质量绘图
|
||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||
|
||
// 清除背景为透明
|
||
g.Clear(Color.Transparent);
|
||
|
||
// 绘制现代化的保存测温区图标
|
||
// 绘制棕色文件夹
|
||
using (SolidBrush brush = new SolidBrush(Color.Sienna))
|
||
{
|
||
// 文件夹主体
|
||
g.FillRectangle(brush, 8, 14, 16, 12);
|
||
// 文件夹顶部
|
||
g.FillPolygon(brush, new Point[] { new Point(8, 14), new Point(12, 9), new Point(24, 9), new Point(24, 14) });
|
||
}
|
||
// 文件夹轮廓
|
||
using (Pen pen = new Pen(Color.DarkGoldenrod, 2))
|
||
{
|
||
g.DrawLine(pen, 8, 14, 12, 9);
|
||
g.DrawLine(pen, 12, 9, 24, 9);
|
||
g.DrawLine(pen, 24, 9, 24, 26);
|
||
g.DrawLine(pen, 24, 26, 8, 26);
|
||
g.DrawLine(pen, 8, 26, 8, 14);
|
||
}
|
||
// 绘制向上的箭头
|
||
using (Pen pen = new Pen(Color.DarkGoldenrod, 2.5f))
|
||
{
|
||
g.DrawLine(pen, 16, 13, 16, 17);
|
||
g.DrawLine(pen, 13, 15, 16, 13);
|
||
g.DrawLine(pen, 19, 15, 16, 13);
|
||
}
|
||
}
|
||
|
||
btnSaveTempRegion.Image = saveTempRegionIcon;
|
||
btnSaveTempRegion.ImageTransparentColor = Color.Transparent;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("保存测温区按钮图标设置失败: " + ex.Message);
|
||
}
|
||
|
||
// 设置新建温差图按钮的图标
|
||
try
|
||
{
|
||
Bitmap newTempDiffIcon = new Bitmap(32, 32);
|
||
using (Graphics g = Graphics.FromImage(newTempDiffIcon))
|
||
{
|
||
// 设置高质量绘图
|
||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||
|
||
// 清除背景为透明
|
||
g.Clear(Color.Transparent);
|
||
|
||
// 绘制现代化的新建温差图图标
|
||
// 绘制渐变条(蓝色到红色)
|
||
using (LinearGradientBrush gradientBrush = new LinearGradientBrush(
|
||
new Rectangle(8, 8, 10, 16),
|
||
Color.Blue, // 低温端
|
||
Color.Red, // 高温端
|
||
LinearGradientMode.Vertical))
|
||
{
|
||
g.FillRectangle(gradientBrush, 8, 8, 10, 16);
|
||
}
|
||
// 添加边框
|
||
g.DrawRectangle(new Pen(Color.Black, 2), 8, 8, 10, 16);
|
||
// 绘制明显的加号
|
||
using (Pen pen = new Pen(Color.Green, 3))
|
||
{
|
||
g.DrawLine(pen, 23, 16, 23, 16); // 中心点
|
||
g.DrawLine(pen, 20, 16, 26, 16); // 水平线
|
||
g.DrawLine(pen, 23, 13, 23, 19); // 垂直线
|
||
}
|
||
// 添加小三角形指示新建功能
|
||
using (SolidBrush brush = new SolidBrush(Color.Green))
|
||
{
|
||
g.FillPolygon(brush, new Point[] {
|
||
new Point(26, 13),
|
||
new Point(29, 13),
|
||
new Point(27, 10)
|
||
});
|
||
}
|
||
}
|
||
|
||
btnNewTempDiff.Image = newTempDiffIcon;
|
||
btnNewTempDiff.ImageTransparentColor = Color.Transparent;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("新建温差图按钮图标设置失败: " + ex.Message);
|
||
}
|
||
|
||
// 设置加载温差图按钮的图标
|
||
try
|
||
{
|
||
Bitmap loadTempDiffIcon = new Bitmap(32, 32);
|
||
using (Graphics g = Graphics.FromImage(loadTempDiffIcon))
|
||
{
|
||
// 设置高质量绘图
|
||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||
|
||
// 清除背景为透明
|
||
g.Clear(Color.Transparent);
|
||
|
||
// 绘制现代化的加载温差图图标
|
||
// 绘制渐变条(蓝色到红色)
|
||
using (LinearGradientBrush gradientBrush = new LinearGradientBrush(
|
||
new Rectangle(8, 8, 10, 16),
|
||
Color.Blue, // 低温端
|
||
Color.Red, // 高温端
|
||
LinearGradientMode.Vertical))
|
||
{
|
||
g.FillRectangle(gradientBrush, 8, 8, 10, 16);
|
||
}
|
||
// 添加边框
|
||
g.DrawRectangle(new Pen(Color.Black, 2), 8, 8, 10, 16);
|
||
// 绘制明显的向下箭头
|
||
using (Pen pen = new Pen(Color.Blue, 2.5f))
|
||
{
|
||
g.DrawLine(pen, 23, 13, 23, 19); // 箭头竖线
|
||
g.DrawLine(pen, 20, 17, 23, 19); // 箭头左斜线
|
||
g.DrawLine(pen, 26, 17, 23, 19); // 箭头右斜线
|
||
}
|
||
// 添加波浪线表示数据流
|
||
using (Pen pen = new Pen(Color.Blue, 1.5f))
|
||
{
|
||
pen.DashStyle = DashStyle.Dot;
|
||
g.DrawLine(pen, 16, 23, 23, 23);
|
||
}
|
||
}
|
||
|
||
btnLoadTempDiff.Image = loadTempDiffIcon;
|
||
btnLoadTempDiff.ImageTransparentColor = Color.Transparent;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("加载温差图按钮图标设置失败: " + ex.Message);
|
||
}
|
||
|
||
// 设置保存温差图按钮的图标
|
||
try
|
||
{
|
||
Bitmap saveTempDiffIcon = new Bitmap(32, 32);
|
||
using (Graphics g = Graphics.FromImage(saveTempDiffIcon))
|
||
{
|
||
// 设置高质量绘图
|
||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||
|
||
// 清除背景为透明
|
||
g.Clear(Color.Transparent);
|
||
|
||
// 绘制现代化的保存温差图图标
|
||
// 绘制渐变条(蓝色到红色)
|
||
using (LinearGradientBrush gradientBrush = new LinearGradientBrush(
|
||
new Rectangle(8, 8, 10, 16),
|
||
Color.Blue, // 低温端
|
||
Color.Red, // 高温端
|
||
LinearGradientMode.Vertical))
|
||
{
|
||
g.FillRectangle(gradientBrush, 8, 8, 10, 16);
|
||
}
|
||
// 添加边框
|
||
g.DrawRectangle(new Pen(Color.Black, 2), 8, 8, 10, 16);
|
||
// 绘制明显的向上箭头
|
||
using (Pen pen = new Pen(Color.Red, 2.5f))
|
||
{
|
||
g.DrawLine(pen, 23, 12, 23, 17); // 箭头竖线
|
||
g.DrawLine(pen, 20, 14, 23, 12); // 箭头左斜线
|
||
g.DrawLine(pen, 26, 14, 23, 12); // 箭头右斜线
|
||
}
|
||
// 添加保存符号(小磁盘)
|
||
using (Pen pen = new Pen(Color.Red, 1.5f))
|
||
{
|
||
g.DrawRectangle(pen, 22, 9, 5, 5);
|
||
g.DrawLine(pen, 23, 9, 23, 6);
|
||
g.DrawLine(pen, 26, 9, 26, 6);
|
||
}
|
||
}
|
||
|
||
btnSaveTempDiff.Image = saveTempDiffIcon;
|
||
btnSaveTempDiff.ImageTransparentColor = Color.Transparent;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("保存温差图按钮图标设置失败: " + ex.Message);
|
||
}
|
||
|
||
// 设置添加温差图例按钮的图标
|
||
try
|
||
{
|
||
Bitmap addTempDiffIcon = new Bitmap(32, 32);
|
||
using (Graphics g = Graphics.FromImage(addTempDiffIcon))
|
||
{
|
||
// 设置高质量绘图
|
||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||
|
||
// 清除背景为透明
|
||
g.Clear(Color.Transparent);
|
||
|
||
// 绘制加号图标
|
||
Pen pen = new Pen(Color.Black, 2);
|
||
// 绘制水平线
|
||
g.DrawLine(pen, 8, 12, 16, 12);
|
||
// 绘制垂直线
|
||
g.DrawLine(pen, 12, 8, 12, 16);
|
||
}
|
||
|
||
btnAddTempDiff.Image = addTempDiffIcon;
|
||
btnAddTempDiff.ImageTransparentColor = Color.Transparent;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("添加温差图例按钮图标设置失败: " + ex.Message);
|
||
}
|
||
|
||
// 设置删除温差图例按钮的图标
|
||
try
|
||
{
|
||
Bitmap deleteTempDiffIcon = new Bitmap(24, 24);
|
||
using (Graphics g = Graphics.FromImage(deleteTempDiffIcon))
|
||
{
|
||
// 设置高质量绘图
|
||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||
|
||
// 清除背景为透明
|
||
g.Clear(Color.Transparent);
|
||
|
||
// 绘制减号图标
|
||
Pen pen = new Pen(Color.Black, 2);
|
||
// 绘制水平线
|
||
g.DrawLine(pen, 8, 12, 16, 12);
|
||
}
|
||
|
||
btnDeleteTempDiff.Image = deleteTempDiffIcon;
|
||
btnDeleteTempDiff.ImageTransparentColor = Color.Transparent;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("删除温差图例按钮图标设置失败: " + ex.Message);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新颜色选择按钮的图标,显示_selectedColor颜色的矩形
|
||
/// </summary>
|
||
private void UpdateColorButtonIcon()
|
||
{
|
||
// 创建颜色选择按钮的图标
|
||
Bitmap colorIcon = new Bitmap(24, 24);
|
||
using (Graphics g = Graphics.FromImage(colorIcon))
|
||
{
|
||
// 设置高质量绘图
|
||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||
|
||
// 清除背景为透明
|
||
g.Clear(Color.Transparent);
|
||
|
||
// 绘制边框
|
||
g.DrawRectangle(new Pen(Color.Black, 1), 3, 3, 18, 18);
|
||
|
||
// 使用_selectedColor填充矩形
|
||
using (SolidBrush brush = new SolidBrush(_selectedColor))
|
||
{
|
||
g.FillRectangle(brush, 4, 4, 16, 16);
|
||
}
|
||
}
|
||
|
||
// 设置按钮图标并设置透明色
|
||
btnSelectColor.Image = colorIcon;
|
||
btnSelectColor.ImageTransparentColor = Color.Transparent;
|
||
|
||
// 启用双缓冲以减少闪烁
|
||
typeof(PictureBox).InvokeMember("DoubleBuffered",
|
||
System.Reflection.BindingFlags.SetProperty |
|
||
System.Reflection.BindingFlags.Instance |
|
||
System.Reflection.BindingFlags.NonPublic,
|
||
null, picBoxTemp, new object[] { true });
|
||
}
|
||
|
||
/// <summary>
|
||
/// 颜色选择按钮点击事件
|
||
/// </summary>
|
||
private void BtnSelectColor_Click(object sender, EventArgs e)
|
||
{
|
||
using (ColorDialog colorDialog = new ColorDialog())
|
||
{
|
||
// 设置初始颜色为当前选中的颜色
|
||
colorDialog.Color = _selectedColor;
|
||
|
||
// 允许自定义颜色
|
||
colorDialog.AllowFullOpen = true;
|
||
colorDialog.FullOpen = true;
|
||
|
||
// 显示颜色选择对话框
|
||
if (colorDialog.ShowDialog() == DialogResult.OK)
|
||
{
|
||
// 更新选中的颜色
|
||
_selectedColor = colorDialog.Color;
|
||
|
||
// 更新按钮图标,显示新选择的颜色
|
||
UpdateColorButtonIcon();
|
||
|
||
// 如果有区域被选中,更新该区域的颜色
|
||
if (_selectedRegionIndex != -1)
|
||
{
|
||
RegionInfo selectedRegion = _drawnRectangles.FirstOrDefault(r => r.Index == _selectedRegionIndex);
|
||
if (selectedRegion != null)
|
||
{
|
||
selectedRegion.Color = _selectedColor;
|
||
// 重新创建叠加层图像以反映颜色变化
|
||
CreateRectangleOverlayImage();
|
||
}
|
||
}
|
||
|
||
// 重绘图片区域,显示新颜色的矩形
|
||
picBoxTemp.Invalidate();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 窗口显示时启动定时器
|
||
/// </summary>
|
||
private void Setting_Shown(object sender, EventArgs e)
|
||
{
|
||
// 仅在非设计模式下启动定时器
|
||
if (!DesignMode)
|
||
{
|
||
_timer.Start();
|
||
|
||
// 立即执行一次定时器事件,避免首次显示时的延迟
|
||
Timer_Tick(sender, e);
|
||
|
||
// 在窗口完全显示后重新计算工具栏尺寸,确保所有按钮都能正确显示在多行中
|
||
AdjustToolStripDimensions();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载测温区按钮点击事件
|
||
/// </summary>
|
||
private void BtnLoadTempRegion_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
// 弹出打开文件对话框
|
||
OpenFileDialog openFileDialog = new OpenFileDialog
|
||
{
|
||
Filter = "CSV文件 (*.csv)|*.csv|所有文件 (*.*)|*.*",
|
||
Title = "选择测温区信息文件",
|
||
DefaultExt = "csv"
|
||
};
|
||
|
||
// 显示打开文件对话框,如果用户点击了确定按钮
|
||
if (openFileDialog.ShowDialog() == DialogResult.OK)
|
||
{
|
||
// 移除所有已有的测温区列表
|
||
_drawnRectangles.Clear();
|
||
_selectedRegionIndex = -1;
|
||
// 清空区域编号文本框
|
||
txtRegionNumber.Text = "";
|
||
|
||
// 用透明色清空叠加层图像
|
||
CreateRectangleOverlayImage();
|
||
|
||
// 重置区域计数器
|
||
_regionCounter = 0;
|
||
|
||
// 从CSV文件中读取所有测温区位置大小和颜色信息
|
||
using (StreamReader reader = new StreamReader(openFileDialog.FileName, Encoding.UTF8))
|
||
{
|
||
// 跳过标题行
|
||
string headerLine = reader.ReadLine();
|
||
|
||
string line;
|
||
// 逐行读取数据
|
||
while ((line = reader.ReadLine()) != null)
|
||
{
|
||
try
|
||
{
|
||
// 分割CSV行数据
|
||
string[] parts = line.Split(',');
|
||
|
||
if (parts.Length >= 6)
|
||
{
|
||
// 尝试解析数据(索引可能是从文件中读取的,也可能是我们自己分配的)
|
||
int.TryParse(parts[1], out int x);
|
||
int.TryParse(parts[2], out int y);
|
||
int.TryParse(parts[3], out int width);
|
||
int.TryParse(parts[4], out int height);
|
||
|
||
// 解析颜色
|
||
Color color;
|
||
try
|
||
{
|
||
color = ColorTranslator.FromHtml(parts[5]);
|
||
}
|
||
catch
|
||
{
|
||
// 如果颜色解析失败,使用默认颜色
|
||
color = Color.Red;
|
||
}
|
||
|
||
// 创建新的测温区并添加到列表
|
||
// 从CSV文件第一列获取原始索引值
|
||
int.TryParse(parts[0], out int regionIndex);
|
||
RegionInfo region = new RegionInfo
|
||
{
|
||
Index = regionIndex,
|
||
ImageRectangle = new Rectangle(x, y, width, height),
|
||
Color = color
|
||
};
|
||
|
||
_drawnRectangles.Add(region);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("解析行数据失败: " + ex.Message);
|
||
// 继续处理下一行
|
||
}
|
||
}
|
||
}
|
||
|
||
// 用读取的颜色填充叠加层图像对应的区域
|
||
CreateRectangleOverlayImage();
|
||
|
||
// 触发重绘
|
||
picBoxTemp.Invalidate();
|
||
|
||
// 更新按钮可见性
|
||
UpdateButtonsVisibility(0);
|
||
|
||
// 更新_regionCounter为当前最大索引值+1,避免后续绘制新区域时索引冲突
|
||
if (_drawnRectangles.Count > 0)
|
||
{
|
||
_regionCounter = _drawnRectangles.Max(r => r.Index) + 1;
|
||
}
|
||
|
||
// 加载成功后提示用户
|
||
MessageBox.Show("测温区信息加载成功,共加载 " + _drawnRectangles.Count + " 个测温区", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("加载测温区信息失败: " + ex.Message);
|
||
MessageBox.Show("加载测温区信息失败: " + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存测温区按钮点击事件
|
||
/// </summary>
|
||
/// <summary>
|
||
/// 区域编号文本框按键事件处理
|
||
/// </summary>
|
||
private void TxtRegionNumber_KeyDown(object sender, KeyEventArgs e)
|
||
{
|
||
// 当用户按下Enter键时更新区域编号
|
||
if (e.KeyCode == Keys.Enter)
|
||
{
|
||
UpdateRegionNumber();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 区域编号文本框内容变化事件处理
|
||
/// </summary>
|
||
private void TxtRegionNumber_TextChanged(object sender, EventArgs e)
|
||
{
|
||
// 只允许输入数字
|
||
string text = txtRegionNumber.Text;
|
||
string newText = new string(text.Where(char.IsDigit).ToArray());
|
||
if (text != newText)
|
||
{
|
||
txtRegionNumber.Text = newText;
|
||
txtRegionNumber.SelectionStart = newText.Length;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新选中区域的编号
|
||
/// </summary>
|
||
private void UpdateRegionNumber()
|
||
{
|
||
if (_selectedRegionIndex != -1 && int.TryParse(txtRegionNumber.Text, out int newNumber))
|
||
{
|
||
// 检查新编号是否已经存在
|
||
if (_drawnRectangles.Any(r => r.Index == newNumber && r.Index != _selectedRegionIndex))
|
||
{
|
||
MessageBox.Show("该编号已存在,请选择其他编号!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||
// 恢复原编号
|
||
var selectedRegion = _drawnRectangles.FirstOrDefault(r => r.Index == _selectedRegionIndex);
|
||
if (selectedRegion != null)
|
||
{
|
||
txtRegionNumber.Text = selectedRegion.Index.ToString();
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 更新区域编号
|
||
var region = _drawnRectangles.FirstOrDefault(r => r.Index == _selectedRegionIndex);
|
||
if (region != null)
|
||
{
|
||
region.Index = newNumber;
|
||
_selectedRegionIndex = newNumber; // 更新选中索引
|
||
|
||
// 重新绘制
|
||
CreateRectangleOverlayImage();
|
||
picBoxTemp.Invalidate();
|
||
}
|
||
}
|
||
else if (!string.IsNullOrEmpty(txtRegionNumber.Text))
|
||
{
|
||
// 输入不是有效的数字
|
||
MessageBox.Show("请输入有效的数字编号!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||
// 恢复原编号
|
||
var selectedRegion = _drawnRectangles.FirstOrDefault(r => r.Index == _selectedRegionIndex);
|
||
if (selectedRegion != null)
|
||
{
|
||
txtRegionNumber.Text = selectedRegion.Index.ToString();
|
||
}
|
||
}
|
||
}
|
||
|
||
private void BtnSaveTempRegion_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
// 如果没有测温区,则提示用户
|
||
if (_drawnRectangles.Count == 0)
|
||
{
|
||
MessageBox.Show("没有可保存的测温区", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||
return;
|
||
}
|
||
|
||
// 弹出保存文件对话框
|
||
SaveFileDialog saveFileDialog = new SaveFileDialog
|
||
{
|
||
Filter = "CSV文件 (*.csv)|*.csv|所有文件 (*.*)|*.*",
|
||
Title = "保存测温区信息",
|
||
DefaultExt = "csv",
|
||
FileName = "测温区信息.csv"
|
||
};
|
||
|
||
// 显示保存文件对话框,如果用户点击了确定按钮
|
||
if (saveFileDialog.ShowDialog() == DialogResult.OK)
|
||
{
|
||
// 使用StreamWriter保存为CSV文件
|
||
using (StreamWriter writer = new StreamWriter(saveFileDialog.FileName, false, Encoding.UTF8))
|
||
{
|
||
// 写入CSV文件头部(使用中文标题)
|
||
writer.WriteLine("索引,X坐标,Y坐标,宽度,高度,颜色");
|
||
|
||
// 遍历所有测温区,将信息写入CSV文件
|
||
foreach (RegionInfo region in _drawnRectangles)
|
||
{
|
||
// 获取颜色的十六进制表示
|
||
string colorHex = ColorTranslator.ToHtml(region.Color);
|
||
|
||
// 写入一行数据,格式:索引,X坐标,Y坐标,宽度,高度,颜色
|
||
// 保存时使用最新的编号
|
||
writer.WriteLine($"{region.Index},{region.ImageRectangle.X},{region.ImageRectangle.Y},{region.ImageRectangle.Width},{region.ImageRectangle.Height},{colorHex}");
|
||
}
|
||
}
|
||
|
||
// 保存成功后提示用户
|
||
MessageBox.Show("测温区信息保存成功", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("保存测温区信息失败: " + ex.Message);
|
||
MessageBox.Show("保存测温区信息失败: " + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 新建测温区按钮点击事件
|
||
/// </summary>
|
||
private void BtnNewTempRegion_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
// 移除所有已有的测温区列表
|
||
_drawnRectangles.Clear();
|
||
|
||
// 取消选中状态
|
||
_selectedRegionIndex = -1;
|
||
// 清空区域编号文本框
|
||
txtRegionNumber.Text = "";
|
||
|
||
// 用透明色清空叠加层图像
|
||
CreateRectangleOverlayImage();
|
||
|
||
// 触发重绘
|
||
picBoxTemp.Invalidate();
|
||
|
||
// 更新按钮可见性,设置为就绪状态
|
||
UpdateButtonsVisibility(0);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("新建测温区失败: " + ex.Message);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 删除按钮点击事件
|
||
/// </summary>
|
||
private void BtnDeleteRegion_Click(object sender, EventArgs e)
|
||
{
|
||
// 确保有选中的区域
|
||
if (_selectedRegionIndex != -1)
|
||
{
|
||
// 查找并移除选中的区域
|
||
RegionInfo regionToRemove = _drawnRectangles.FirstOrDefault(r => r.Index == _selectedRegionIndex);
|
||
if (regionToRemove != null)
|
||
{
|
||
_drawnRectangles.Remove(regionToRemove);
|
||
|
||
// 取消选中状态
|
||
_selectedRegionIndex = -1;
|
||
|
||
// 使用统一的方法更新按钮可见性,设置为就绪状态
|
||
UpdateButtonsVisibility(0);
|
||
|
||
// 重绘叠加层
|
||
CreateRectangleOverlayImage();
|
||
|
||
// 触发重绘
|
||
picBoxTemp.Invalidate();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 窗口关闭时停止定时器并释放资源
|
||
/// </summary>
|
||
private void Setting_FormClosing(object sender, FormClosingEventArgs e)
|
||
{
|
||
_timer.Stop();
|
||
|
||
// 释放叠加层图像资源
|
||
if (_rectangleOverlayImage != null)
|
||
{
|
||
_rectangleOverlayImage.Dispose();
|
||
_rectangleOverlayImage = null;
|
||
}
|
||
|
||
// 释放温差层图像资源
|
||
if (_tempDiffOverlayImage != null)
|
||
{
|
||
_tempDiffOverlayImage.Dispose();
|
||
_tempDiffOverlayImage = null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 定时器每秒触发的事件处理方法
|
||
/// </summary>
|
||
/// <summary>
|
||
/// 获取鼠标当前悬停的句柄
|
||
/// </summary>
|
||
/// <param name="rectangle">控件坐标的矩形</param>
|
||
/// <param name="mousePoint">鼠标坐标</param>
|
||
/// <returns>鼠标悬停的句柄类型</returns>
|
||
private ResizeHandle GetHoveredHandle(Rectangle rectangle, Point mousePoint)
|
||
{
|
||
int handleSize = 8; // 句柄检测范围稍大,提高用户体验
|
||
|
||
// 检查各个句柄
|
||
if (IsPointInRegion(mousePoint, new Rectangle(rectangle.Left - handleSize/2, rectangle.Top - handleSize/2, handleSize, handleSize)))
|
||
return ResizeHandle.TopLeft;
|
||
if (IsPointInRegion(mousePoint, new Rectangle(rectangle.Left + rectangle.Width/2 - handleSize/2, rectangle.Top - handleSize/2, handleSize, handleSize)))
|
||
return ResizeHandle.Top;
|
||
if (IsPointInRegion(mousePoint, new Rectangle(rectangle.Right - handleSize/2, rectangle.Top - handleSize/2, handleSize, handleSize)))
|
||
return ResizeHandle.TopRight;
|
||
if (IsPointInRegion(mousePoint, new Rectangle(rectangle.Right - handleSize/2, rectangle.Top + rectangle.Height/2 - handleSize/2, handleSize, handleSize)))
|
||
return ResizeHandle.Right;
|
||
if (IsPointInRegion(mousePoint, new Rectangle(rectangle.Right - handleSize/2, rectangle.Bottom - handleSize/2, handleSize, handleSize)))
|
||
return ResizeHandle.BottomRight;
|
||
if (IsPointInRegion(mousePoint, new Rectangle(rectangle.Left + rectangle.Width/2 - handleSize/2, rectangle.Bottom - handleSize/2, handleSize, handleSize)))
|
||
return ResizeHandle.Bottom;
|
||
if (IsPointInRegion(mousePoint, new Rectangle(rectangle.Left - handleSize/2, rectangle.Bottom - handleSize/2, handleSize, handleSize)))
|
||
return ResizeHandle.BottomLeft;
|
||
if (IsPointInRegion(mousePoint, new Rectangle(rectangle.Left - handleSize/2, rectangle.Top + rectangle.Height/2 - handleSize/2, handleSize, handleSize)))
|
||
return ResizeHandle.Left;
|
||
|
||
return ResizeHandle.None;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查点是否在矩形区域内
|
||
/// </summary>
|
||
private bool IsPointInRegion(Point point, Rectangle region)
|
||
{
|
||
return region.Contains(point);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据句柄类型设置光标
|
||
/// </summary>
|
||
private void SetCursorByHandle(ResizeHandle handle)
|
||
{
|
||
switch (handle)
|
||
{
|
||
case ResizeHandle.TopLeft:
|
||
case ResizeHandle.BottomRight:
|
||
picBoxTemp.Cursor = Cursors.SizeNWSE;
|
||
break;
|
||
case ResizeHandle.TopRight:
|
||
case ResizeHandle.BottomLeft:
|
||
picBoxTemp.Cursor = Cursors.SizeNESW;
|
||
break;
|
||
case ResizeHandle.Top:
|
||
case ResizeHandle.Bottom:
|
||
picBoxTemp.Cursor = Cursors.SizeNS;
|
||
break;
|
||
case ResizeHandle.Left:
|
||
case ResizeHandle.Right:
|
||
picBoxTemp.Cursor = Cursors.SizeWE;
|
||
break;
|
||
default:
|
||
picBoxTemp.Cursor = Cursors.Default;
|
||
break;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据鼠标移动计算新的矩形大小
|
||
/// </summary>
|
||
private Rectangle CalculateNewRectangle(Rectangle original, Point startPoint, Point currentPoint, ResizeHandle handle)
|
||
{
|
||
Rectangle newRect = original;
|
||
|
||
switch (handle)
|
||
{
|
||
case ResizeHandle.TopLeft:
|
||
newRect.X = currentPoint.X;
|
||
newRect.Y = currentPoint.Y;
|
||
newRect.Width = original.Right - currentPoint.X;
|
||
newRect.Height = original.Bottom - currentPoint.Y;
|
||
break;
|
||
case ResizeHandle.Top:
|
||
newRect.Y = currentPoint.Y;
|
||
newRect.Height = original.Bottom - currentPoint.Y;
|
||
break;
|
||
case ResizeHandle.TopRight:
|
||
newRect.Y = currentPoint.Y;
|
||
newRect.Width = currentPoint.X - original.Left;
|
||
newRect.Height = original.Bottom - currentPoint.Y;
|
||
break;
|
||
case ResizeHandle.Right:
|
||
newRect.Width = currentPoint.X - original.Left;
|
||
break;
|
||
case ResizeHandle.BottomRight:
|
||
newRect.Width = currentPoint.X - original.Left;
|
||
newRect.Height = currentPoint.Y - original.Top;
|
||
break;
|
||
case ResizeHandle.Bottom:
|
||
newRect.Height = currentPoint.Y - original.Top;
|
||
break;
|
||
case ResizeHandle.BottomLeft:
|
||
newRect.X = currentPoint.X;
|
||
newRect.Width = original.Right - currentPoint.X;
|
||
newRect.Height = currentPoint.Y - original.Top;
|
||
break;
|
||
case ResizeHandle.Left:
|
||
newRect.X = currentPoint.X;
|
||
newRect.Width = original.Right - currentPoint.X;
|
||
break;
|
||
}
|
||
|
||
// 确保矩形不会变成负尺寸
|
||
if (newRect.Width < 0)
|
||
{
|
||
newRect.X += newRect.Width;
|
||
newRect.Width = Math.Abs(newRect.Width);
|
||
}
|
||
if (newRect.Height < 0)
|
||
{
|
||
newRect.Y += newRect.Height;
|
||
newRect.Height = Math.Abs(newRect.Height);
|
||
}
|
||
|
||
return newRect;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 绘制矩形的八个句柄
|
||
/// </summary>
|
||
/// <param name="g">绘图对象</param>
|
||
/// <param name="rectangle">要绘制句柄的矩形(控件坐标)</param>
|
||
private void DrawHandles(Graphics g, Rectangle rectangle)
|
||
{
|
||
// 句柄大小
|
||
int handleSize = 6;
|
||
|
||
// 句柄颜色
|
||
Color handleColor = Color.White;
|
||
Color handleBorderColor = Color.Black;
|
||
|
||
// 八个句柄的位置
|
||
Point[] handlePoints = new Point[]
|
||
{
|
||
// 左上角
|
||
new Point(rectangle.Left, rectangle.Top),
|
||
// 上中
|
||
new Point(rectangle.Left + rectangle.Width / 2, rectangle.Top),
|
||
// 右上角
|
||
new Point(rectangle.Right, rectangle.Top),
|
||
// 右中
|
||
new Point(rectangle.Right, rectangle.Top + rectangle.Height / 2),
|
||
// 右下角
|
||
new Point(rectangle.Right, rectangle.Bottom),
|
||
// 下中
|
||
new Point(rectangle.Left + rectangle.Width / 2, rectangle.Bottom),
|
||
// 左下角
|
||
new Point(rectangle.Left, rectangle.Bottom),
|
||
// 左中
|
||
new Point(rectangle.Left, rectangle.Top + rectangle.Height / 2)
|
||
};
|
||
|
||
// 绘制每个句柄
|
||
using (SolidBrush handleBrush = new SolidBrush(handleColor))
|
||
using (Pen handleBorderPen = new Pen(handleBorderColor, 1))
|
||
{
|
||
foreach (Point point in handlePoints)
|
||
{
|
||
// 计算句柄矩形
|
||
Rectangle handleRect = new Rectangle(
|
||
point.X - handleSize / 2,
|
||
point.Y - handleSize / 2,
|
||
handleSize,
|
||
handleSize);
|
||
|
||
// 绘制句柄(白色填充,黑色边框)
|
||
g.FillRectangle(handleBrush, handleRect);
|
||
g.DrawRectangle(handleBorderPen, handleRect);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 鼠标双击事件 - 在绘制状态时,随机生成一个颜色,用于绘制矩形框
|
||
/// </summary>
|
||
private void PicBoxTemp_MouseDoubleClick(object sender, MouseEventArgs e)
|
||
{
|
||
// 仅在绘制状态下处理双击事件
|
||
if (_isDrawingMode)
|
||
{
|
||
// 创建随机数生成器
|
||
Random random = new Random();
|
||
|
||
// 定义基本颜色数组,提高辨识度
|
||
Color[] basicColors = new Color[]
|
||
{
|
||
Color.Red, // 红色
|
||
Color.Green, // 绿色
|
||
Color.Blue, // 蓝色
|
||
Color.Yellow, // 黄色
|
||
Color.Cyan, // 青色
|
||
Color.Magenta, // 品红色
|
||
Color.Orange, // 橙色
|
||
Color.Purple, // 紫色
|
||
Color.Lime, // 酸橙绿
|
||
Color.Pink, // 粉色
|
||
Color.Teal, // 蓝绿色
|
||
Color.Brown // 棕色
|
||
};
|
||
|
||
// 70%的概率选择基本颜色,30%的概率生成随机颜色
|
||
if (random.Next(10) < 7)
|
||
{
|
||
// 从基本颜色数组中随机选择一个颜色
|
||
_selectedColor = basicColors[random.Next(basicColors.Length)];
|
||
}
|
||
else
|
||
{
|
||
// 生成随机颜色(避免太暗的颜色,确保可见性)
|
||
_selectedColor = Color.FromArgb(
|
||
random.Next(80, 256), // R
|
||
random.Next(80, 256), // G
|
||
random.Next(80, 256) // B
|
||
);
|
||
}
|
||
|
||
// 更新按钮图标,显示新生成的颜色
|
||
UpdateColorButtonIcon();
|
||
|
||
// 可以在这里添加状态栏提示或其他反馈
|
||
// 例如:statusLabel.Text = "颜色已更改为:" + _selectedColor.Name;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 鼠标点击事件 - 处理区域选中、右击退出选中状态,以及温差图绘制状态下的单击绘制和擦除
|
||
/// </summary>
|
||
private void PicBoxTemp_MouseClick(object sender, MouseEventArgs e)
|
||
{
|
||
// 温差图绘制状态下处理左键单击 - 只有在有选中的温差图例时才进行绘制
|
||
bool hasSelectedLegend = false;
|
||
int selectedRowIndex = -1;
|
||
|
||
// 检查是否有选中的温差图例
|
||
if (dataGridViewTempDiff.SelectedRows.Count > 0)
|
||
{
|
||
selectedRowIndex = dataGridViewTempDiff.SelectedRows[0].Index;
|
||
}
|
||
else if (dataGridViewTempDiff.SelectedCells.Count > 0)
|
||
{
|
||
selectedRowIndex = dataGridViewTempDiff.SelectedCells[0].RowIndex;
|
||
}
|
||
|
||
// 检查选中行索引是否有效
|
||
hasSelectedLegend = selectedRowIndex >= 0 && selectedRowIndex < tempDiffData.Count;
|
||
|
||
if (_isTempDiffDrawingMode && e.Button == MouseButtons.Left && picBoxTemp.Image != null && !_isDrawingRectangle && hasSelectedLegend)
|
||
{
|
||
// 初始化温差层图像(如果不存在或尺寸不匹配)
|
||
if (_tempDiffOverlayImage == null ||
|
||
_tempDiffOverlayImage.Width != picBoxTemp.Image.Width ||
|
||
_tempDiffOverlayImage.Height != picBoxTemp.Image.Height)
|
||
{
|
||
InitializeTempDiffOverlayImage();
|
||
}
|
||
|
||
// 获取相对于图像的坐标
|
||
Point imagePoint = ControlPointToImagePoint(e.Location);
|
||
|
||
// 在温差层图像上绘制/擦除
|
||
using (Graphics g = Graphics.FromImage(_tempDiffOverlayImage))
|
||
{
|
||
// 取消抗锯齿,使用最近邻插值,获得边界清晰的图像
|
||
g.SmoothingMode = SmoothingMode.None;
|
||
g.InterpolationMode = InterpolationMode.NearestNeighbor;
|
||
|
||
if (_isEraseMode)
|
||
{
|
||
// 擦除模式:使用透明色填充,设置CompositingMode为清除
|
||
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
|
||
|
||
// 计算考虑图像缩放比例的画笔大小
|
||
// 使用封装的方法获取调整后的画笔大小和半大小
|
||
int adjustedBrushSize = GetAdjustedBrushSize();
|
||
int halfSize = adjustedBrushSize / 2;
|
||
|
||
// 绘制擦除区域(方形),与光标块保持一致的居中对齐方式
|
||
g.FillRectangle(Brushes.Transparent, imagePoint.X - halfSize, imagePoint.Y - halfSize, adjustedBrushSize, adjustedBrushSize);
|
||
|
||
// 恢复CompositingMode为默认值
|
||
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
|
||
}
|
||
else
|
||
{
|
||
// 绘制模式:获取当前选中的温差图例颜色
|
||
Color selectedColor = Color.Black; // 默认颜色
|
||
|
||
// 直接使用方法开始时定义的selectedRowIndex变量
|
||
// 如果选中行索引有效,获取对应的颜色
|
||
if (selectedRowIndex >= 0 && selectedRowIndex < tempDiffData.Count)
|
||
{
|
||
selectedColor = (Color)tempDiffData[selectedRowIndex]["color"];
|
||
}
|
||
|
||
// 使用封装的方法获取调整后的画笔大小和半大小
|
||
int adjustedBrushSize = GetAdjustedBrushSize();
|
||
int halfSize = adjustedBrushSize / 2;
|
||
|
||
// 绘制区域(方形),与光标块保持一致的居中对齐方式
|
||
using (SolidBrush brush = new SolidBrush(selectedColor))
|
||
{
|
||
g.FillRectangle(brush, imagePoint.X - halfSize, imagePoint.Y - halfSize, adjustedBrushSize, adjustedBrushSize);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 刷新显示
|
||
picBoxTemp.Invalidate();
|
||
return;
|
||
}
|
||
|
||
// 温差图绘制状态下的其他情况(非左键单击),不执行任何操作
|
||
if (_isTempDiffDrawingMode)
|
||
{
|
||
return;
|
||
}
|
||
|
||
// 处理右键点击 - 退出选中状态
|
||
if (!_isDrawingMode && e.Button == MouseButtons.Right && _selectedRegionIndex != -1)
|
||
{
|
||
// 取消选中状态
|
||
_selectedRegionIndex = -1;
|
||
// 清空区域编号文本框
|
||
txtRegionNumber.Text = "";
|
||
|
||
// 使用统一的方法更新按钮可见性,设置为就绪状态
|
||
UpdateButtonsVisibility(0); // 0表示初始状态/就绪状态
|
||
|
||
// 刷新绘制
|
||
picBoxTemp.Invalidate();
|
||
return; // 处理完右键事件后返回
|
||
}
|
||
|
||
// 仅在就绪状态(非绘制模式)下处理左键点击
|
||
if (!_isDrawingMode && e.Button == MouseButtons.Left && picBoxTemp.Image != null && !_isResizing)
|
||
{
|
||
// 将控件坐标转换为图像坐标
|
||
Point imagePoint = ControlPointToImagePoint(e.Location);
|
||
|
||
// 检查是否点击在某个区域内
|
||
bool clickedOnRegion = false;
|
||
foreach (RegionInfo region in _drawnRectangles)
|
||
{
|
||
if (region.ImageRectangle.Contains(imagePoint))
|
||
{
|
||
// 选中该区域(点击已选中区域不再取消选中)
|
||
_selectedRegionIndex = region.Index;
|
||
clickedOnRegion = true;
|
||
// 更新区域编号文本框
|
||
txtRegionNumber.Text = region.Index.ToString();
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 如果没有点击在任何区域上,取消选中
|
||
if (!clickedOnRegion)
|
||
{
|
||
_selectedRegionIndex = -1;
|
||
}
|
||
|
||
// 更新按钮的可见性(简化为单行条件调用,移除不必要的try-catch)
|
||
UpdateButtonsVisibility(_selectedRegionIndex != -1 ? 1 : 0);
|
||
|
||
// 刷新绘制
|
||
picBoxTemp.Invalidate();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新温差层图像中指定颜色的所有像素
|
||
/// 将旧颜色的像素替换为新颜色
|
||
/// </summary>
|
||
/// <param name="oldColor">需要替换的旧颜色</param>
|
||
/// <param name="newColor">替换后的新颜色</param>
|
||
private void UpdateTempDiffOverlayPixelsColor(Color oldColor, Color newColor)
|
||
{
|
||
if (_tempDiffOverlayImage == null || !( _tempDiffOverlayImage is Bitmap))
|
||
return;
|
||
|
||
Bitmap bitmap = (Bitmap)_tempDiffOverlayImage;
|
||
|
||
// 锁定位图以提高性能
|
||
BitmapData bitmapData = bitmap.LockBits(
|
||
new Rectangle(0, 0, bitmap.Width, bitmap.Height),
|
||
ImageLockMode.ReadWrite,
|
||
PixelFormat.Format32bppArgb);
|
||
|
||
try
|
||
{
|
||
int bytesPerPixel = 4; // 32bppArgb格式每像素4字节
|
||
int byteCount = bitmapData.Stride * bitmapData.Height;
|
||
byte[] pixels = new byte[byteCount];
|
||
|
||
// 将图像数据复制到数组
|
||
Marshal.Copy(bitmapData.Scan0, pixels, 0, byteCount);
|
||
|
||
// 转换颜色为ARGB字节数组以便比较
|
||
byte oldA = oldColor.A;
|
||
byte oldR = oldColor.R;
|
||
byte oldG = oldColor.G;
|
||
byte oldB = oldColor.B;
|
||
|
||
byte newA = newColor.A;
|
||
byte newR = newColor.R;
|
||
byte newG = newColor.G;
|
||
byte newB = newColor.B;
|
||
|
||
// 遍历所有像素并替换颜色
|
||
for (int i = 0; i < byteCount; i += bytesPerPixel)
|
||
{
|
||
// ARGB格式:从低位到高位是B, G, R, A
|
||
if (pixels[i] == oldB && pixels[i + 1] == oldG && pixels[i + 2] == oldR && pixels[i + 3] == oldA)
|
||
{
|
||
pixels[i] = newB; // B
|
||
pixels[i + 1] = newG; // G
|
||
pixels[i + 2] = newR; // R
|
||
pixels[i + 3] = newA; // A
|
||
}
|
||
}
|
||
|
||
// 将修改后的数据复制回位图
|
||
Marshal.Copy(pixels, 0, bitmapData.Scan0, byteCount);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("更新温差层像素颜色失败: " + ex.Message);
|
||
}
|
||
finally
|
||
{
|
||
// 解锁位图
|
||
bitmap.UnlockBits(bitmapData);
|
||
// 刷新图像显示
|
||
picBoxTemp.Invalidate();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 当图像更新或控件大小变化时,重新创建叠加层图像
|
||
/// 确保矩形框正确显示在新的尺寸下
|
||
/// </summary>
|
||
private void UpdateOverlayForSizeChange()
|
||
{
|
||
// 处理温差层图像尺寸变化
|
||
if (picBoxTemp.Image != null && _tempDiffOverlayImage != null && (
|
||
_tempDiffOverlayImage.Width != picBoxTemp.Image.Width ||
|
||
_tempDiffOverlayImage.Height != picBoxTemp.Image.Height))
|
||
{
|
||
InitializeTempDiffOverlayImage();
|
||
}
|
||
|
||
// 处理矩形叠加层图像
|
||
if (picBoxTemp.Image != null && _drawnRectangles.Count > 0)
|
||
{
|
||
CreateRectangleOverlayImage();
|
||
picBoxTemp.Invalidate();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 当控件大小改变时,更新叠加层以确保矩形框正确缩放
|
||
/// </summary>
|
||
private void PicBoxTemp_SizeChanged(object sender, EventArgs e)
|
||
{
|
||
UpdateOverlayForSizeChange();
|
||
}
|
||
|
||
private void Timer_Tick(object sender, EventArgs e)
|
||
{
|
||
// 这里可以添加每秒需要执行的代码
|
||
// 例如:更新界面数据、检查状态等
|
||
if (DesignMode || this.IsDisposed || this.Disposing)
|
||
return;
|
||
|
||
// 线程安全检查 - 确保在UI线程上执行
|
||
if (this.InvokeRequired)
|
||
{
|
||
try
|
||
{
|
||
this.BeginInvoke(new Action(() =>
|
||
{
|
||
// 直接在这里实现更新图像的逻辑
|
||
}));
|
||
}
|
||
catch (ObjectDisposedException)
|
||
{
|
||
// 控件已释放,忽略
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 直接实现更新图像的逻辑
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新pictureBoxTemperatureDisplay的图像
|
||
/// 此方法可以从任何线程调用
|
||
/// 注意:根据要求,不修改此方法的核心逻辑
|
||
/// </summary>
|
||
/// <param name="NewImage">要显示的新图像</param>
|
||
public void UpdateRealTimeImage(Image NewImage)
|
||
{
|
||
// 空值检查
|
||
if (NewImage == null)
|
||
{
|
||
Console.WriteLine("传入UpdateRealTimeImage的图像为空");
|
||
return;
|
||
}
|
||
|
||
// 检查是否处于设计模式或窗口不可见
|
||
if (DesignMode || !this.Visible || this.IsDisposed || this.Disposing)
|
||
{
|
||
// 如果不满足条件,释放图像资源并返回
|
||
NewImage.Dispose();
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
// 线程安全检查 - 确保在UI线程上执行
|
||
if (this.InvokeRequired)
|
||
{
|
||
try
|
||
{
|
||
// 使用BeginInvoke在UI线程上更新图像
|
||
this.BeginInvoke(new Action(() =>
|
||
{
|
||
try
|
||
{
|
||
// 确保窗口未被释放
|
||
if (!this.IsDisposed && !this.Disposing && picBoxTemp != null && !picBoxTemp.IsDisposed)
|
||
{
|
||
// 保存旧图像引用,以便在设置新图像后释放
|
||
Image oldImage = picBoxTemp.Image;
|
||
|
||
// 设置新图像
|
||
picBoxTemp.Image = NewImage;
|
||
|
||
// 释放旧图像资源
|
||
if (oldImage != null && oldImage != NewImage)
|
||
{
|
||
oldImage.Dispose();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 如果控件已释放,确保释放图像资源
|
||
NewImage.Dispose();
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"更新设置窗口图像失败: {ex.Message}");
|
||
// 确保在异常情况下释放图像资源
|
||
try
|
||
{
|
||
NewImage.Dispose();
|
||
}
|
||
catch {}
|
||
}
|
||
}));
|
||
}
|
||
catch (ObjectDisposedException)
|
||
{
|
||
// 控件已释放,忽略并释放图像资源
|
||
NewImage.Dispose();
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 在UI线程上直接更新图像
|
||
if (!this.IsDisposed && !this.Disposing && picBoxTemp != null && !picBoxTemp.IsDisposed)
|
||
{
|
||
// 保存旧图像引用,以便在设置新图像后释放
|
||
Image oldImage = picBoxTemp.Image;
|
||
|
||
// 设置新图像
|
||
picBoxTemp.Image = NewImage;
|
||
|
||
// 释放旧图像资源
|
||
if (oldImage != null && oldImage != NewImage)
|
||
{
|
||
oldImage.Dispose();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 如果控件已释放,确保释放图像资源
|
||
NewImage.Dispose();
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"处理实时图像更新时出错: {ex.Message}");
|
||
// 确保在任何异常情况下都释放图像资源
|
||
try
|
||
{
|
||
NewImage.Dispose();
|
||
}
|
||
catch {}
|
||
}
|
||
}
|
||
|
||
// 上次调整时间,用于节流
|
||
private DateTime lastAdjustTime = DateTime.MinValue;
|
||
// 节流时间间隔(毫秒)
|
||
private const int throttleInterval = 100;
|
||
// 缓存的可见按钮数量,避免每次都重新计算
|
||
private int cachedVisibleButtonCount = -1;
|
||
|
||
/// <summary>
|
||
/// 窗体大小变化事件处理程序
|
||
/// 根据toolStripContainer宽度动态调整toolStrip和TopToolStripPanel的尺寸
|
||
/// </summary>
|
||
private void Setting_Resize(object sender, EventArgs e)
|
||
{
|
||
// 实现节流,避免频繁调用
|
||
DateTime now = DateTime.Now;
|
||
if ((now - lastAdjustTime).TotalMilliseconds >= throttleInterval)
|
||
{
|
||
AdjustToolStripDimensions();
|
||
lastAdjustTime = now;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据toolStripContainer宽度动态调整toolStrip和TopToolStripPanel的尺寸
|
||
/// </summary>
|
||
private void AdjustToolStripDimensions()
|
||
{
|
||
try
|
||
{
|
||
// 确保toolStripContainer和toolStrip不为null,并且窗体未被释放
|
||
if (toolStripContainer != null && toolStrip != null && !this.IsDisposed)
|
||
{
|
||
// 检查窗口句柄是否已创建,如果已创建则使用BeginInvoke,否则直接执行
|
||
if (this.IsHandleCreated)
|
||
{
|
||
// 使用BeginInvoke确保调整操作在UI线程的下一个消息循环中执行
|
||
this.BeginInvoke((MethodInvoker)delegate
|
||
{
|
||
TryAdjustToolStripDimensions();
|
||
});
|
||
}
|
||
else
|
||
{
|
||
// 窗口句柄未创建时直接执行
|
||
TryAdjustToolStripDimensions();
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("调整toolStrip尺寸失败: " + ex.Message);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 实际执行toolStrip尺寸调整的逻辑
|
||
/// </summary>
|
||
private void TryAdjustToolStripDimensions()
|
||
{
|
||
try
|
||
{
|
||
// 再次检查控件是否存在且不为null,并检查是否已被释放
|
||
if (toolStripContainer != null && toolStrip != null && !this.IsDisposed && !toolStrip.IsDisposed && !toolStripContainer.IsDisposed)
|
||
{
|
||
// 获取toolStripContainer的宽度
|
||
int containerWidth = toolStripContainer.Width;
|
||
|
||
// 只有当宽度发生显著变化时才进行调整(容差5像素)
|
||
bool widthChanged = Math.Abs(toolStrip.Width - containerWidth) > 5;
|
||
if (widthChanged)
|
||
{
|
||
toolStrip.Width = containerWidth;
|
||
}
|
||
|
||
// 优化:仅当缓存无效或按钮状态可能变化时才重新计算可见按钮数量
|
||
bool needRecalculateButtons = cachedVisibleButtonCount == -1;
|
||
if (!needRecalculateButtons)
|
||
{
|
||
// 快速检查:只检查前几个按钮作为可见性变化的判断依据
|
||
int quickCheckCount = 0;
|
||
foreach (ToolStripItem item in toolStrip.Items)
|
||
{
|
||
if (quickCheckCount >= 5) break; // 只检查前5个按钮
|
||
if (item.Visible && !(item is ToolStripSeparator))
|
||
{
|
||
quickCheckCount++;
|
||
}
|
||
}
|
||
// 如果快速检查结果与缓存的差异超过一半,则重新计算
|
||
needRecalculateButtons = quickCheckCount > cachedVisibleButtonCount / 2 || quickCheckCount < cachedVisibleButtonCount / 2;
|
||
}
|
||
|
||
// 重新计算可见按钮数量(如果需要)
|
||
if (needRecalculateButtons)
|
||
{
|
||
int visibleButtonCount = 0;
|
||
foreach (ToolStripItem item in toolStrip.Items)
|
||
{
|
||
if (item.Visible && !(item is ToolStripSeparator))
|
||
{
|
||
visibleButtonCount++;
|
||
}
|
||
}
|
||
cachedVisibleButtonCount = visibleButtonCount;
|
||
}
|
||
|
||
// 简化计算逻辑
|
||
int buttonWidth = 40;
|
||
int buttonsPerRow = Math.Max(1, containerWidth / buttonWidth);
|
||
int requiredRows = Math.Min(3, (int)Math.Ceiling((double)cachedVisibleButtonCount / buttonsPerRow));
|
||
|
||
// 简化高度计算,使用固定值避免多次计算
|
||
int requiredHeight = requiredRows * 40;
|
||
requiredHeight = Math.Max(requiredHeight, 60);
|
||
|
||
// 只有当高度需要显著变化时才更新(容差10像素)
|
||
bool heightChanged = Math.Abs(toolStrip.MinimumSize.Height - requiredHeight) > 10;
|
||
if (heightChanged)
|
||
{
|
||
toolStrip.MinimumSize = new Size(toolStrip.MinimumSize.Width, requiredHeight);
|
||
// 设置TopToolStripPanel的最小高度
|
||
toolStripContainer.TopToolStripPanel.MinimumSize = new Size(toolStripContainer.TopToolStripPanel.MinimumSize.Width, requiredHeight);
|
||
}
|
||
|
||
// 只在实际有尺寸变化时执行布局,并使用BeginInvoke延迟执行
|
||
if (widthChanged || heightChanged)
|
||
{
|
||
this.BeginInvoke((MethodInvoker)delegate
|
||
{
|
||
try
|
||
{
|
||
// 再次检查控件状态
|
||
if (!this.IsDisposed && !toolStrip.IsDisposed && !toolStripContainer.IsDisposed)
|
||
{
|
||
toolStrip.PerformLayout();
|
||
toolStripContainer.PerformLayout();
|
||
}
|
||
}
|
||
catch (Exception innerEx)
|
||
{
|
||
Console.WriteLine("执行布局失败: " + innerEx.Message);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("执行toolStrip尺寸调整失败: " + ex.Message);
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 根据颜色查找对应的温度值
|
||
/// </summary>
|
||
/// <param name="color">要查找的颜色</param>
|
||
/// <returns>对应的温度值,如果没有找到则返回null</returns>
|
||
private double? GetTemperatureByColor(Color color)
|
||
{
|
||
// 遍历温差图例数据,查找最接近的颜色
|
||
foreach (var item in tempDiffData)
|
||
{
|
||
Color legendColor = (Color)item["color"];
|
||
|
||
// 如果颜色完全匹配
|
||
if (legendColor.R == color.R && legendColor.G == color.G && legendColor.B == color.B)
|
||
{
|
||
string tempString = item["tempDiffValue"].ToString().Replace("°C", "").Trim();
|
||
return Convert.ToDouble(tempString);
|
||
}
|
||
}
|
||
|
||
// 如果没有找到完全匹配的颜色,返回null
|
||
return null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存温差图例按钮点击事件处理程序
|
||
/// </summary>
|
||
/// <summary>
|
||
/// 新建温差图按钮点击事件处理程序
|
||
/// </summary>
|
||
private void BtnNewTempDiff_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
// 用透明色清空温差层图像
|
||
if (_tempDiffOverlayImage != null)
|
||
{
|
||
_tempDiffOverlayImage.Dispose();
|
||
_tempDiffOverlayImage = null;
|
||
}
|
||
|
||
// 确保温差层图像已初始化
|
||
if (picBoxTemp.Image != null)
|
||
{
|
||
InitializeTempDiffOverlayImage();
|
||
}
|
||
|
||
// 移除所有已有的温差图例列表
|
||
tempDiffData.Clear();
|
||
dataGridViewTempDiff.Rows.Clear();
|
||
|
||
// 更新显示
|
||
picBoxTemp.Invalidate();
|
||
MessageBox.Show("已成功创建新的温差图!", "操作成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"新建温差图时发生错误: {ex.Message}");
|
||
MessageBox.Show($"新建失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载温差图按钮点击事件处理程序
|
||
/// </summary>
|
||
private void BtnLoadTempDiff_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
// 弹出用户打开文件对话框,用户选择要加载的csv文件
|
||
OpenFileDialog openFileDialog = new OpenFileDialog
|
||
{
|
||
Filter = "CSV文件 (*.csv)|*.csv|所有文件 (*.*)|*.*",
|
||
Title = "加载温差图例和温度数据",
|
||
FileName = "温差数据.csv"
|
||
};
|
||
|
||
if (openFileDialog.ShowDialog() == DialogResult.OK)
|
||
{
|
||
// 用透明色清空温差层图像
|
||
if (_tempDiffOverlayImage != null)
|
||
{
|
||
_tempDiffOverlayImage.Dispose();
|
||
_tempDiffOverlayImage = null;
|
||
}
|
||
|
||
// 确保温差层图像已初始化
|
||
if (picBoxTemp.Image != null)
|
||
{
|
||
InitializeTempDiffOverlayImage();
|
||
}
|
||
|
||
// 移除所有已有的温差图例列表
|
||
tempDiffData.Clear();
|
||
dataGridViewTempDiff.Rows.Clear();
|
||
|
||
bool readingLegend = false;
|
||
bool readingPixelData = false;
|
||
// 使用温度值作为键,颜色作为值的映射表,与保存时保持一致
|
||
Dictionary<double, Color> tempToColorMap = new Dictionary<double, Color>();
|
||
|
||
// 读取CSV文件
|
||
using (StreamReader reader = new StreamReader(openFileDialog.FileName, Encoding.UTF8))
|
||
{
|
||
string line;
|
||
while ((line = reader.ReadLine()) != null)
|
||
{
|
||
line = line.Trim();
|
||
if (string.IsNullOrEmpty(line))
|
||
continue;
|
||
|
||
// 识别温差图例信息部分
|
||
if (line == "温差图例信息")
|
||
{
|
||
readingLegend = true;
|
||
readingPixelData = false;
|
||
reader.ReadLine(); // 跳过表头行
|
||
continue;
|
||
}
|
||
|
||
// 识别像素温度数据部分
|
||
if (line == "像素温度数据")
|
||
{
|
||
readingLegend = false;
|
||
readingPixelData = true;
|
||
reader.ReadLine(); // 跳过表头行
|
||
continue;
|
||
}
|
||
|
||
// 处理温差图例信息
|
||
if (readingLegend && _tempDiffOverlayImage is Bitmap)
|
||
{
|
||
string[] parts = line.Split(',');
|
||
if (parts.Length == 2)
|
||
{
|
||
try
|
||
{
|
||
double temperature = Convert.ToDouble(parts[0]);
|
||
string colorHex = parts[1].TrimStart('#');
|
||
Color color = Color.FromArgb(
|
||
Convert.ToInt32(colorHex.Substring(0, 2), 16),
|
||
Convert.ToInt32(colorHex.Substring(2, 2), 16),
|
||
Convert.ToInt32(colorHex.Substring(4, 2), 16)
|
||
);
|
||
|
||
// 添加到温差图例列表
|
||
int rowIndex = dataGridViewTempDiff.Rows.Add();
|
||
dataGridViewTempDiff.Rows[rowIndex].Cells["tempDiffValue"].Value = $"{temperature:F1}°C";
|
||
|
||
// 保存到tempDiffData列表和映射表
|
||
Dictionary<string, object> item = new Dictionary<string, object>
|
||
{
|
||
{ "tempDiffValue", $"{temperature:F1}°C" },
|
||
{ "color", color }
|
||
};
|
||
tempDiffData.Add(item);
|
||
tempToColorMap[temperature] = color;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"解析温差图例失败: {ex.Message}");
|
||
}
|
||
}
|
||
}
|
||
|
||
// 处理像素温度数据
|
||
if (readingPixelData && _tempDiffOverlayImage is Bitmap)
|
||
{
|
||
string[] parts = line.Split(',');
|
||
if (parts.Length == 3 && parts[0] != "无温差图像数据")
|
||
{
|
||
try
|
||
{
|
||
int x = Convert.ToInt32(parts[0]);
|
||
int y = Convert.ToInt32(parts[1]);
|
||
double temperature = Convert.ToDouble(parts[2]);
|
||
|
||
// 找到对应的颜色 - 必须精确匹配
|
||
Color pixelColor = Color.Transparent;
|
||
// 尝试精确匹配
|
||
if (tempToColorMap.TryGetValue(Math.Round(temperature, 1), out Color exactColor))
|
||
{
|
||
pixelColor = exactColor;
|
||
}
|
||
else
|
||
{
|
||
// 如果没有精确匹配,显示错误并中止加载
|
||
MessageBox.Show($"温度值 {temperature:F1}°C 在温差图例中找不到精确匹配,请检查温差图例设置!", "加载失败", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||
return;
|
||
}
|
||
|
||
if (pixelColor != Color.Transparent)
|
||
{
|
||
// 绘制温差层图像对应的像素
|
||
// 由于保存时保存了所有非透明像素,加载时只需要设置对应像素点的颜色即可
|
||
if (_tempDiffOverlayImage is Bitmap bitmap)
|
||
{
|
||
// 直接设置像素点颜色,这是最准确的方式
|
||
bitmap.SetPixel(x, y, pixelColor);
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"解析像素数据失败: {ex.Message}");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 更新显示
|
||
picBoxTemp.Invalidate();
|
||
MessageBox.Show("温差图已成功加载!", "加载成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"加载温差数据时发生错误: {ex.Message}");
|
||
MessageBox.Show($"加载失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
private void BtnSaveTempDiff_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
// 弹出保存文件对话框
|
||
SaveFileDialog saveFileDialog = new SaveFileDialog
|
||
{
|
||
Filter = "CSV文件 (*.csv)|*.csv|所有文件 (*.*)|*.*",
|
||
Title = "保存温差图例和温度数据",
|
||
FileName = $"温差数据.csv"
|
||
};
|
||
|
||
if (saveFileDialog.ShowDialog() == DialogResult.OK)
|
||
{
|
||
using (StreamWriter writer = new StreamWriter(saveFileDialog.FileName, false, Encoding.UTF8))
|
||
{
|
||
// 写入温差图例信息
|
||
writer.WriteLine("温差图例信息");
|
||
writer.WriteLine("温度(°C),颜色");
|
||
|
||
// 保存温差图例列表中的所有温差图例信息
|
||
foreach (var item in tempDiffData)
|
||
{
|
||
// 获取温差值并去除°C符号进行转换
|
||
string tempString = item["tempDiffValue"].ToString().Replace("°C", "").Trim();
|
||
double temperature = Convert.ToDouble(tempString);
|
||
Color color = (Color)item["color"];
|
||
string colorHex = $"#{color.R:X2}{color.G:X2}{color.B:X2}";
|
||
writer.WriteLine($"{temperature:F1},{colorHex}");
|
||
}
|
||
|
||
// 写入分隔行
|
||
writer.WriteLine();
|
||
writer.WriteLine("像素温度数据");
|
||
writer.WriteLine("X坐标,Y坐标,温度值(°C)");
|
||
|
||
// 从_tempDiffOverlayImage中获取真实温度数据
|
||
if (_tempDiffOverlayImage != null && _tempDiffOverlayImage is Bitmap)
|
||
{
|
||
Bitmap bitmap = (Bitmap)_tempDiffOverlayImage;
|
||
|
||
// 锁定位图以提高性能
|
||
BitmapData bitmapData = bitmap.LockBits(
|
||
new Rectangle(0, 0, bitmap.Width, bitmap.Height),
|
||
ImageLockMode.ReadOnly,
|
||
PixelFormat.Format32bppArgb);
|
||
|
||
try
|
||
{
|
||
int bytesPerPixel = 4; // 32bppArgb格式每像素4字节
|
||
int byteCount = bitmapData.Stride * bitmapData.Height;
|
||
byte[] pixels = new byte[byteCount];
|
||
|
||
// 将图像数据复制到数组
|
||
Marshal.Copy(bitmapData.Scan0, pixels, 0, byteCount);
|
||
|
||
// 修改:保存所有非透明像素,不再每隔10个像素采样
|
||
// 这样可以确保加载时能准确还原原始绘制效果
|
||
for (int y = 0; y < bitmap.Height; y++)
|
||
{
|
||
for (int x = 0; x < bitmap.Width; x++)
|
||
{
|
||
// 计算当前像素在数组中的位置
|
||
int pixelIndex = y * bitmapData.Stride + x * bytesPerPixel;
|
||
|
||
// 获取像素颜色
|
||
byte b = pixels[pixelIndex];
|
||
byte g = pixels[pixelIndex + 1];
|
||
byte r = pixels[pixelIndex + 2];
|
||
byte a = pixels[pixelIndex + 3];
|
||
|
||
// 只处理非透明的像素
|
||
if (a > 0)
|
||
{
|
||
Color pixelColor = Color.FromArgb(a, r, g, b);
|
||
double? temperature = GetTemperatureByColor(pixelColor);
|
||
|
||
// 如果找到对应的温度值,则写入文件
|
||
if (temperature.HasValue)
|
||
{
|
||
writer.WriteLine($"{x},{y},{temperature.Value:F1}");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
finally
|
||
{
|
||
// 解锁位图
|
||
bitmap.UnlockBits(bitmapData);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 如果没有温差图像数据,提示用户
|
||
writer.WriteLine("无温差图像数据");
|
||
MessageBox.Show("警告:未找到温差图像数据,仅保存了温差图例信息。", "警告", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||
}
|
||
}
|
||
|
||
MessageBox.Show("温差图例和温度数据已成功保存到CSV文件!", "保存成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"保存温差数据时发生错误: {ex.Message}");
|
||
MessageBox.Show($"保存失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||
}
|
||
}
|
||
}
|
||
} |