GDI Win32 绘图图

Posted

技术标签:

【中文标题】GDI Win32 绘图图【英文标题】:GDI Win32 Plotting Graphs 【发布时间】:2021-08-03 21:00:05 【问题描述】:

在我的小而简陋的 c++ Win32 GUI 应用程序中,我已经能够绘制网格了:

这是为显示光谱数据做准备,即从我的设备返回的 36 个(浮动)测量点。我仍然需要标记轴...这是我到目前为止绘制网格的代码:

case WM_PAINT:

    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hWnd, &ps);    

    FillRect(hdc, &ps.rcPaint, HBRUSH (COLOR_WINDOW + 1));  

    bool Retour;
    
    pen = (HPEN)GetStockObject(TRANSPARENT);
    SelectObject(hdc, pen);

    Brosse = CreateSolidBrush(RGB(240, 240, 240));
    SelectObject(hdc, Brosse);

    Retour = LineTo(hdc, 100, 500);*/

    Retour = Rectangle(hdc, 350, 150, 750, 375);

    for (int i = 380; i <= 730; i=i+10)    // 36 vertical lines
        MoveToEx(hdc, i, 175, NULL);
        LineTo(hdc, i, 350);
    
    for (int i = 175; i <= 350; i = i + 17) 
        MoveToEx(hdc, 375, i, NULL);
        LineTo(hdc, 730, i);
    

    DeleteObject(pen);
    DeleteObject(Brosse);

    EndPaint(hWnd, &ps);

如您所见,我在 WM_PAINT 过程中找到了绘制网格的代码。将代码放在那里以试验 GDI 指令是有意义的,但也因为网格需要在启动时作为界面的一部分可见。

我的问题是,当按下“测量样本”按钮后,我可以访问数据时,我应该如何“更新”图表?我收集到在网格上绘制的代码不一定需要位于 WM_PAINT 过程中,是吗?根据我有限的理解,只要我能得到一个设备上下文句柄(hDC),我应该可以去吧?我只需要绘制一条从左到右连接 36 个数据点的“线”。不确定我应该为此目的使用简单的 MOVE_TO 和 LINE_TO 吗?我认为有一种方法可以绘制一条穿过所有 36 个数据点的“平滑”线?

最后一件事,如果可以的话……我将以一种非常基本的方式进行讨论,因为我对使用“图形库”和对象的想法感到害怕。但我怀疑他们会让我的生活更轻松,同时提供许多选项,我会花很长时间才能弄清楚,我会打赌并实施?

非常感谢您的热心帮助和耐心等待。

【问题讨论】:

SelectObject 转移资源的所有权。选择到设备上下文中的资源归 DC 所有,之前选择到 DC 中的对象现在由您负责。这样一来,在对象仍被选入 DC 时删除它总是错误的。这里最常见的模式是:1 选择对象进入 DC,2 使用对象,3 选择对象退出(通过选择前一个对象进入)DC,4 清理您的资源。还要确保阅读WM_PAINT message。 与此问题完全无关,您可能想查看Enabling Visual Styles。它不涉及编写任何代码,但会使您的下一个屏幕截图看起来明显不同。毫无疑问更好。 【参考方案1】:

您应该始终在WM_PAINT 处理程序中完成所有绘图。这样,例如,当覆盖窗口的东西被移除时,窗口将正确重绘。

当您的图表数据发生变化时,调用InvalidateRect 提示重绘。

要确定要重绘的内容,您需要将合适的变量传递给您的WM_PAINT 处理程序。从MoveToLineTo(或PolylinePolyPolyline)开始,然后首先让它工作。然后,如果您认为需要平滑算法,您可以研究它们。

【讨论】:

