Files
JoyD/Windows/CS/Framework4.0/Toprie/Toprie/Setting.cs

4597 lines
212 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
}
}
}
}