1665 lines
69 KiB
C#
1665 lines
69 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.ComponentModel;
|
||
using System.Data;
|
||
using System.Drawing;
|
||
using System.Drawing.Drawing2D;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Text;
|
||
using System.Windows.Forms;
|
||
|
||
namespace JoyD.Windows.CS
|
||
{
|
||
public partial class Setting : Form
|
||
{
|
||
// 创建并显示检测配置窗口
|
||
public static Setting Form = new Setting();
|
||
|
||
// 定时器字段
|
||
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 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;
|
||
|
||
public Setting()
|
||
{
|
||
InitializeComponent();
|
||
|
||
// 设置按钮图标
|
||
SetButtonIcon();
|
||
|
||
// 初始化定时器
|
||
_timer = new Timer { Interval = 1000 };
|
||
_timer.Tick += Timer_Tick;
|
||
|
||
// 初始隐藏颜色选择按钮
|
||
btnSelectColor.Visible = false;
|
||
|
||
// 初始隐藏删除按钮
|
||
try
|
||
{
|
||
btnDeleteRegion.Visible = false;
|
||
// 初始状态/就绪状态下显示温差图按钮
|
||
btnDrawTempDiff.Visible = true;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("删除按钮初始化失败: " + ex.Message);
|
||
}
|
||
|
||
// 初始化温差图例DataGridView
|
||
InitializeTempDiffDataGridView();
|
||
|
||
// 初始状态下隐藏温差图例表格
|
||
dataGridViewTempDiff.Visible = false;
|
||
}
|
||
|
||
/// <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.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);
|
||
|
||
// 添加一些示例数据
|
||
AddSampleTempDiffData();
|
||
}
|
||
|
||
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, "");
|
||
}
|
||
|
||
private void DataGridViewTempDiff_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
|
||
{
|
||
// 检查是否是温差值列
|
||
if (dataGridViewTempDiff.CurrentCell.ColumnIndex == dataGridViewTempDiff.Columns["tempDiffValue"].Index)
|
||
{
|
||
// 获取TextBox编辑控件
|
||
TextBox tb = e.Control as TextBox;
|
||
if (tb != null)
|
||
{
|
||
// 清除之前的处理程序
|
||
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();
|
||
|
||
// 尝试解析输入为浮点数
|
||
if (!float.TryParse(value, 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;
|
||
}
|
||
|
||
// 清除错误信息
|
||
dataGridViewTempDiff.Rows[e.RowIndex].ErrorText = string.Empty;
|
||
|
||
// 更新数据模型
|
||
if (e.RowIndex < tempDiffData.Count)
|
||
{
|
||
tempDiffData[e.RowIndex]["tempDiffValue"] = $"{tempValue}°C";
|
||
}
|
||
}
|
||
}
|
||
|
||
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)
|
||
{
|
||
if (e.RowIndex >= 0 && e.RowIndex < tempDiffData.Count)
|
||
{
|
||
// 处理颜色列点击
|
||
if (e.ColumnIndex == dataGridViewTempDiff.Columns["colorColumn"].Index)
|
||
{
|
||
using (ColorDialog colorDialog = new ColorDialog())
|
||
{
|
||
// 设置初始颜色为当前行的颜色
|
||
colorDialog.Color = (Color)tempDiffData[e.RowIndex]["color"];
|
||
|
||
if (colorDialog.ShowDialog() == DialogResult.OK)
|
||
{
|
||
// 更新颜色数据
|
||
tempDiffData[e.RowIndex]["color"] = colorDialog.Color;
|
||
// 刷新单元格以显示新颜色
|
||
dataGridViewTempDiff.Refresh();
|
||
}
|
||
}
|
||
}
|
||
// 处理温差值列点击 - 当点击温差值列时,启动编辑模式
|
||
else if (e.ColumnIndex == dataGridViewTempDiff.Columns["tempDiffValue"].Index)
|
||
{
|
||
dataGridViewTempDiff.BeginEdit(true);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void PicBoxTemp_MouseDown(object sender, MouseEventArgs e)
|
||
{
|
||
// 检查是否处于温差图绘制状态且右击鼠标
|
||
if (_isTempDiffDrawingMode && e.Button == MouseButtons.Right)
|
||
{
|
||
// 退出温差图绘制状态,返回到就绪状态
|
||
ExitTempDiffDrawingMode();
|
||
}
|
||
// 检查是否处于绘制状态且右击鼠标
|
||
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>
|
||
/// <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; // 隐藏删除区域按钮
|
||
dataGridViewTempDiff.Visible = false; // 隐藏温差图例表格
|
||
break;
|
||
case 1: // 选中区域状态
|
||
btnDrawRegion.Visible = false; // 隐藏绘制区域按钮
|
||
btnDrawTempDiff.Visible = false; // 隐藏温差图按钮
|
||
btnSelectColor.Visible = true; // 显示颜色选择按钮
|
||
btnDeleteRegion.Visible = true; // 显示删除区域按钮
|
||
dataGridViewTempDiff.Visible = false; // 隐藏温差图例表格
|
||
break;
|
||
case 2: // 绘制状态
|
||
btnSelectColor.Visible = true; // 显示颜色选择按钮
|
||
btnDrawRegion.Visible = true; // 显示绘制区域按钮
|
||
btnDeleteRegion.Visible = false; // 隐藏删除区域按钮
|
||
btnDrawTempDiff.Visible = false; // 隐藏温差图按钮
|
||
dataGridViewTempDiff.Visible = false; // 隐藏温差图例表格
|
||
break;
|
||
case 3: // 温差图绘制状态
|
||
btnDrawTempDiff.Visible = true; // 显示温差图按钮
|
||
btnDrawRegion.Visible = false; // 隐藏绘制区域按钮
|
||
btnSelectColor.Visible = false; // 隐藏颜色选择按钮
|
||
btnDeleteRegion.Visible = false; // 隐藏删除区域按钮
|
||
dataGridViewTempDiff.Visible = true; // 显示温差图例表格
|
||
break;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("更新按钮可见性失败: " + ex.Message);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 绘制温差图按钮点击事件
|
||
/// </summary>
|
||
private void BtnDrawTempDiff_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
// 设置温差图绘制模式标志
|
||
_isTempDiffDrawingMode = !_isTempDiffDrawingMode;
|
||
btnDrawTempDiff.Checked = _isTempDiffDrawingMode;
|
||
|
||
if (_isTempDiffDrawingMode)
|
||
{
|
||
// 进入温差图绘制状态
|
||
picBoxTemp.Cursor = Cursors.Cross;
|
||
btnDrawTempDiff.ToolTipText = "温差图绘制模式已启用,点击图片区域进行绘制(点击关闭)";
|
||
|
||
// 调用按钮可见性更新方法设置为温差图绘制状态
|
||
UpdateButtonsVisibility(3); // 3表示温差图绘制状态
|
||
}
|
||
else
|
||
{
|
||
// 退出温差图绘制状态,返回到就绪状态
|
||
ExitTempDiffDrawingMode();
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("温差图绘制模式切换失败: " + ex.Message);
|
||
ExitTempDiffDrawingMode();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 退出温差图绘制模式
|
||
/// </summary>
|
||
private void ExitTempDiffDrawingMode()
|
||
{
|
||
_isTempDiffDrawingMode = false;
|
||
btnDrawTempDiff.Checked = false;
|
||
|
||
// 重置鼠标光标
|
||
picBoxTemp.Cursor = Cursors.Default;
|
||
|
||
// 更新按钮提示文本
|
||
btnDrawTempDiff.ToolTipText = "绘制温差图";
|
||
|
||
// 按就绪状态设置按钮可见性
|
||
UpdateButtonsVisibility(0); // 0表示初始状态/就绪状态
|
||
|
||
// 刷新绘制
|
||
picBoxTemp.Invalidate();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 绘制区域按钮点击事件
|
||
/// </summary>
|
||
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>
|
||
private void PicBoxTemp_MouseMove(object sender, MouseEventArgs e)
|
||
{
|
||
// 处理调整大小
|
||
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>
|
||
/// <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 (_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);
|
||
|
||
// 增加计数器并创建新的区域信息(存储图像坐标)
|
||
_regionCounter++;
|
||
RegionInfo regionInfo = new RegionInfo
|
||
{
|
||
ImageRectangle = new Rectangle(imageX, imageY, imageWidth, imageHeight),
|
||
Color = _selectedColor,
|
||
Index = _regionCounter
|
||
};
|
||
|
||
_drawnRectangles.Add(regionInfo);
|
||
|
||
// 检查是否需要完全重建叠加层
|
||
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;
|
||
|
||
// 图像合并机制:先绘制叠加层(根据控件尺寸进行缩放)
|
||
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 (!_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(24, 24);
|
||
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), 3, 3, 18, 18);
|
||
|
||
// 绘制虚线内矩形
|
||
using (Pen dashedPen = new Pen(Color.Black, 1.5f))
|
||
{
|
||
dashedPen.DashPattern = new float[] { 2, 1 };
|
||
g.DrawRectangle(dashedPen, 5, 5, 14, 14);
|
||
}
|
||
|
||
// 绘制四个角的控制点
|
||
SolidBrush brush = new SolidBrush(Color.Black);
|
||
g.FillEllipse(brush, 6, 6, 3, 3); // 左上角
|
||
g.FillEllipse(brush, 15, 6, 3, 3); // 右上角
|
||
g.FillEllipse(brush, 6, 15, 3, 3); // 左下角
|
||
g.FillEllipse(brush, 15, 15, 3, 3); // 右下角
|
||
}
|
||
|
||
// 设置按钮图标并设置透明色
|
||
btnDrawRegion.Image = icon;
|
||
btnDrawRegion.ImageTransparentColor = Color.Transparent;
|
||
|
||
// 设置删除按钮的图标
|
||
try
|
||
{
|
||
Bitmap deleteIcon = new Bitmap(24, 24);
|
||
using (Graphics g = Graphics.FromImage(deleteIcon))
|
||
{
|
||
// 设置高质量绘图
|
||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||
|
||
// 清除背景为透明
|
||
g.Clear(Color.Transparent);
|
||
|
||
// 绘制一个红色的叉号作为删除图标
|
||
Pen deletePen = new Pen(Color.Red, 3);
|
||
g.DrawLine(deletePen, 6, 6, 18, 18); // 从左上角到右下角的线
|
||
g.DrawLine(deletePen, 18, 6, 6, 18); // 从右上角到左下角的线
|
||
}
|
||
|
||
btnDeleteRegion.Image = deleteIcon;
|
||
btnDeleteRegion.ImageTransparentColor = Color.Transparent;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("删除按钮图标设置失败: " + ex.Message);
|
||
}
|
||
|
||
// 设置颜色选择按钮的图标
|
||
UpdateColorButtonIcon();
|
||
|
||
// 设置绘制温差图按钮的图标
|
||
try
|
||
{
|
||
Bitmap tempDiffIcon = new Bitmap(24, 24);
|
||
using (Graphics g = Graphics.FromImage(tempDiffIcon))
|
||
{
|
||
// 设置高质量绘图
|
||
g.SmoothingMode = SmoothingMode.AntiAlias;
|
||
|
||
// 清除背景为透明
|
||
g.Clear(Color.Transparent);
|
||
|
||
// 绘制表示温差的图标 - 使用渐变效果
|
||
// 绘制底部蓝色(低温)和顶部红色(高温)的矩形条
|
||
using (LinearGradientBrush gradientBrush = new LinearGradientBrush(
|
||
new Rectangle(8, 5, 8, 14),
|
||
Color.Blue, // 低温端
|
||
Color.Red, // 高温端
|
||
LinearGradientMode.Vertical))
|
||
{
|
||
g.FillRectangle(gradientBrush, 8, 5, 8, 14);
|
||
}
|
||
|
||
// 添加边框
|
||
g.DrawRectangle(new Pen(Color.Black, 1), 8, 5, 8, 14);
|
||
}
|
||
|
||
btnDrawTempDiff.Image = tempDiffIcon;
|
||
btnDrawTempDiff.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);
|
||
}
|
||
}
|
||
|
||
/// <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;
|
||
}
|
||
}
|
||
|
||
/// <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_MouseClick(object sender, MouseEventArgs e)
|
||
{
|
||
// 处理右键点击 - 退出选中状态
|
||
if (!_isDrawingMode && e.Button == MouseButtons.Right && _selectedRegionIndex != -1)
|
||
{
|
||
// 取消选中状态
|
||
_selectedRegionIndex = -1;
|
||
|
||
// 使用统一的方法更新按钮可见性,设置为就绪状态
|
||
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;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 如果没有点击在任何区域上,取消选中
|
||
if (!clickedOnRegion)
|
||
{
|
||
_selectedRegionIndex = -1;
|
||
}
|
||
|
||
// 更新按钮的可见性
|
||
try
|
||
{
|
||
bool isRegionSelected = (_selectedRegionIndex != -1);
|
||
if (isRegionSelected)
|
||
{
|
||
// 选中区域状态
|
||
UpdateButtonsVisibility(1);
|
||
}
|
||
else
|
||
{
|
||
// 就绪状态
|
||
UpdateButtonsVisibility(0);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine("更新按钮可见性失败: " + ex.Message);
|
||
}
|
||
|
||
// 刷新绘制
|
||
picBoxTemp.Invalidate();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 当图像更新或控件大小变化时,重新创建叠加层图像
|
||
/// 确保矩形框正确显示在新的尺寸下
|
||
/// </summary>
|
||
private void UpdateOverlayForSizeChange()
|
||
{
|
||
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(UpdatePictureBoxImage));
|
||
}
|
||
catch (ObjectDisposedException)
|
||
{
|
||
// 控件已释放,忽略
|
||
}
|
||
return;
|
||
}
|
||
|
||
UpdatePictureBoxImage();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新PictureBox图像的辅助方法
|
||
/// 此方法必须在UI线程上执行
|
||
/// </summary>
|
||
private void UpdatePictureBoxImage()
|
||
{
|
||
}
|
||
|
||
/// <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 {}
|
||
}
|
||
}
|
||
}
|
||
} |