简体   繁体   English

CChildView::OnPaint() 绘制多个数据点时刷新速度变慢

[英]CChildView::OnPaint() Refresh Slowing When Painting Many Data Points

I am writing an MFC Windows App to plot the motion of a wheeled robot.我正在编写一个 MFC Windows 应用程序来绘制轮式机器人的运动。 In CChildView::OnPaint() I do a pdc->FillRect(ViewRect, &CBrush( RGB(128,128,128) ) );CChildView::OnPaint()我做了一个pdc->FillRect(ViewRect, &CBrush( RGB(128,128,128) ) );

Then I use pdc->MoveTo and pdc->LineTo to draw all the data points etc. refreshed every 250ms.然后我使用pdc->MoveTopdc->LineTo绘制所有数据点等,每 250 毫秒刷新一次。

Problem is as the robot progresses on its travels the number of data points increases to a level where the OnPaint() function refresh time becomes noticeable.问题是随着机器人在其行程中的进展,数据点的数量增加到OnPaint()函数刷新时间变得明显的水平。

In a previous DOS app I did years ago I got around this sluggish refresh rate by removing the fill background paint and replaced it with a plot all the data points in the background colour, then painting them all again in there foreground item colour.在我多年前做的一个以前的 DOS 应用程序中,我通过删除填充背景涂料并将其替换为绘制背景颜色的所有数据点,然后再次将它们全部涂在前景项目颜色中,从而解决了这种缓慢的刷新率问题。 Thus reducing the number of changed pixels.从而减少改变像素的数量。

Would this method be valid for what I am doing in MFC OnPaint() or is there a better solution please?这种方法对我在 MFC OnPaint()中所做的事情是否有效,或者有更好的解决方案吗?

Painting in a DOS applications is simple, because you have full control of the process and you don't get paint requests from the system - normally you have to introduce delays if you want to achieve a "motion" effect.在 DOS 应用程序中绘画很简单,因为您可以完全控制该过程,并且您不会从系统获得绘画请求 - 如果您想获得“运动”效果,通常您必须引入延迟。 In Windows though painting is very different, because you may (or may not) receive paint requests from the system, if the user minimizes/maximizes/restores, moves, unhides etc or programmatically invalidates a window.在 Windows 中,虽然绘画非常不同,因为如果用户最小化/最大化/恢复、移动、取消隐藏等或以编程方式使窗口无效,您可能(或可能不会)从系统收到绘画请求。 This causes an "invalid" area on the window surface.这会导致窗口表面出现“无效”区域。 If the window contains an invalid area it will receive a WM_PAINT message.如果窗口包含无效区域,它将收到WM_PAINT消息。 It is a low-priority message, posted just before the application is about to enter the idle state, also it is in some way "asynchronous", posted when the system decides so.它是一个低优先级的消息,在应用程序即将进入空闲状态之前发布,它在某种程度上也是“异步的”,在系统决定如此时发布。

First you need to fully understand the invalidating/painting mechanism in Windows.首先,您需要充分了解 Windows 中的失效/绘制机制。 The documentation you must read is: Painting and Drawing .您必须阅读的文档是: 绘画和绘图 I have posted quite a bit about this, please check my answer here (and the links in the post).我已经发布了很多关于此的内容,请在此处查看我的答案(以及帖子中的链接)。

As for your question, I would suggest using a combination of painting and drawing.至于你的问题,我建议使用绘画和素描相结合的方式。 Many suggest not painting outside the WM_PAINT message ( OnPaint() / OnDraw() in MFC), but this is not suitable for all cases, as the message is sent asynchronously, and it's also very hard to implement a really optimized operation, painting only the affected area;许多人建议不要在WM_PAINT消息(MFC 中的OnPaint() / OnDraw() )之外绘画,但这并不适合所有情况,因为消息是异步发送的,而且也很难实现真正优化的操作,仅绘画受影响的地区; you would actually have to paint the invalidated area as well.您实际上还必须绘制无效区域。 It is best used to paint the whole client area of the window.最好用于绘制窗口的整个客户区。 You could definitely use it in your app (and it's logically "correct"), but as you have found it's really "wasteful", and has a sluggish and choppy feel.您绝对可以在您的应用程序中使用它(并且它在逻辑上是“正确的”),但正如您发现的那样,它真的很“浪费”,并且有一种迟钝和断断续续的感觉。 So my proposal is:所以我的建议是:

  • Use Painting to paint the entire client area, in response to WM_PAINT messages.使用 Painting 绘制整个客户区,以响应 WM_PAINT 消息。 Better use OnDraw() , rather than OnPaint() for a CView/CScrollView -derived class.对于CView/CScrollView派生类,最好使用OnDraw()而不是OnPaint() It should paint the background, if any, all additoinal objects (labels, axes etc) and finally all data points.它应该绘制背景(如果有的话)所有附加对象(标签、轴等),最后是所有数据点。
  • Use Drawing ( GetDC() / ReleaseDC() ) to paint only the section that changes, ie a single data point at a time.使用绘图 ( GetDC() / ReleaseDC() ) 仅绘制更改的部分,即一次一个数据点。 You will have to create a new worker thread (so as to not block the main/UI thread).您将必须创建一个新的工作线程(以免阻塞主/UI 线程)。 It must do the calculations, add the new data point to some list-of-data-points (also used by the UI thread in OnDraw() to paint the whole window) and draw the new data point only.它必须进行计算,将新数据点添加到一些数据点列表(也由OnDraw()中的 UI 线程用于绘制整个窗口)并仅绘制新数据点。 You should use some sort of synchronization (eg Critical Section) to access the data points list, because it is used and modified by more than one threads.您应该使用某种同步(例如关键部分)来访问数据点列表,因为它被多个线程使用和修改。 As all paint operations should best be performed in the context of the UI thread, you can define a custom message and have the worker thread sending this message ( SendMessage() ) to the window.由于所有绘制操作最好在 UI 线程的上下文中执行,因此您可以定义自定义消息并让工作线程将此消息 ( SendMessage() ) 发送到窗口。 The routine handling the message should do the actual drawing, ie paint the new data point.处理消息的例程应该进行实际绘图,即绘制新数据点。

If implemented correctly this should result in a smooth operation, allowing the window to be moved or resized while the paint operation is in progress, without blocking the window or the application.如果正确实施,这应该会导致平稳的操作,允许在绘制操作进行时移动窗口或调整窗口大小,而不会阻塞窗口或应用程序。

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

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