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

1405 lines
58 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.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);
}
}
/// <summary>
/// 鼠标按下事件 - 处理右击退出绘制状态、左击开始绘制矩形和开始调整区域大小
/// </summary>
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; // 隐藏删除区域按钮
break;
case 1: // 选中区域状态
btnDrawRegion.Visible = false; // 隐藏绘制区域按钮
btnDrawTempDiff.Visible = false; // 隐藏温差图按钮
btnSelectColor.Visible = true; // 显示颜色选择按钮
btnDeleteRegion.Visible = true; // 显示删除区域按钮
break;
case 2: // 绘制状态
btnSelectColor.Visible = true; // 显示颜色选择按钮
btnDrawRegion.Visible = true; // 显示绘制区域按钮
btnDeleteRegion.Visible = false; // 隐藏删除区域按钮
btnDrawTempDiff.Visible = false; // 隐藏温差图按钮
break;
case 3: // 温差图绘制状态
btnDrawTempDiff.Visible = true; // 显示温差图按钮
btnDrawRegion.Visible = false; // 隐藏绘制区域按钮
btnSelectColor.Visible = false; // 隐藏颜色选择按钮
btnDeleteRegion.Visible = false; // 隐藏删除区域按钮
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;
// 隐藏删除按钮和颜色选择按钮
btnDeleteRegion.Visible = false;
btnSelectColor.Visible = false;
// 显示绘制区域按钮
btnDrawRegion.Visible = true;
// 重绘叠加层
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);
btnDeleteRegion.Visible = isRegionSelected;
btnSelectColor.Visible = isRegionSelected;
btnDrawRegion.Visible = !isRegionSelected; // 选中区域时隐藏绘制按钮
// 温差图按钮只在就绪状态且未选中区域时显示
btnDrawTempDiff.Visible = !isRegionSelected;
}
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 {}
}
}
}
}