2

我正在通过 System.Drawing 进行一些绘图,但我遇到了一些问题。

我将数据保存在队列中,并将数据绘制(绘图)到三个图片框

此方法填充图片框,然后滚动图形。

所以不要在以前的图纸之上绘制(并且逐渐看起来更混乱),我找到了两种绘制图表的解决方案。

  1. plot.Clear(BACKGOUNDCOLOR)在绘制循环之前调用 [块评论]

尽管这会导致从执行实际绘图循环所需的时间开始出现闪烁。

  1. plot.DrawLine(channelPen[5], j, 140, j, 0);在每个绘制线之前调用 [评论]

虽然这会导致绘图开始正常,然后很快减速到爬行,就好像在绘图命令之前放置了一个等待命令。

这是供参考的代码:

/*plotx.Clear(BACKGOUNDCOLOR)

ploty.Clear(BACKGOUNDCOLOR)

plotz.Clear(BACKGOUNDCOLOR)*/

for (int j = 1; j < 599; j++)
            {
                if (j > RealTimeBuffer.Count - 1) break;

                QueueEntity past = RealTimeBuffer.ElementAt(j - 1);
                QueueEntity current = RealTimeBuffer.ElementAt(j);

                if (j == 1)
                {
                    //plotx.DrawLine(channelPen[5], 0, 140, 0, 0);
                    //ploty.DrawLine(channelPen[5], 0, 140, 0, 0);
                    //plotz.DrawLine(channelPen[5], 0, 140, 0, 0);
                }

                //plotx.DrawLine(channelPen[5], j, 140, j, 0);
                plotx.DrawLine(channelPen[0], j - 1, (((past.accdata.X - 0x7FFF) / 256) + 64), j, (((current.accdata.X - 0x7FFF) / 256) + 64));

                //ploty.DrawLine(channelPen[5], j, 140, j, 0);
                ploty.DrawLine(channelPen[1], j - 1, (((past.accdata.Y - 0x7FFF) / 256) + 64), j, (((current.accdata.Y - 0x7FFF) / 256) + 64));

                //plotz.DrawLine(markerPen, j, 140, j, 0);
                plotz.DrawLine(channelPen[2], j - 1, (((past.accdata.Z - 0x7FFF) / 256) + 94), j, (((current.accdata.Z - 0x7FFF) / 256) + 94));


            }

有什么技巧可以避免这些开销吗?

如果没有,会有其他/更好的解决方案吗?

编辑:有关解决方案代码,请参阅下面的 [最终解决方案]

4

5 回答 5

4

PictureBox 已经开启了双缓冲。您可以感知闪烁的唯一方法是直接在屏幕上绘制而不是使用 Paint 事件。目前尚不清楚您是否从您的代码段中执行此操作。使用位图缓冲自己也可以,但不如 Windows 窗体实现的双缓冲效率高。

当数据发生变化时调用其 Invalidate() 方法,在 Paint 事件中进行绘制(使用 e.Graphics),它不会闪烁。

于 2010-03-16T01:02:20.797 回答
1

尝试将数据缓冲到另一个位图,然后再将其放入图片框中。这将消除闪烁。

例如:

// create this once for each graph
Bitmap buffer = new Bitmap({width}, {height});

// when the data changes
using (Graphics g = Graphics.FromImage(buffer))
{
    // ... draw using the graphics
}

// draw the buffer on the picture box's image

你是在单独的线程上绘图吗?你每秒绘制数据多少次?如果您在主线程上执行此操作或每秒多次重复绘制整个位图,您的程序会感觉很慢。

于 2010-03-15T22:50:17.977 回答
0

我会使用双缓冲方法。在内存中创建一个新位图(与图形大小相同)创建该位图的图形对象。

然后在该对象上绘制您想要的任何内容。

完成所有绘图后,从图片框创建另一个图形对象,并使用 DrawImage 绘制内存位图。

这应该可以减少闪烁,因为您绘制了屏幕上的所有内容,除了一个更新整个图像的大画。

希望能帮助到你。

于 2010-03-15T22:58:09.873 回答
0

子类化您的图片框并启用双缓冲选项。

公共类 MyBufferedPictureBox 继承 System.Windows.Forms.PictureBox

Public Sub New()
    MyBase.New()

    Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
End Sub

结束类

http://msdn.microsoft.com/en-us/library/system.windows.forms.control.setstyle.aspx

于 2010-03-15T23:02:45.843 回答
0

[最终解决方案]

即使有重复循环,使用绘制事件也足够快!

最后,我没有在每个事件中重复绘制循环,而是使用第一个事件将另外两个图形也绘制到位图上,然后在它们的绘制事件上,简单地绘制位图。

所以基本上最终的解决方案是你的许多答案的组合。

谢谢大家。

private void pictureBoxAccX_Paint(object sender, PaintEventArgs e)
        {
            bufferedploty.Clear(Color.Black);
            bufferedplotz.Clear(Color.Black);           

            for (int j = 1; j < 599; j++)
            {
                if (j > RealTimeBuffer.Count - 1) break;

                QueueEntity past = RealTimeBuffer.ElementAt(j - 1);
                QueueEntity current = RealTimeBuffer.ElementAt(j);

                e.Graphics.DrawLine(channelPen[0], j - 1, (((past.accdata.X - 0x7FFF) / 256) + 64), j, (((current.accdata.X - 0x7FFF) / 256) + 64));
                bufferedploty.DrawLine(channelPen[1], j - 1, (((past.accdata.Y - 0x7FFF) / 256) + 64), j, (((current.accdata.Y - 0x7FFF) / 256) + 64));
                bufferedplotz.DrawLine(channelPen[2], j - 1, (((past.accdata.Z - 0x7FFF) / 256) + 94), j, (((current.accdata.Z - 0x7FFF) / 256) + 94));
            }   
        }


        private void pictureBoxAccY_Paint(object sender, PaintEventArgs e)
        {        
            e.Graphics.DrawImage(BufferedBitMapy, new Point(0, 0));            
        }


        private void pictureBoxAccZ_Paint(object sender, PaintEventArgs e)
        {            
            e.Graphics.DrawImage(BufferedBitMapz, new Point(0, 0));
        }




private void AddAccPoints()
        {
            //Code for putting in New queue data Here...
            pictureBoxAccX.Invalidate();
            pictureBoxAccY.Invalidate();
            pictureBoxAccZ.Invalidate();
        }

编辑:使用这种精确的解决方案,使控件无效可能会导致事件调度未定义并随机停止绘制,因为位图创建是在一个事件方法中完成的。

请参阅此处:Onpaint 事件(无效)在一段时间正常操作(运行时)后更改执行顺序

于 2010-03-16T10:24:57.580 回答