如何在绘画事件之前静态绘制线条?
How to draw lines statically before paint event?
我正在创建一个频谱分析仪,我想将其网格化。我的光谱将绘制在 windows 表单应用程序的面板上。当我在 panel_Paint 函数内绘制线条时,它与面板的绘制事件相关联,它每秒重绘 2048/48000 次,因为它是一个实时 FFT 频谱,FFT 大小为 2048,采样率为 48000。这是我的在 paint 函数中画线的方式:
private void panel1_Paint(object sender, PaintEventArgs e)
{
for (int i = 0; i < panel1.Width; i+= panel1.Width / 20)
{
for (int j = 0; j < panel1.Height; j+= panel1.Height / 20)
{
g.DrawLine(Pens.Black, i, j, i, panel1.Height + j);
g.DrawLine(Pens.Black, i, j, panel1.Width + i, j);
}
}
}
这是线后的频谱:
我喜欢频谱的外观,但在计算时没有时间在等待重新绘制线条时处理频谱。
注意:上图是频谱的即时(单个 fft 块)屏幕截图。正如我上面提到的,在执行实时应用程序时,频谱不可读。
任何帮助将不胜感激,提前致谢。
制作带有图形的实时应用程序并非易事。几点:
- 以 2048 Hz 绘画对观看者没有用,屏幕通常以 60 Hz 刷新,因为我们的眼睛无法观察到更快的变化。
- 绘制事件旨在一次绘制所有内容,因此网格线和 FFT 图。
- 您可以通过调用
Control.Invalidate()
来触发绘画事件,或者在这种情况下 Control.Refresh()
可能会带来更愉快的刷新。参见:https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.control.invalidate
- 可能需要将收集信号和计算 FFT 频谱的代码移动到后台线程,这样您可以更好地控制其时序并可以不间断地处理数据。
因此组织应用程序的可能方式如下:
- 让后台线程执行繁重的工作并在缓冲区数据结构中收集数据。
- 在 UI 表单上放置一个计时器,定期调用
Control.Refresh()
。参见:https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.timer
- 让绘制事件处理程序读取缓冲区中准备的所有数据并将其清除。然后使用该数据绘制 FFT 图。合并来自多个 FFT 块的所有数据。
- 确保缓冲区可以包含多个 FFT 块,方法是使用合适的数据结构,例如
ConcurrentQueue<T>
,其中 T 是包含一个 FFT 块的类型。参见:https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentqueue-1
有很多关于如何从后台线程与前台线程(如处理绘制事件的线程)进行通信的信息。所以我不打算在这里重复。但如果做得好,它会使您的应用程序 运行 流畅且响应迅速。
将多个 FFT 块组合成一个图形绘制是一项挑战。因此,首先,通过例如丢弃除最新块以外的所有块来保持该部分简单。然后在其他一切正常的情况下改进这部分的设计。
也从设置为 20 Hz 左右的油漆计时器开始,然后从那里增加它。因为图形系统可能不喜欢很多绘画动作。
原来我用错了这个工作的工具,在处理实时数据时,你应该在显示它之前进行非常快的计算。当涉及到显示时,双缓冲父显示工具就像“魔术”一样工作。
正如我在上面的问题中提到的,我在绘制频谱时在窗体上使用了面板,当我移除面板并将整个窗体更改为 UserControl 时,我能够进行双缓冲,这是我做不到的。 t在面板上做,整个问题就解决了。
当DoubleBuffered 属性设置为true时,当前绘图数据在绘制到要绘图的表面之前被写入第二个缓冲区,那么这个第二个缓冲区中的数据是快速写入将要绘制的内存中。瞧!防止闪烁。
UserControl 的加载函数中的代码:
this.DoubleBuffered = true;
我正在创建一个频谱分析仪,我想将其网格化。我的光谱将绘制在 windows 表单应用程序的面板上。当我在 panel_Paint 函数内绘制线条时,它与面板的绘制事件相关联,它每秒重绘 2048/48000 次,因为它是一个实时 FFT 频谱,FFT 大小为 2048,采样率为 48000。这是我的在 paint 函数中画线的方式:
private void panel1_Paint(object sender, PaintEventArgs e)
{
for (int i = 0; i < panel1.Width; i+= panel1.Width / 20)
{
for (int j = 0; j < panel1.Height; j+= panel1.Height / 20)
{
g.DrawLine(Pens.Black, i, j, i, panel1.Height + j);
g.DrawLine(Pens.Black, i, j, panel1.Width + i, j);
}
}
}
这是线后的频谱:
我喜欢频谱的外观,但在计算时没有时间在等待重新绘制线条时处理频谱。
注意:上图是频谱的即时(单个 fft 块)屏幕截图。正如我上面提到的,在执行实时应用程序时,频谱不可读。
任何帮助将不胜感激,提前致谢。
制作带有图形的实时应用程序并非易事。几点:
- 以 2048 Hz 绘画对观看者没有用,屏幕通常以 60 Hz 刷新,因为我们的眼睛无法观察到更快的变化。
- 绘制事件旨在一次绘制所有内容,因此网格线和 FFT 图。
- 您可以通过调用
Control.Invalidate()
来触发绘画事件,或者在这种情况下Control.Refresh()
可能会带来更愉快的刷新。参见:https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.control.invalidate - 可能需要将收集信号和计算 FFT 频谱的代码移动到后台线程,这样您可以更好地控制其时序并可以不间断地处理数据。
因此组织应用程序的可能方式如下:
- 让后台线程执行繁重的工作并在缓冲区数据结构中收集数据。
- 在 UI 表单上放置一个计时器,定期调用
Control.Refresh()
。参见:https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.timer - 让绘制事件处理程序读取缓冲区中准备的所有数据并将其清除。然后使用该数据绘制 FFT 图。合并来自多个 FFT 块的所有数据。
- 确保缓冲区可以包含多个 FFT 块,方法是使用合适的数据结构,例如
ConcurrentQueue<T>
,其中 T 是包含一个 FFT 块的类型。参见:https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentqueue-1
有很多关于如何从后台线程与前台线程(如处理绘制事件的线程)进行通信的信息。所以我不打算在这里重复。但如果做得好,它会使您的应用程序 运行 流畅且响应迅速。
将多个 FFT 块组合成一个图形绘制是一项挑战。因此,首先,通过例如丢弃除最新块以外的所有块来保持该部分简单。然后在其他一切正常的情况下改进这部分的设计。
也从设置为 20 Hz 左右的油漆计时器开始,然后从那里增加它。因为图形系统可能不喜欢很多绘画动作。
原来我用错了这个工作的工具,在处理实时数据时,你应该在显示它之前进行非常快的计算。当涉及到显示时,双缓冲父显示工具就像“魔术”一样工作。
正如我在上面的问题中提到的,我在绘制频谱时在窗体上使用了面板,当我移除面板并将整个窗体更改为 UserControl 时,我能够进行双缓冲,这是我做不到的。 t在面板上做,整个问题就解决了。
当DoubleBuffered 属性设置为true时,当前绘图数据在绘制到要绘图的表面之前被写入第二个缓冲区,那么这个第二个缓冲区中的数据是快速写入将要绘制的内存中。瞧!防止闪烁。
UserControl 的加载函数中的代码:
this.DoubleBuffered = true;