MFC里多文档多视图+多线程动态计算、绘制曲线

对于初学MFC多线程的,我建议看一看《Windows环境下的多线程编程原理与应用--王险峰》一书,里面代码实例较多,适合初学者理解MFC里的工作、界面多线程及线程间通信。

对于MFC里多线程的原理,不做过多介绍,可以看书获得。下面是我在MFC里,使用MDI框架,多文档/多视图(View)里同步绘制动态数据曲线的实现。

对应MFC里多文档/多视图的学校,推荐看侯捷的《深入浅出MFC》。

(本实例主要完成以下工作:

(1)、利用VS2010创建MDI工程;

(2)、在主.cpp文件中注册多个文档模板

(3)、重新OnFileNew()函数,使得重新执行或New时显示自己要求的View视图窗口;

(4)、利用MFC的界面多线程CWinThread类进行界面多线程的建立;

(5)、利用全局数组的形式在和类之间传递数据;

该工程是基于VS2010的,完整源代码可在我得Github里下载:https://github.com/LuoKuanH/MultiThread

1、  利用VS2010创建MDI工程项目

MFC里多文档多视图+多线程动态计算、绘制曲线_第1张图片

2、在主cpp里注册多文档模板

// Draw.h : Draw 应用程序的主头文件
//
#pragma once

#ifndef __AFXWIN_H__
	#error "在包含此文件之前包含“stdafx.h”以生成 PCH 文件"
#endif

#include "resource.h"       // 主符号


// CDrawApp:
// 有关此类的实现,请参阅 Draw.cpp
//

class CDrawApp : public CWinAppEx
{
public:
	CDrawApp();

	CMultiDocTemplate* m_pTemplateTime;//创建文档模板1,用于绘制时域图
	CMultiDocTemplate* m_pTemplateDFT;//创建文档模板2,用于绘制实时DFT图

// 重写
public:
	virtual BOOL InitInstance();
	virtual int ExitInstance();

// 实现
	UINT  m_nAppLook;
	BOOL  m_bHiColorIcons;

	virtual void PreLoadState();
	virtual void LoadCustomState();
	virtual void SaveCustomState();

	afx_msg void OnAppAbout();
	afx_msg void OnFileNew();
	DECLARE_MESSAGE_MAP()
	afx_msg void OnDraw();
};

extern CDrawApp theApp;
BOOL CDrawApp::InitInstance()
{
......
        // 注册应用程序的文档模板。文档模板
	// 将用作文档、框架窗口和视图之间的连接
	//CMultiDocTemplate* pDocTemplate;
	m_pTemplateTime = new CMultiDocTemplate(IDR_DrawTYPE,   //注册时域视图模板
		RUNTIME_CLASS(CDrawDoc),
		RUNTIME_CLASS(CChildFrame), 
		RUNTIME_CLASS(CMainView));
	if (!m_pTemplateTime)
		return FALSE;
	AddDocTemplate(m_pTemplateTime);

	m_pTemplateDFT = new CMultiDocTemplate(IDR_DrawTYPE,   //注册频域视图域模板
		RUNTIME_CLASS(CMainDoc),
		RUNTIME_CLASS(CChildFrame), 
		RUNTIME_CLASS(CDFTView));
	if (!m_pTemplateDFT)
		return FALSE;
	AddDocTemplate(m_pTemplateDFT);
......
}

然后在主cpp里重写OnFileNew()函数,使得新建文件时,同时打开两个用于绘图的View视图

MFC里多文档多视图+多线程动态计算、绘制曲线_第2张图片

ON_COMMAND(ID_FILE_NEW, OnFileNew)

void CDrawApp::OnFileNew()
{
	//CDrawApp *pMyApp = (CDrawApp * )AfxGetApp();
	//CMultiDocTemplate* m_pTemplateTime = pMyApp->m_pTemplateTime;
	m_pTemplateTime->OpenDocumentFile(NULL);
	m_pTemplateDFT->OpenDocumentFile(NULL);
}
此时当开始执行程序,或者新建时,灰同时显示两个View视图:

3、在菜单栏添加菜单命令:

Caption ID 设置 Prompt
更新数据 ID_REDATA 默认 两个View同时绘图
在Drawdoc里添加该菜单命令的响应函数:

DrawDoc.cpp

BEGIN_MESSAGE_MAP(CDrawDoc, CDocument)
	ON_COMMAND(ID_REDATA, &CDrawDoc::OnRedata)
END_MESSAGE_MAP()
void CDrawDoc::OnRedata()
{
	// TODO: 在此添加命令处理程序代码
	pDFTThread = AfxBeginThread(RUNTIME_CLASS(CMainThread));
	pDrawThread = AfxBeginThread(RUNTIME_CLASS(CViewThread));
	pThread = AfxBeginThread(Calculate,this);

UINT Calculate(LPVOID pParam)
{
	CDrawDoc *pdoc = (CDrawDoc*)pParam;
	CViewThread *pdrawThrd = (CViewThread*)(pdoc->pDrawThread);
	CMainThread *pUIThrdd = (CMainThread*)(pdoc->pDFTThread);
	//CViewThread *pdlgThrd = (CViewThread*)(pdoc->pPROSThread);
	for (int i =0; i<100; i++)
	{
		datax[i] = i;
		datay[i] = i*i/2;
		data = i;
		pdrawThrd->PostThreadMessage(WM_SHOW_VI,NULL,NULL);
		//pdlgThrd->PostThreadMessage(WM_PROS,NULL,NULL);
		pUIThrdd->PostThreadMessage(WM_SHOW_DFT,NULL,NULL);
		Sleep(10);
	}
	return 0;
}

响应该菜单命令的函数主要是创建两个界面多线程和一个工作线程,其中Calculate()函数是工作线程,用于实时计算数据,然后利用两个界面线程,实现显示计算出来的数据。

4、由MFC的界面多线程类CWinThread派生class CMainThread : public CWinThread,线程间通信采用消息传递法方式。

线程间通信采用消息传递法,在两个派生的界面线程中,添加对应的消息响应函数

CMainThread

#define WM_SHOW_DFT WM_USER + 3
...
afx_msg void ShowDFT(WPARAM wParam,LPARAM lParam);
...
BEGIN_MESSAGE_MAP(CMainThread, CWinThread)
	ON_THREAD_MESSAGE(WM_SHOW_DFT,ShowDFT)
END_MESSAGE_MAP()
...
// CMainThread 消息处理程序
void CMainThread::ShowDFT(WPARAM wParam,LPARAM lParam)
{
	POSITION curTemplatePos = theApp.GetFirstDocTemplatePosition();
	CDocTemplate *m_doc=theApp.GetNextDocTemplate(curTemplatePos);
	CDocTemplate *m_doc1=theApp.GetNextDocTemplate(curTemplatePos);//文档模板
	//获得文档:
	

	curTemplatePos=m_doc1->GetFirstDocPosition();
	CMainDoc *m_pdoc=(CMainDoc*)m_doc1->GetNextDoc(curTemplatePos);
	//获得视图:
	curTemplatePos=m_pdoc->GetFirstViewPosition();
	CDFTView *m_dview;
	while(curTemplatePos !=NULL)
	{
		m_dview = (CDFTView*)m_pdoc->GetNextView(curTemplatePos);
		if (m_dview->IsKindOf(RUNTIME_CLASS(CDFTView)))
		{
			m_dview->drawline();//调用相应视图的绘图函数进行曲线绘制
		}
		else
		{
		}
	}
}
上述的ShowDFT()函数里,主要功能是遍历程序的文档模板及文档视图,遍历找到需要的View类,并利用对应的View类调用该View对应的drawline()函数进行曲线绘制。

线程间的数据传递采用全局变量的方式:

定义两个全局数组int datax[100];int datay[100];用于保存计算的数据,同时用于数据传递至界面线程里面进行曲线绘制(本实例较简单,通过Calculate()函数实现更新数据,然后两个界面线程利用此数据进行简单的直线绘制);

本例中,两个界面线程分别使用MainView和DFTView进行曲线绘制,因此,在这两个View中,分别加入函数drawline();

MainView.cppj及DFTview.cpp

// CMainView
extern int datax[100];
extern int datay[100];
void CMainView::drawline()
{
	CClientDC dc (this);
	CPen pen1(PS_SOLID,1,RGB(0,255,0)); 建立一个画笔类对象,构造时设置画笔属性
	dc.SelectObject(&pen1);
	for (int i=0; i<100; i++)
	{
		dc.MoveTo(datax[i],150);
		dc.LineTo(datay[i],150);
		//Sleep(10);
	}
}

5、结果显示

当我们把鼠标单击上面一个视图后,点击菜单栏的更新数据命令,便可以看到两个View里的直线同步绘制,从左至右。

注意:由于本实例代码中使用了两个文档注册文档模板,而菜单命令只在DrawDoc文档里进行的响应,因此,我们绘图是需要将焦点定位于与DrawDoc关联的View(及上面一个View里,才能响应我们的“更新数据”菜单命令);
MFC里多文档多视图+多线程动态计算、绘制曲线_第3张图片

总结:这只是我自己实现的一个例子,可能有很多问题,希望指正。刚开始学习写博客,感觉好多话说不清楚。该实例的完整源码可在我的GitHub里下载,链接在上方。

对应MFC实现多文档多视图,首先需要在主cpp里注册多个文档模板,然后如果想要程序开始时或新建时显示特定View,需要重新主cpp里的OnFileOpen()和OnFileNew()函数。对应变量的传递,我苦于只想到使用全局变量的方式,应该还有更好的方式。



你可能感兴趣的:(MFC)