繁体   English   中英

GDI +的计时器问题

[英]Timer problem with GDI+

我目前在GDI和计时器方面遇到了一个非常奇怪的问题。

首先是代码:

class Graph : UserControl {
  private System.Threading.Timer timer;
  private int refreshRate = 25;              //Hz (redrawings per second)
  private float offsetX = 0;                 //X offset for moving graph

  public Graph() {
    timer = new System.Threading.Timer(timerTick);
  }

  private void timerTick(object data) {
    offsetX -= 1 / refreshRate;
    this.Invalidate();
  }

  public void timerStart() {
    timer.Change(0, 1000 / refreshRate);
  }

  private void onPaint(object sender, PaintEventArgs e) {
    //350 lines of code for drawing the graph
    //Here the offsetX will be used to move the graph
  }
}

我在这里尝试在特定时间将绘制的图形向左移动1个“图形单位”。 因此,我使用了一个计时器,该计时器将逐步改变偏移量,因此它将是一个平滑的移动(也就是refreshRate)。

在第一个视图中,此代码有效,但是后来我发现了以下问题:如果我使用的refreshRate为1(1Hz),它将以1步1(图形单位)向左移动图形。 如果我增加refreshRate,我的动作将变慢。 以20 FPS的速度缓慢运转,以200 FPS的速度缓慢运转。

所以这是我尝试的:

  1. 我使用刷新或更新而不是无效

  2. 我使用普通的线程(带有睡眠)而不是计时器

两次代码更改都没有改变结果。

除了使用计时器进行移动外,我还可以使用鼠标来移动图形,如果计时器正在运行,我仍然可以使用鼠标来平滑地移动图形。 因此,这不是性能问题。

我想到了绘画队列中的一个问题,因为刷新的速度比绘画完成的速度快吗? (但是为什么我不能用鼠标平稳移动图形呢?!)

所以我在这里需要一点帮助。 谢谢

以20 FPS的速度缓慢运转,以200 FPS的速度缓慢运转。

这里有一个根本的问题。 要获得200fps的刷新率,您需要每5ms重新绘制一次。 这将永远不会发生。 无论您将计时器的间隔设置为什么,分辨率都限制为大约10-15ms。 因此,您可能的最佳帧速率约为66-100fps,这是假定您的绘图代码花费的时间为零,而这当然不是。

最重要的是,你使用的是System.Threading.Timer 执行它的UI线程上的回调,所以调用Invalidate()从那里甚至可能是不安全的。 您通常不将System.Threading.Timer用于UI代码。

您可能需要尝试使用此处所示的所谓的Multi-Media Timer ,它使用CPU的高性能计时器并提供大约1ms的分辨率。

您可以尝试稍微改变问题。

由于您不知道绘画需要花费多长时间,因此您无法对此进行假设。

您所知道的是您希望过渡发生的时间,例如30秒,如下所示:

private MAX_TRANSITION_TIME = 30; //seconds

在这一点上,您还可以通过记录操作开始的时间来跟踪自操作开始以来经过的时间

private DateTime _startMoment;

现在,您可以做的是确保手头有正确的例程,并根据startMoment和now之间的差值计算位置

var elapsedTime = DateTime.Now.Subtract(_startMoment).Milliseconds;
var elapsedPercent = elapsedTime / MAX_TRANSITION_TIME * 1000.0 ;

从现在开始,您可以根据您的已用时间百分比进行绘制。

因此,在完成OnPaint之后,您应该可以刷新它。 如果您使用一个UI计时器,则可以执行以下操作:

 private void onPaint(object sender, PaintEventArgs e) {
       Timer1.Enabled = false;

     //350 lines of (adjusted)code go here

     If (ElapsedPercent<1)
     {
         Timer1.Enabled=True;
     }
     else
     {
         // you may need to perform the last draw setting the ElapsedPercent 
         // to 1. This is because it is very unlikely that your 
         // last call will happen at the very last millisecond
     }
 }

其中Timer1必须是Timer控件。

在间隔事件中,您只需编写

 _startMoment = DateTime.Now();
 this.Invalidate();

希望这可以帮助

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM