在 MFC 应用中绘制图形C++

Drawing a Graph in C++ MFC App

本文关键字:图形 C++ 绘制 MFC 应用      更新时间:2023-10-16

我正在编写一个C++MFC应用程序来控制制造环境中的机器。 这个应用程序还需要在非常短的周期内分析大量信息。

出于测试目的和长期维护,我需要能够绘制来自控制台上的传感器的数据。我可能完全忽略了一个选项(随意提出其他选项),但我的研究使我开始使用图片控件。

我通过使用OnPaint()成功地绘制了此控件。我的问题是我需要每隔几秒钟重新绘制一个新图像,并且我不能重复调用OnPaint()或向其传递数据。

如何创建可用于在图片控件上重复绘制的新函数?此外,这是我第一次尝试 MFC 应用程序,因此请在适当的层面上进行解释。谢谢!

class CPicture : public CStatic
{
    DECLARE_MESSAGE_MAP()
public:
    afx_msg void OnPaint();
};

BEGIN_MESSAGE_MAP(CPicture, CStatic)
    ON_WM_PAINT()
END_MESSAGE_MAP()

void CPicture::OnPaint()
{
    CPaintDC dc(this); // device context for painting
    dc.SelectStockObject(BLACK_BRUSH);
    dc.Rectangle(5, 50, 1000, 51);
}

我想问题是如何以及在哪里访问它

//Picture
class CPicture : public CStatic
{
    DECLARE_MESSAGE_MAP()
public:
    afx_msg void OnPaint();
    vector<Coordinates> GraphData;
};
void CPicture::OnPaint()
{
    // device context for painting
    CPaintDC dc(this); 
    // save current brush
    CBrush *pOldBrush = (CBrush*)dc.SelectStockObject(BLACK_BRUSH);
    int NumPoints = GraphData.size() - 1;
    for (int N = 0; N <= NumPoints; N++) {
        dc.Rectangle(GraphData[N].x, GraphData[N].y, GraphData[N].x, GraphData[N].y);
    }
    // select original brush into device contect
    dc.SelectObject(pOldBrush);
}

可以在新数据到达时调用控件上的Invalidate(),或使用RedrawWindow()强制立即重绘:

CPicture myPicture;
myPicture.Invalidate();

myPicture.RedrawWindow();

我不能重复调用OnPaint()或将数据传递给它。

要传递数据,可以在CPicture类(或程序中的其他位置)中声明包含数据的结构,然后可以从OnPaint()中访问该数据:

struct myData {
    int value1;
    int value2; // or an array, or some other data structure
}
class CPicture : public CStatic
{
    DECLARE_MESSAGE_MAP()
public:
    myData m_data;
    afx_msg void OnPaint();
};

OnPaint()中(还应选择原始画笔返回到设备上下文以避免资源泄漏):

void CPicture::OnPaint()
{
    CPaintDC dc(this); // device context for painting
    // save current brush
    CBrush *pOldBrush = (CBrush*)dc.SelectStockObject(BLACK_BRUSH);
    // check pOldBrush - could be NULL
    // dc.Rectangle(5, 50, 1000, 51);
    // access m_data here, for example
    dc.Rectangle(m_data.value1, m_data.value2, 1000, 51);
    // select original brush into device contect
    dc.SelectObject(pOldBrush);
}

更新(使用线程):

假设以下内容(来自评论):

  • 对于主线程,您有一个对话框CLongbowDlg

  • 对于图形,您有一个派生自 CStaticPicControl,并且该控件放置在对话框中。

  • 从主线程,启动工作线程来读取数据。

PicControl 和 CLongbowDlg 在同一个标头中定义,但 彼此独立。我需要能够调用 Invalidate() 或 从 CLongbowDlg 的函数内部重绘窗口(),因为它们 表示主线程。

我将尝试在这里简要描述其中一种可能性,因为这实际上应该是一个单独的问题。

首先,PicControl的对象必须是CLongbowDlg的成员,我假设是这种情况(我们称之为m_PicControl) - 所以,在类CLongbowDlg

PicControl m_PicControl;

对于数据(我将使用上面的myData作为示例数据):在您的主线程(对话框)中,创建一个类型为 myData 的变量:m_data(对于较大的数据,您可以在堆上分配空间,或使用 CArray 或其他容器):

myData m_data;

PicControl创建一个类型为 myData* 的成员变量,并在 PicControl 构造函数中将其设置为 NULL。

myData *m_pData;

OnInitDialog()(主对话框)中,为m_picControl提供指向数据的指针(或者最好创建一个函数来在 PicControl 中执行此操作):

m_picControl.m_pData = &m_data;

启动工作线程时,还要为其提供指向m_data的指针和/或指向对话框本身的指针 (this )。

确保使用关键部分保护数据。

当数据传入时,工作线程可以通过提供的指针写入数据。

PicControl::OnPaint()中,相同的数据可以通过m_pData访问。

要启动重绘,有几种方法:

  • PicControl或主对话框中使用计时器,并在每次计时器触发时调用Invalidate()

  • 要控制从工作线程重绘(例如,当一定数量的新数据到达时),可以使用PostMessage()将消息发布到主对话框(使用启动线程时提供的指针 - this指针)。

    要接收消息,您必须在主对话框中创建一个消息处理程序,然后从那里调用Invalidate() m_picControl(您也可以直接将消息发布到 PicControl,但我更喜欢通过主窗口执行此操作)。