“平滑”是锦上添花。我保证我会坚持在 WM_PAINT 中进行绘画。最初,没有要绘制的数据,所以我必须使用某种“标志”来控制执行哪些代码:单独的空图或图形加上设备数据的“线”?所以,我想我需要从“测量”按钮“事件”中设置这个标志,这样,一旦我有了数据,我就会调用 InvalidateRect() 以使图形在相应地设置该标志的同时使用设备数据“更新” ?这是一个好方法吗? 听起来很合理,是的。 出于“开发”的目的,我决定使用“虚拟”数据。所以当应用程序启动时,它应该有一些数据可以“咀嚼”。我们会看看这会把我引向何方?感谢您的帮助。 你不需要一个标志来区分“数据可用”“没有数据可用”。只需将数据存储在容器中。大小为 0 的容器表示没有数据。【参考方案2】:

您应该将所有自定义绘画代码放在窗口的 WM_PAINT 处理程序中,是的。如果愿意,您可以调用单独的函数来绘制图形。将您从BeginPaint(...) 获得的 HDC 和您的光谱数据传递给它。但是,该函数需要从 WM_PAINT 处理程序中调用。

如果您在 WM_PAINT 中处理绘画,然后在数据更改时更新图形,您将在窗口上调用 InvalidateRect(...)InvalidateRect 本质上告诉 Windows “此窗口已更改,需要重新绘制”。它将驱动对绘制处理程序的调用。

您将遇到的一个问题是您的图表会闪烁。可能不太明显。它可能不会打扰您,但问题是在每次调用 WM_PAINT 处理程序时,您都在擦除屏幕设备上下文中的图形;这个擦除将是可见的。解决这个问题的方法是对图形进行“双缓冲”:创建一个离屏位图,在数据更改时绘制到该位图,然后在 WM_PAINT 中将离屏位图绘制到屏幕上。如果您需要多个图形或只是为代码添加一些模块化,您还可以将图形作为一个单独的子窗口来管理屏幕外位图等。

至于您是否会更轻松地使用图形库...可能,但更重要的是:使用比 Win32 API 更高级别的 GUI 库会更容易。如果您只关心 Windows,请考虑 .Net 框架 WinForms 或 WPF。否则,考虑 Qt。

【讨论】:

感谢您提供宝贵而详细的建议。我希望我可以使用 .NET,但我被一个 SDK DLL 卡住了,它使用过于复杂的调用让我将它们“移植”到 c# 或 vb。我尝试了 QT,并且希望 FAR 更喜欢在 QT 中工作但是我受到 Qlibrary 对象和我的 Resolve DLL 函数的限制,这些函数我只能在 MS C++ 中使用(使用 LoadLibrary)。我意识到当图表“无效”时我会看到“闪烁”。我的计划是让逻辑在“闪烁效果”下工作,稍后,如果它真的困扰我,我将研究“双缓冲”技术:-) 你能通过 .Net 中的“P/Invoking”调用 DLL 吗?即***.com/questions/14673049/… 我不明白为什么不这样做。但是,在“学习如何与设备有效交互”方面,我选择了 c++ 路线,因为设备制造商提供的示例应用程序是用 c++ 编写的。但由于我不太了解 c++,所以使用 P/Invoking 会很有意义,我同意 100%。 Windows Forms 是一个围绕原生 Windows 控件的托管(和疯狂的)包装器。它不是本机代码的替代策略。更不用说 WPF,它既发明了自己的 UI 堆栈,又只提供托管 API。这不是任何东西的替代策略,除非您的目标是完全锁定供应商。另一方面,WinUI 包含 Windows 10 的标准 UI 实现,用本机代码编写,带有本机 API。现在 that 是原生代码的真正替代策略。

以上是关于GDI Win32 绘图图的主要内容,如果未能解决你的问题,请参考以下文章

win32day07-图形绘制/GDI绘图对象-画笔/画刷

GDI+ 多线程绘图

MFC GDI绘图基础

使用 Direct2D 在非客户区绘图

14.Windows绘图

GDI+ 绘图基础-图形设备&创建Graphics对象