大家好,业余C#、WPF使用者;在这里我写了一个绘制地震波形图的窗体程序,遇到了一些问题:
1.数据量达到1000道左右时,很卡,即使数据量不多,一两百道也要等好一会儿才出结果
2.当该窗口被关闭时,没有释放内存
该函数如下:
readonly FileStream fileStream;
readonly int nsamples;
readonly int startTrace;
readonly int endTrace;
readonly float interval;
double[,] datax;
double[] datay;
//readonly float maxAmpliude = 1;
public Waveform(FileStream fileStream, int nsamples, int startTrace, int endTrace, float startTime, float endTime, float interval, int type)
{
//判断一下吧
if (nsamples < 0)
{
MessageBox.Show("num. of samples error!");
return;
}
if (startTrace <= 0)
{
MessageBox.Show("num. of start trace(s) error!");
return;
}
if (endTrace < startTrace)
{
MessageBox.Show("num. of end trace(s) error!");
return;
}
if (startTime < 0)
{
MessageBox.Show("num. of start time error!");
return;
}
if (endTime < startTime)
{
MessageBox.Show("num. of end time error!");
return;
}
if (interval < 0)
{
MessageBoxResult result = MessageBox.Show("num. of sample interval error!\ndo you want to replay it using 1 ms ?", "Requick", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
interval = 1;
else if (result == MessageBoxResult.No)
return;
}
InitializeComponent();
this.fileStream = fileStream;
this.nsamples = nsamples;
this.startTrace = startTrace;
this.endTrace = endTrace;
this.interval = interval;
int startSampl = Convert.ToInt32(startTime / interval);
int endSampl = Convert.ToInt32(endTime / interval);
this.datax = new double[endSampl - startSampl + 1, endTrace - startTrace + 1];
this.datay = new double[endSampl - startSampl + 1];
for (int i = 0; i < datay.Length; i++)
datay[i] = startTime + i * interval;
fileStream.Seek(3600 + (startTrace - 1) * (240 + 4 * nsamples), SeekOrigin.Begin);
if (type == 1)
IBMSwap(startSampl, endSampl);
else if (type == 2)
IBM(startSampl, endSampl);
else if (type == 3)
IEEESwap(startSampl, endSampl);
else
IEEE(startSampl, endSampl);
ChartArea chartArea = new ChartArea();
ChartPlot_Waveform.ChartAreas.Add(chartArea);
for (int i = 0; i < endTrace - startTrace + 1; i++)
{
Series series = new Series();
series.ChartType = SeriesChartType.Line;
series.Color = System.Drawing.Color.Black;
for (int j = 0; j < datay.Length; j++)
{
series.Points.AddXY(datax[j, i], datay[j]);
}
ChartPlot_Waveform.Series.Add(series);
}
int traceInterval = (endTrace - startTrace + 1) / 5;
//ChartPlot_Waveform.Dock = System.Windows.Forms.DockStyle.Fill;
//设置属性
ChartPlot_Waveform.ChartAreas[0].AxisX.Title = "Trace(s)";
ChartPlot_Waveform.ChartAreas[0].AxisX.TitleFont = new System.Drawing.Font("Times new Roman", 8);
ChartPlot_Waveform.ChartAreas[0].AxisX.Minimum = startTrace - 1;
ChartPlot_Waveform.ChartAreas[0].AxisX.Maximum = endTrace + 1;
ChartPlot_Waveform.ChartAreas[0].AxisX.Interval = traceInterval;
ChartPlot_Waveform.ChartAreas[0].AxisX.MajorGrid.Enabled = false;
ChartPlot_Waveform.ChartAreas[0].AxisY.IsReversed = true;
ChartPlot_Waveform.ChartAreas[0].AxisY.Title = "Time / ms";
ChartPlot_Waveform.ChartAreas[0].AxisY.Minimum = startTime;
ChartPlot_Waveform.ChartAreas[0].AxisY.Maximum = endTime;
//ChartPlot_Waveform.ChartAreas[0].AxisY.Interval = interval;
ChartPlot_Waveform.ChartAreas[0].AxisY.MajorGrid.LineColor = System.Drawing.Color.Gray;
ChartPlot_Waveform.ChartAreas[0].AxisY.TitleFont = new System.Drawing.Font("Times new Roman", 8);
ChartPlot_Waveform.ChartAreas[0].BorderDashStyle = ChartDashStyle.Solid;
ChartPlot_Waveform.ChartAreas[0].BorderColor = System.Drawing.Color.Black;
ChartPlot_Waveform.Legends.Clear();
//
}
调用函数是另外一个窗体的一个事件
调用函数如下:
private void WaveForm_Grapic_Button_Click(object sender, RoutedEventArgs e)
{
int k = 0;
if (Data_IBM_ComboBox.IsSelected == true && Data_CheckBox.IsChecked == true)
k = 1;
else if (Data_IBM_ComboBox.IsSelected == true && Data_CheckBox.IsChecked == false)
k = 2;
else if (Data_IBM_ComboBox.IsSealed == false && Data_CheckBox.IsChecked == true)
k = 3;
else
k = 4;
Waveform waveform = new Waveform(fileStraeamOperater, nsamples, Convert.ToInt32(StartTrace.Text),
Convert.ToInt32(EndTrace.Text), Convert.ToSingle(StartTime.Text), Convert.ToSingle(EndTime.Text), Convert.ToInt32(fileHead[5].Value) / 1000, k);
waveform.Show();
}
附一张200道的图吧,以便理解
另外:还有两个个问题:
1.我想正相位填充颜色:以第一道为例,其取值范围为0-1,以0.5为界,想将大于0.5的部分填充为纯色
2.绘制颜色图,类似于这样,两张图是同一个数据
请各位帮忙解决问题
问题分析:
这个问题可能是由于绘制时使用了较为耗费资源的方式造成的。可以考虑使用DrawingVisual对象进行绘制,DrawingVisual对象比Canvas等容器对象更轻量级,绘制效率更高。
可以在窗口关闭时,手动释放资源,如绘制所使用的DrawingVisual对象等。
解决思路:
DrawingVisual visual = new DrawingVisual();
using (DrawingContext dc = visual.RenderOpen())
{
//在DrawingContext中进行绘制
}
canvas.AddVisual(visual);
protected override void OnClosed(EventArgs e)
{
//释放DrawingVisual对象等资源
base.OnClosed(e);
}
代码示例:
以下是使用DrawingVisual对象进行绘制的示例代码:
private void DrawData(List<double> data)
{
if (data == null || data.Count == 0) return;
//创建DrawingVisual对象并添加到Canvas中
DrawingVisual visual = new DrawingVisual();
using (DrawingContext dc = visual.RenderOpen())
{
double width = canvas.ActualWidth;
double height = canvas.ActualHeight;
double xInterval = width / data.Count;
double yInterval = height / 2;
Point start = new Point(0, yInterval);
for (int i = 0; i < data.Count; i++)
{
double y = yInterval - data[i] * yInterval;
Point end = new Point((i + 1) * xInterval, yInterval - data[i] * yInterval);
dc.DrawLine(new Pen(Brushes.Black, 1), start, end);
start = end;
}
}
canvas.AddVisual(visual);
}
在窗口关闭时释放资源的代码示例:
protected override void OnClosed(EventArgs e)
{
//释放DrawingVisual对象等资源
canvas.ClearVisuals();
base.OnClosed(e);
}
个人经验,你可以参考一下
1,首先ChartPlot_Waveform这个控件,你是要调查一下,他可能性能本身就不好
2,WPF本质上性能就是低下的,他比不上winform,更比不上C++,但加载绘制简单的图像,我的经验是10万就撑不住了,2,3万就有点卡了,但控件必须是你自己写的,自己去优化。
3,你现在的代码使用ChartPlot_Waveform.Series.Add(series);添加线,但你添加全是在一个for循环里加的,等于你画面展示时要一起加载,所以这个是你1000条线就卡的主因。目测没有什么特别好的解决办法,你只能试试分批加载数据和寻找一下控件自身有没有缓存的设置,你给开启一下。
4,建议换组件,有个叫LightningChart的组件,收费的,性能非常好,你也可以搜搜其他组件,有一些性能不错的组件
以下答案由GPT-3.5大模型与博主波罗歌共同编写:
针对问题一,数据量大时卡顿的问题,您可以考虑对数据进行分段处理,不一次性全部绘制出来。比如将所有数据分成若干个段,每次只绘制一段数据,等待响应之后再绘制下一段数据。
针对问题二,填充颜色可以通过设置Series的FillColor属性和BorderWidth属性来实现。具体来说,可以将大于0.5的部分绘制成红色,小于0.5的部分不填充色。代码如下:
Series series = new Series();
series.ChartType = SeriesChartType.Line;
for (int i = 0; i < data.Length; i++)
{
double xValue = i;
double yValue = data[i];
series.Points.AddXY(xValue, yValue);
if (yValue >= 0.5)
{
DataPoint point = series.Points[i];
point.BorderColor = Color.Red;
point.BorderWidth = 2;
point.Color = Color.FromArgb(100, 255, 0, 0);
}
}
针对问题三,绘制颜色图可以使用Chart的HeatMap图表类型。HeatMap需要二维数据作为输入,可以将数据的每个时间点作为一个维度,每个道作为另外一个维度。例如可以使用以下代码来绘制颜色图:
double[,] data = GetData(); // 获取二维数据
ChartArea chartArea = new ChartArea();
chartArea.AxisX.Title = "Time";
chartArea.AxisY.Title = "Trace";
chartArea.AxisX.Minimum = 0;
chartArea.AxisX.Maximum = data.GetLength(0);
chartArea.AxisY.Minimum = 0;
chartArea.AxisY.Maximum = data.GetLength(1);
chartArea.AxisX.Interval = 1;
chartArea.AxisY.Interval = 1;
chartArea.AxisX.MajorGrid.Enabled = false;
chartArea.AxisY.MajorGrid.Enabled = false;
ChartPlot_Waveform.ChartAreas.Add(chartArea);
Series series = new Series("HeatMap");
series.ChartType = SeriesChartType.HeatMap;
series["PixelPointWidth"] = "10"; // 设置每个像素点的宽度
series["PixelPointGapDepth"] = "0"; // 设置两个像素点之间的间隙
series["Minimum"] = "0"; // 颜色范围最小值
series["Maximum"] = "1"; // 颜色范围最大值
series.Points.DataBindXY(GetLabels(data.GetLength(0), data.GetLength(1)), GetValues(data));
ChartPlot_Waveform.Series.Add(series);
其中,GetLabels和GetValues分别返回二维数组的横、纵轴标签和数值数组。
如果我的回答解决了您的问题,请采纳!