添加项目文件。
This commit is contained in:
524
压差法气体渗透仪/TrendChart.cs
Normal file
524
压差法气体渗透仪/TrendChart.cs
Normal file
@@ -0,0 +1,524 @@
|
||||
using OxyPlot;
|
||||
using OxyPlot.Axes;
|
||||
using OxyPlot.Legends;
|
||||
using OxyPlot.Series;
|
||||
using OxyPlot.WindowsForms;
|
||||
using Sunny.UI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using 薄膜气体透过率测试仪.Data;
|
||||
using OxyPlot.Pdf;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
|
||||
namespace 薄膜气体透过率测试仪
|
||||
{
|
||||
public partial class TrendChart : UIForm
|
||||
{
|
||||
|
||||
private TestScreen _testScreen;
|
||||
Timer Timer;
|
||||
|
||||
public List<lineData> Data { get; set; }
|
||||
|
||||
public void AddChart(lineData data)
|
||||
{
|
||||
Data.Add(data);
|
||||
CreateBasicLineChart();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public TrendChart(List<lineData> Data)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
Timer = new Timer();
|
||||
Timer.Tick += Timer_Tick;
|
||||
Timer.Interval = 500;
|
||||
Timer.Start();
|
||||
|
||||
this.Data = Data;
|
||||
if (Data == null || Data.Count() == 0)
|
||||
{
|
||||
Data = new List<lineData>();
|
||||
}
|
||||
CreateBasicLineChart();
|
||||
}
|
||||
|
||||
private void Timer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
uiLabel2.Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
}
|
||||
|
||||
private void CreateBasicLineChart()
|
||||
{
|
||||
// 创建模型
|
||||
var plotModel = new PlotModel
|
||||
{
|
||||
//Title = "月度销售额统计",
|
||||
//TitleFontSize = 16,
|
||||
//TitleFontWeight = FontWeights.Bold
|
||||
};
|
||||
|
||||
// 设置坐标轴
|
||||
plotModel.Axes.Add(new LinearAxis
|
||||
{
|
||||
Position = AxisPosition.Left,
|
||||
Title = "低压室压力(kPa)",
|
||||
//MajorStep = 0.05,
|
||||
// 可选:设置最小范围,确保坐标轴不会太小
|
||||
MinimumRange = 0.1, // 最小显示范围
|
||||
|
||||
// 可选:设置绝对的边界
|
||||
AbsoluteMinimum = 0, // 确保最小值不会低于0
|
||||
AbsoluteMaximum = double.MaxValue, // 最大值无上限
|
||||
|
||||
// 自动调整的额外设置
|
||||
MaximumPadding = 0.05, // 顶部留5%的空白
|
||||
MinimumPadding = 0.05, // 底部留5%的空白
|
||||
});
|
||||
|
||||
plotModel.Axes.Add(new CategoryAxis
|
||||
{
|
||||
Position = AxisPosition.Bottom,
|
||||
Title = "时间",
|
||||
MajorStep = 30,
|
||||
ItemsSource = Data.Select(s => s.X)
|
||||
});
|
||||
|
||||
// 创建折线系列
|
||||
var lineSeries = new LineSeries
|
||||
{
|
||||
//Title = "2024年",
|
||||
Color = OxyColor.FromRgb(255, 0, 0), // 红色
|
||||
//MarkerType = MarkerType.Circle,
|
||||
//MarkerSize = 5,
|
||||
//MarkerFill = OxyColors.Red,
|
||||
MarkerStroke = OxyColors.White,
|
||||
MarkerStrokeThickness = 1,
|
||||
StrokeThickness = 2
|
||||
};
|
||||
|
||||
// 添加数据点
|
||||
float[] salesData = Data.Select(x => x.Y).ToArray();
|
||||
|
||||
for (int i = 0; i < salesData.Length; i++)
|
||||
{
|
||||
lineSeries.Points.Add(new DataPoint(i, salesData[i]));
|
||||
}
|
||||
|
||||
// 添加系列到模型
|
||||
plotModel.Series.Add(lineSeries);
|
||||
|
||||
|
||||
// 设置背景色
|
||||
plotModel.Background = OxyColors.White;
|
||||
plotModel.PlotAreaBorderColor = OxyColors.LightGray;
|
||||
|
||||
// 应用到PlotView
|
||||
plotView1.Model = plotModel;
|
||||
}
|
||||
|
||||
private void TrendChart_FormClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
Timer?.Stop();
|
||||
Timer?.Dispose();
|
||||
}
|
||||
|
||||
//导出图表
|
||||
private void uiButton1_Click(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 创建保存文件对话框
|
||||
using (SaveFileDialog saveFileDialog = new SaveFileDialog())
|
||||
{
|
||||
saveFileDialog.Filter = "PDF文件 (*.pdf)|*.pdf|PNG图片 (*.png)|*.png|所有文件 (*.*)|*.*";
|
||||
saveFileDialog.FilterIndex = 1;
|
||||
saveFileDialog.RestoreDirectory = true;
|
||||
saveFileDialog.FileName = $"趋势图表_{DateTime.Now:yyyyMMdd_HHmmss}.pdf";
|
||||
|
||||
if (saveFileDialog.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
string filePath = saveFileDialog.FileName;
|
||||
|
||||
// 根据选择的文件类型调用不同的导出方法
|
||||
string extension = Path.GetExtension(filePath).ToLower();
|
||||
|
||||
if (extension == ".pdf")
|
||||
{
|
||||
ExportChartToPdf(filePath);
|
||||
}
|
||||
else if (extension == ".png")
|
||||
{
|
||||
ExportChartToImage(filePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果没有扩展名或扩展名不对,默认使用PDF
|
||||
if (string.IsNullOrEmpty(extension))
|
||||
{
|
||||
filePath += ".pdf";
|
||||
ExportChartToPdf(filePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 尝试导出为PDF
|
||||
ExportChartToPdf(filePath);
|
||||
}
|
||||
}
|
||||
|
||||
MessageBox.Show($"图表已成功导出到:\n{filePath}",
|
||||
"导出成功",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"导出图表时发生错误:\n{ex.Message}",
|
||||
"导出失败",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void ExportChartToPdf(string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 1. 创建图表图片
|
||||
Bitmap chartImage = CreateChartImage(800, 600);
|
||||
|
||||
// 2. 使用System.Drawing.Printing创建PDF(简单方法)
|
||||
// 或者直接保存为高质量图片
|
||||
string tempImagePath = Path.Combine(Path.GetTempPath(), $"chart_temp_{Guid.NewGuid()}.png");
|
||||
|
||||
// 保存为高质量PNG
|
||||
chartImage.Save(tempImagePath, System.Drawing.Imaging.ImageFormat.Png);
|
||||
|
||||
// 3. 由于System.Drawing不直接支持PDF,我们使用iTextSharp(需要安装NuGet包)
|
||||
// 如果没有安装iTextSharp,我们直接保存为图片
|
||||
ExportImageAsPdf(chartImage, filePath);
|
||||
|
||||
// 4. 清理临时文件
|
||||
if (File.Exists(tempImagePath))
|
||||
{
|
||||
File.Delete(tempImagePath);
|
||||
}
|
||||
|
||||
// 5. 释放资源
|
||||
chartImage.Dispose();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 如果PDF导出失败,降级为图片导出
|
||||
string imagePath = Path.ChangeExtension(filePath, ".png");
|
||||
ExportChartToImage(imagePath);
|
||||
throw new Exception($"PDF导出失败,已保存为图片格式: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void ExportChartToImage(string filePath)
|
||||
{
|
||||
Bitmap chartImage = null;
|
||||
try
|
||||
{
|
||||
// 创建高质量图表图片
|
||||
chartImage = CreateChartImage(1600, 1200); // 高分辨率
|
||||
|
||||
// 保存图片
|
||||
System.Drawing.Imaging.ImageFormat format = System.Drawing.Imaging.ImageFormat.Png;
|
||||
string extension = Path.GetExtension(filePath).ToLower();
|
||||
|
||||
if (extension == ".jpg" || extension == ".jpeg")
|
||||
format = System.Drawing.Imaging.ImageFormat.Jpeg;
|
||||
else if (extension == ".bmp")
|
||||
format = System.Drawing.Imaging.ImageFormat.Bmp;
|
||||
|
||||
chartImage.Save(filePath, format);
|
||||
}
|
||||
finally
|
||||
{
|
||||
chartImage?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Bitmap CreateChartImage(int width, int height)
|
||||
{
|
||||
Bitmap bitmap = new Bitmap(width, height);
|
||||
|
||||
using (Graphics graphics = Graphics.FromImage(bitmap))
|
||||
{
|
||||
// 设置高质量绘制
|
||||
graphics.SmoothingMode = SmoothingMode.HighQuality;
|
||||
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
graphics.CompositingQuality = CompositingQuality.HighQuality;
|
||||
|
||||
// 填充白色背景
|
||||
graphics.Clear(Color.White);
|
||||
|
||||
// 设置边距
|
||||
int marginLeft = 80;
|
||||
int marginRight = 40;
|
||||
int marginTop = 60;
|
||||
int marginBottom = 80;
|
||||
|
||||
int chartWidth = width - marginLeft - marginRight;
|
||||
int chartHeight = height - marginTop - marginBottom;
|
||||
|
||||
// 1. 绘制标题
|
||||
using (Font titleFont = new Font("微软雅黑", 16, FontStyle.Bold))
|
||||
using (Brush titleBrush = new SolidBrush(Color.Black))
|
||||
{
|
||||
string title = "趋势图表";
|
||||
SizeF titleSize = graphics.MeasureString(title, titleFont);
|
||||
graphics.DrawString(title, titleFont, titleBrush,
|
||||
(width - titleSize.Width) / 2, 20);
|
||||
}
|
||||
|
||||
// 2. 绘制坐标轴
|
||||
Pen axisPen = new Pen(Color.Black, 2);
|
||||
|
||||
// Y轴
|
||||
graphics.DrawLine(axisPen, marginLeft, marginTop, marginLeft, marginTop + chartHeight);
|
||||
// X轴
|
||||
graphics.DrawLine(axisPen, marginLeft, marginTop + chartHeight,
|
||||
marginLeft + chartWidth, marginTop + chartHeight);
|
||||
|
||||
// 3. 绘制坐标轴标题
|
||||
using (Font axisFont = new Font("微软雅黑", 12))
|
||||
using (Brush axisBrush = new SolidBrush(Color.Black))
|
||||
{
|
||||
// Y轴标题
|
||||
graphics.DrawString("低压室压力(kPa)", axisFont, axisBrush,
|
||||
marginLeft - 70, marginTop - 40);
|
||||
|
||||
// X轴标题
|
||||
graphics.DrawString("时间", axisFont, axisBrush,
|
||||
marginLeft + chartWidth / 2 - 20, marginTop + chartHeight + 40);
|
||||
}
|
||||
|
||||
// 4. 计算数据范围
|
||||
if (Data != null && Data.Count > 0)
|
||||
{
|
||||
float minY = Data.Min(d => d.Y);
|
||||
float maxY = Data.Max(d => d.Y);
|
||||
|
||||
// 添加一些余量
|
||||
float rangeY = maxY - minY;
|
||||
if (rangeY == 0) rangeY = 1;
|
||||
minY -= rangeY * 0.1f;
|
||||
maxY += rangeY * 0.1f;
|
||||
|
||||
// 5. 自适应Y轴刻度
|
||||
int yTicks = 5; // 固定5个刻度,清晰易读
|
||||
|
||||
// 计算合适的刻度步长
|
||||
float roughStep = (maxY - minY) / (yTicks - 1);
|
||||
float magnitude = (float)Math.Pow(10, Math.Floor(Math.Log10(roughStep)));
|
||||
float stepSize = (float)Math.Ceiling(roughStep / magnitude) * magnitude;
|
||||
|
||||
// 调整minY和maxY为步长的整数倍
|
||||
minY = (float)(Math.Floor(minY / stepSize) * stepSize);
|
||||
maxY = (float)(Math.Ceiling(maxY / stepSize) * stepSize);
|
||||
|
||||
// 重新计算刻度数量
|
||||
yTicks = (int)Math.Ceiling((maxY - minY) / stepSize);
|
||||
float yTickStep = chartHeight / yTicks;
|
||||
float yValueStep = stepSize;
|
||||
|
||||
using (Font tickFont = new Font("Arial", 9))
|
||||
using (Brush tickBrush = new SolidBrush(Color.Black))
|
||||
{
|
||||
for (int i = 0; i <= yTicks; i++)
|
||||
{
|
||||
float yPos = marginTop + chartHeight - (i * yTickStep);
|
||||
float yValue = minY + (i * yValueStep);
|
||||
|
||||
// 绘制刻度线
|
||||
graphics.DrawLine(Pens.Gray, marginLeft - 5, yPos, marginLeft, yPos);
|
||||
|
||||
// 绘制刻度值(根据值的大小选择合适的小数位数)
|
||||
string tickLabel;
|
||||
if (Math.Abs(yValue) < 0.01)
|
||||
tickLabel = "0";
|
||||
else if (stepSize < 0.1)
|
||||
tickLabel = yValue.ToString("F3");
|
||||
else if (stepSize < 1)
|
||||
tickLabel = yValue.ToString("F2");
|
||||
else if (stepSize < 10)
|
||||
tickLabel = yValue.ToString("F1");
|
||||
else
|
||||
tickLabel = yValue.ToString("F0");
|
||||
|
||||
SizeF textSize = graphics.MeasureString(tickLabel, tickFont);
|
||||
graphics.DrawString(tickLabel, tickFont, tickBrush,
|
||||
marginLeft - textSize.Width - 8, yPos - textSize.Height / 2);
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 绘制X轴刻度(时间)
|
||||
int xTicks = Math.Min(10, Data.Count);
|
||||
using (Font tickFont = new Font("Arial", 9))
|
||||
using (Brush tickBrush = new SolidBrush(Color.Black))
|
||||
{
|
||||
for (int i = 0; i < xTicks; i++)
|
||||
{
|
||||
int dataIndex = i * (Data.Count - 1) / (xTicks - 1);
|
||||
if (dataIndex >= Data.Count) dataIndex = Data.Count - 1;
|
||||
|
||||
float xPos = marginLeft + (i * chartWidth / (float)(xTicks - 1));
|
||||
|
||||
// 绘制刻度线
|
||||
graphics.DrawLine(Pens.Gray, xPos, marginTop + chartHeight,
|
||||
xPos, marginTop + chartHeight + 5);
|
||||
|
||||
// 绘制时间标签
|
||||
if (dataIndex < Data.Count)
|
||||
{
|
||||
string timeLabel = Data[dataIndex].X;
|
||||
if (timeLabel.Length > 8)
|
||||
timeLabel = timeLabel.Substring(timeLabel.Length - 8); // 只取时间部分
|
||||
|
||||
SizeF textSize = graphics.MeasureString(timeLabel, tickFont);
|
||||
graphics.DrawString(timeLabel, tickFont, tickBrush,
|
||||
xPos - textSize.Width / 2, marginTop + chartHeight + 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 7. 绘制数据曲线
|
||||
if (Data.Count > 1)
|
||||
{
|
||||
PointF[] points = new PointF[Data.Count];
|
||||
|
||||
for (int i = 0; i < Data.Count; i++)
|
||||
{
|
||||
float xRatio = i / (float)(Data.Count - 1);
|
||||
float yRatio = (Data[i].Y - minY) / (maxY - minY);
|
||||
|
||||
float xPos = marginLeft + (xRatio * chartWidth);
|
||||
float yPos = marginTop + chartHeight - (yRatio * chartHeight);
|
||||
|
||||
points[i] = new PointF(xPos, yPos);
|
||||
}
|
||||
|
||||
// 绘制曲线
|
||||
using (Pen curvePen = new Pen(Color.Red, 2))
|
||||
{
|
||||
curvePen.StartCap = System.Drawing.Drawing2D.LineCap.Round;
|
||||
curvePen.EndCap = System.Drawing.Drawing2D.LineCap.Round;
|
||||
|
||||
// 绘制平滑曲线
|
||||
if (points.Length > 1)
|
||||
{
|
||||
graphics.DrawCurve(curvePen, points, 0.5f);
|
||||
}
|
||||
|
||||
// 绘制数据点
|
||||
foreach (PointF point in points)
|
||||
{
|
||||
graphics.FillEllipse(Brushes.Red,
|
||||
point.X - 3, point.Y - 3, 6, 6);
|
||||
graphics.DrawEllipse(Pens.White,
|
||||
point.X - 3, point.Y - 3, 6, 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 8. 绘制图例
|
||||
using (Font legendFont = new Font("微软雅黑", 10))
|
||||
{
|
||||
string legendText = "压力数据";
|
||||
SizeF legendSize = graphics.MeasureString(legendText, legendFont);
|
||||
|
||||
RectangleF legendRect = new RectangleF(
|
||||
width - marginRight - legendSize.Width - 30,
|
||||
marginTop,
|
||||
legendSize.Width + 20,
|
||||
legendSize.Height + 10);
|
||||
|
||||
graphics.FillRectangle(Brushes.White, legendRect);
|
||||
graphics.DrawRectangle(Pens.Gray,
|
||||
legendRect.X, legendRect.Y, legendRect.Width, legendRect.Height);
|
||||
|
||||
// 绘制图例颜色标记
|
||||
graphics.FillRectangle(Brushes.Red,
|
||||
legendRect.X + 5, legendRect.Y + 5, 15, legendRect.Height - 10);
|
||||
|
||||
graphics.DrawString(legendText, legendFont, Brushes.Black,
|
||||
legendRect.X + 25, legendRect.Y + 5);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 没有数据时显示提示
|
||||
using (Font infoFont = new Font("微软雅黑", 14))
|
||||
using (Brush infoBrush = new SolidBrush(Color.Gray))
|
||||
{
|
||||
string infoText = "暂无数据";
|
||||
SizeF textSize = graphics.MeasureString(infoText, infoFont);
|
||||
graphics.DrawString(infoText, infoFont, infoBrush,
|
||||
(width - textSize.Width) / 2, (height - textSize.Height) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
// 9. 绘制边框
|
||||
graphics.DrawRectangle(Pens.LightGray, 0, 0, width - 1, height - 1);
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
private void ExportImageAsPdf(Image image, string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 方法1:如果有iTextSharp,使用它创建PDF
|
||||
// 这需要安装iTextSharp NuGet包
|
||||
|
||||
// 方法2:直接保存为图片
|
||||
string imagePath = Path.ChangeExtension(filePath, ".png");
|
||||
image.Save(imagePath, System.Drawing.Imaging.ImageFormat.Png);
|
||||
|
||||
// 方法3:使用Windows自带的PDF打印功能(需要用户交互)
|
||||
|
||||
// 这里我们使用方法2,因为最简单可靠
|
||||
// 如果需要PDF,可以提示用户安装iTextSharp
|
||||
throw new NotImplementedException("PDF导出需要安装iTextSharp库,已保存为PNG图片");
|
||||
}
|
||||
catch (NotImplementedException)
|
||||
{
|
||||
// 保存为图片
|
||||
string imagePath = Path.ChangeExtension(filePath, ".png");
|
||||
image.Save(imagePath, System.Drawing.Imaging.ImageFormat.Png);
|
||||
|
||||
// 更新文件路径
|
||||
File.Move(imagePath, filePath.Replace(".pdf", ".png"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user