【问题标题】:Performance issue while using PictureBox使用 PictureBox 时的性能问题
【发布时间】:2014-02-27 03:00:42
【问题描述】:

我正在使用 Visual Studio 2010 开发应用程序,并且正在使用 C++/CLI 进行编码。
在我之前的问题中,我遇到了来自串行端口的数据系列问题。现在看起来没问题,现在我正在尝试绘制它们。
我不想使用Chart,所以我想使用使用Graphics 类的手工函数。为了测试我的代码,我创建了两个数组:第一个数组填充了来自高斯的值。第二个是随机数。
当我绘制这些值时,我希望看到我的图像示波器一样增长和更新。第二个Do_Plot 设法通过BackColor 绘制它们的“删除点”。 所有代码都按预期工作,但我遇到了性能问题。如果我在我的电脑上运行代码,我的系列每 500/700 毫秒绘制一次。
有时它会减慢到 1500 毫秒,然后恢复得更快。
我试图在我同事的电脑上运行代码,我注意到第一个系列每 170 毫秒绘制一次,而第二个系列每 950 毫秒绘制一次。 这是代码:

System::Void Form1::button1_Click(System::Object^  sender, System::EventArgs^  e) {
    button1->Enabled = false;

    array<float,1>^ Gauss = gcnew array<float,1>(1001);

    array<float,1>^ Rumore = gcnew array<float,1>(1001);


     /*Some useful variables*/
     Random^ generatore = gcnew Random;
     float a = safe_cast<float>(Math::Round(5/(SIGMA*Math::Sqrt(2*PI)), 2));
     float b = safe_cast<float>(2*(SIGMA*SIGMA));

      /*Start */


      float portante; 
      float r;
      float s;

      int convX =1000/1000;
      int convY =500/2;  

      /*time variables */  

      int bias = 50;
      int dif =600;  

      /*Gap between drawing and removing*/  

      int k = 3; 
      int e1 = 0;


     for ( ; ; ) {

          /*Start*/
          clock_t Start = clock();

          if(textBox1->Text==""){
            portn = 5;
          }
          else
            portn = float::Parse(textBox1->Text);

          /*temp variables to go out the for cycle */
          portante = portn;
          r = rand;
          s = sig;

          /ckeck state is OK */
          check = 0;


            for(int i = 1; i<=1000; i++) {

                Gauss[i] = safe_cast<float>(Math::Round( a*s*Math::Exp(-Math::Pow(((0.01*1*(i))-portante), 2)/b), 2));

                Rumore[i] = safe_cast<float>(Math::Round(r*generatore->NextDouble(), 2));
                bool clipSup = ClipSup(2, Gauss[i]+Rumore[i]);

                if(clipSup==true) {
                    Gauss[i] = 1.99f;
                    Rumore[i] = 0;
                }
                Do_Plot(g, disegna, i-1, Gauss[i-1]+Rumore[i-1], i, Gauss[i]+Rumore[i], convX, convY);


                e1 =(k+i)%1000;
                Do_Plot(g, rimuovi, e1, Gauss[e1]+Rumore[e1], e1+1, Gauss[e1+1]+Rumore[e1+1], convX, convY);




                /*Ckeck if go out for cycle*/
                if(check == CODE_1 ) {
                    portante = portn;
                    break;
                }
                if(check == CODE_2 ) {
                    r = rand;
                    break;
                }
                if(check == CODE_3 ) {
                    s = sig;
                    break;
                }

            }

             clock_t Stop = clock();
             int Diff = Stop-Start;
             label8->Text = Convert::ToString(Diff);
             int tempDiff = (Stop-Start)+bias;
             if(tempDiff>dif)
             {

                //Do_Axes(g);           /*Do Axes*/
                //Do_Grid(g);           /*Do Grid */
                Application::DoEvents();
                dif = 600;
                bias = 0;
            }
            else
                bias +=50; //Else bias grows
      }


}

Do_Plot 在哪里:

void Do_Plot(Graphics^ g, Pen^ penna, int Xi, float Yi, int Xf, float Yf, int convX, int convY) {


    g->DrawLine(penna, (convX*Xi+50), safe_cast<int>(500-(Yi*convY)+50), 
        (convX*Xf+50), safe_cast<int>(500-(Yf*convY)+50)); 
}

我在这里声明了Graphics^ g

