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

686 lines
26 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 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;
public Setting()
{
InitializeComponent();
// 订阅SizeChanged事件确保控件大小变化时矩形框正确缩放
this.picBoxTemp.SizeChanged += new EventHandler(PicBoxTemp_SizeChanged);
// 设置按钮图标
SetButtonIcon();
// 初始化定时器
_timer = new Timer { Interval = 1000 };
_timer.Tick += Timer_Tick;
// 注册窗口事件
this.Shown += Setting_Shown;
this.FormClosing += Setting_FormClosing;
// 注册按钮点击事件
btnDrawRegion.Click += BtnDrawRegion_Click;
btnSelectColor.Click += BtnSelectColor_Click;
}
/// <summary>
/// 绘制区域按钮点击事件
/// </summary>
private void BtnDrawRegion_Click(object sender, EventArgs e)
{
_isDrawingMode = btnDrawRegion.Checked;
if (_isDrawingMode)
{
// 启用绘制模式
picBoxTemp.MouseDown += PicBoxTemp_MouseDown;
picBoxTemp.MouseMove += PicBoxTemp_MouseMove;
picBoxTemp.MouseUp += PicBoxTemp_MouseUp;
picBoxTemp.Cursor = Cursors.Cross;
btnDrawRegion.ToolTipText = "绘制模式已启用,点击图片区域绘制矩形框(点击关闭)";
}
else
{
// 禁用绘制模式
picBoxTemp.MouseDown -= PicBoxTemp_MouseDown;
picBoxTemp.MouseMove -= PicBoxTemp_MouseMove;
picBoxTemp.MouseUp -= PicBoxTemp_MouseUp;
picBoxTemp.Cursor = Cursors.Default;
_currentRectangle = Rectangle.Empty;
btnDrawRegion.ToolTipText = "绘制温度检测区域(点击开启/关闭)";
// 重绘以清除临时矩形
picBoxTemp.Invalidate();
}
}
/// <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_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left && _isDrawingMode)
{
_startPoint = e.Location;
_isDrawing = true;
_currentRectangle = new Rectangle(e.X, e.Y, 0, 0);
}
}
/// <summary>
/// 鼠标移动事件 - 更新矩形大小
/// </summary>
private void PicBoxTemp_MouseMove(object sender, MouseEventArgs e)
{
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();
}
}
/// <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 (_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 (!_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;
// 设置颜色选择按钮的图标
UpdateColorButtonIcon();
}
/// <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();
// 重绘图片区域,显示新颜色的矩形
picBoxTemp.Invalidate();
}
}
}
/// <summary>
/// 窗口显示时启动定时器
/// </summary>
private void Setting_Shown(object sender, EventArgs e)
{
// 仅在非设计模式下启动定时器
if (!DesignMode)
{
_timer.Start();
// 立即执行一次定时器事件,避免首次显示时的延迟
Timer_Tick(sender, e);
}
}
/// <summary>
/// 窗口关闭时停止定时器并释放资源
/// </summary>
private void Setting_FormClosing(object sender, FormClosingEventArgs e)
{
_timer.Stop();
// 释放叠加层图像资源
if (_rectangleOverlayImage != null)
{
_rectangleOverlayImage.Dispose();
_rectangleOverlayImage = null;
}
}
/// <summary>
/// 定时器每秒触发的事件处理方法
/// </summary>
/// <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 {}
}
}
}
}