public ref class Form1 : public System::Windows::Forms::Form
{
Graphics^ g; 
public:
    Form1(void) 
    {
        InitializeComponent();
        //
        //TODO: aggiungere qui il codice del costruttore.
        //
        g = pictureBox1->CreateGraphics();

    }

我不知道为什么我的代码在另一台 PC 上运行时会如此不同。我认为问题出在g = pictureBox1-&gt;CreateGraphics();,但我只是在做一些假设。任何形式的帮助都将不胜感激,因为自上周以来我一直坚持这一点!
非常感谢!
埃米利亚诺

【问题讨论】:

  • 您确定是Do_Plot() 减慢了进程,而不是循环中涉及的数学吗?如果您想要真正的快速绘图,请使用普通 GDI 而不是 GDI+。
  • 感谢您的回复!我不认为数学是问题。如果不调用该函数,则每个循环持续约 1 毫秒。

标签: winforms performance c++-cli picturebox


【解决方案1】:

我将为您提供 c/c++ 中的代码。然后将找到如何在 c++/cli 中执行此操作

首先初始化我们将要使用的对象(button_click开始):

RECT rt = {0, 0, 1200, 600};
HBITMAP hBitmap = NULL;
HPEN hLinePen = NULL;
HDC hdcPctrBox = NULL, hdc = NULL, hdcMemTempImage = NULL;
HBRUSH hBrush = NULL;
POINT arrayPnt[1000]; //array of points

hdc = CreateIC(TEXT("DISPLAY"), NULL, NULL, NULL);
hdcPctrBox = GetWindowDC(hwndPctrBox); //hwndPctrBox is the handle of picturebox
hdcMemTempImage = CreateCompatibleDC(hdc);

hBitmap = CreateCompatibleBitmap(hdc, 1200, 600);  
SelectObject(hdcMemTempImage, hBitmap);  

hLinePen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0)); //width of pen = 1,and color (255, 0, 0)
SelectObject(hdcMemTempImage, hLinePen);

hBrush = CreateSolidBrush(RGB(100, 100, 255)); //color of picturebox

然后:

for ( ; ; ) {
    /*Start*/
    clock_t Start = clock();

    FillRect(hdcMemTempImage, &rt, hBrush); //clear the hdcMemTempImage with the picturebox color
    //your code continues...

for(int i = 1; i&lt;=1000; i++) 中一次添加一个点:

arrayPnt[i - 1].x = X; //calculate X
arrayPnt[i - 1].x = Y; ////calculate Y

最后我们在for循环之后和clock_t Stop之前绘制它(我们不需要Do_Plot()):

Polyline(hdcMemTempImage, &arrayPnt, 1000);
BitBlt(hdcPctrBox, 0, 0, 1200, 600, hdcMemTempImage, 0, 0, SRCCOPY);

clock_t Stop = clock();
//your code continues...

编辑:使用您的原始代码(您不需要 rt、hBitmap、hBrush、hdcMemTempImage、arrayPnt 和hdc),而不是调用Do_Plot() 添加此代码:

//create two pens, one with the color you want hLinePen, and another with the color of picturebox hLinePenErase
//inside for(int i = 1; i<=1000; i++)
(some code...)
if(clipSup==true) {
    Gauss[i] = 1.99f;
    Rumore[i] = 0;
}

SelectObject(hdcPctrBox, hLinePen); //the pen to draw the line
MoveToEx(hdcPctrBox, xStart, yStart, NULL); //start point of line
LineTo(hdcPctrBox, xEnd, yEnd); //end point of line

SelectObject(hdcPctrBox, hLinePenErase); //the pen to erase the line
MoveToEx(hdcPctrBox, xStartErase, yStartErase, NULL); //start point of line to erase
LineTo(hdcPctrBox, xEndErase, yEndErase); //end point of line to erase
(code continues...)

要删除您的资源:

HPEN hLinePen = NULL, hLinePenErase = NULL, originalPen = NULL;
HDC hdcPctrBox = NULL;

hdcPctrBox = GetWindowDC(hwndPctrBox); //hwndPctrBox is the handle of picturebox
originalPen = SelectObject(hdcPctrBox, GetStockObject(DC_PEN));
//create the two pens

//in the end release resources
SelectObject(hdcPctrBox, originalPen);
DeleteObject(hLinePen);
DeleteObject(hLinePenErase);
ReleaseDC(hwndPctrBox, hdcPctrBox);

变态

【讨论】:

  • GDI和visual c++有很好的链接click here
  • 这正是我的解决方案所做的......它创建了一个双缓冲区并通过 BitBlt 绘制......
  • 您好,非常感谢!今天早上我会开始研究这个链接,我会试试你的代码!
  • 只是想知道:是否可以在 clr 项目中使用这种编码,或者我应该使用 MFC 应用程序?因为我尝试过包括windef.hwindows.h 并且编译器不断给我错误。
  • @Emi987 使用纯 C++。我可以帮助你的 GUI 工作人员。
【解决方案2】:

我建议实现你自己的绘画类。从“Panel”或“UserControl”派生您自己的类,并在构造函数中执行以下操作,从而激活双缓冲和优化绘图:

public ref class MyPaintControl : public System::Windows::Forms::UserControl
{
public:
    MyPaintControl()
    {
        this->SetStyle(System::Windows::Forms::ControlStyles::AllPaintingInWmPaint, true);
        this->SetStyle(System::Windows::Forms::ControlStyles::DoubleBuffer, true);
        this->SetStyle(System::Windows::Forms::ControlStyles::ResizeRedraw, true);
        this->SetStyle(System::Windows::Forms::ControlStyles::UserPaint, true);
    }

 // And then implement your painting in OnPaint:

protected: 
    virtual void OnPaint(PaintEventArgs^ e)
    {
      // Do painting...
      System::Drawing::Brush^ br = gcnew System::Drawing::SolidBrush(this->BackColor);
      e->Graphics->FillRectangle(br, this->ClientRectangle);
      delete br;

     // Do other paintings...
    }

    // This function might be called if new data arrived...
    public:
      void UpdateArea(int daten)  // pass data here, if you want...
      {
        // Store the data in a member variable...

        // force a redraw:
        this->InvalidateRect(); // this will force a "OnPaint"
      }
}

然后将此控件嵌入到您的 UI 中。这应该会大大提高你的绘画水平!

即使您移动或调整窗口大小,这也有效。请注意,绘画应该始终在“OnPaint”中完成!

【讨论】:

  • 感谢您的回复!这个看起来很有趣,我一定会试一试!我想问你是否可以用这个进行实时绘图!
  • @Emi987:是的,您可以制作一个实时绘图系统...顺便说一句:要强制重绘,您需要使矩形无效(InvalidateRect)。同样对于非常快速的系统,您可能需要一些“dirwct”绘图......这并不经常需要......更简单的方法是使用“OnPaint”......
  • 我真的很感谢你的帮助 Jochen。老实说,我不清楚如何将我的代码转换成这个,但它似乎是正确的方法。如果我在利用你,我很抱歉,但是你还有其他建议可以让我的代码以这种方式工作吗? :) 再次感谢!
  • 1.从“UserControl”创建派生类 / 2. 将此控件添加到“主控件/窗口”/ 3. 将属性“MainWindow/Controls”添加到指向“MainWIdow/Control”数据的新 UserControl => 然后您可以访问所有数据并可以绘制您的内容...您也可以调用 UserControl 中的任何函数来更新数据/强制重绘。你还需要什么?
  • 我不知道如何创建派生类。我正在谷歌上搜索如何做!
【解决方案3】:

我想更新我的编码状态。
我使用了 Jochen 发布的方法。它非常快,但不适合我的应用程序,或者我无法适应它。我需要做的是不断更新我的情节。在时间 t0,我应该画一条线。在时间 t1,当我存储新行时,我应该绘制新行并将其添加到前一行。情节必须是“活的”。我试图派生一个面板类并覆盖OnPaintevent,但只有在存储和显示所有行时我的绘图才会刷新。

【讨论】:

  • 所以你想一次画一条线?每画一条线的时间间隔是多少?图片框的大小是多少?
  • 是的,没错。例如,我不想同时绘制 1000 条线。我的 pictureBox 的大小为 (1200, 600),但在我的代码中,我设法使用一些缩放来使用 (1000, 500) 的区域。每次我的for 循环评估一个新值时,我都想更新我的情节。我不想等待所有值都被评估。如果你运行我的代码,你会看到它按预期工作。就是有点慢。
  • 我无法测试您的代码,因为我没有使用 c++ 或 c++-cli,仅使用普通 c。您的任务很简单,可以很快完成。对我来说,使用 gdi+ 绘制 1200x600 图像大约需要 40 帧/秒,而使用 gdi 大约需要 1000 帧/秒。所以gdi是没有道理的。我会看到一个解决方案。
  • 为了让您的图表“活着”,您必须将所有图表向左移动 1px 并绘制到最后。新点的 x 坐标必须始终相同,例如 1050。您正在做的是以循环绘制,例如从 50 到 1050 并重复。
  • 感谢您的努力!不幸的是,我不清楚如何将图表向左移动并评估新点。你能否给我更多关于如何修改我的代码和运行你的解决方案的细节?我迫不及待想试试!
猜你喜欢
  • 2011-05-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-06
  • 2010-10-04
  • 2012-07-01
  • 2021-07-02
  • 1970-01-01
相关资源
最近更新 更多