1、用 Visual C+开发监控界面的方法 作者: 陈晔 QQ: 1004595314 Visual C+开发工具提供了现成的窗口、控制与工具条的制作手段,大大简化了界面的开发过程,并且使得开发出的界面具有组态软件风格,使用起来方便、灵活、简单易学。 本文以一实例介绍如何实现三个独立的分离窗口:监视窗口,控制窗口和动画窗口,并以图 1 中的进水和温度值传递为例,介绍如何实现控制功能和不同窗口间的数据共享,并介绍实现无闪烁动画的方法。 将工作台分离成为三个窗口,动画窗口 用于模拟锅炉的进、出水、升温的画面显示,其中的画面与系统采集的数据相对应。控制窗口用于实现预设温度值,调节水位、控制加热、暂停等
2、功能。监视窗口用来实时跟踪采样的温度值,作出温度 -时间曲线。 一、创立分离窗口 要实现多窗口显示,必须使用 CSplitterWnd 类,将窗口分成三个子窗口,然后将各个功能类与窗口联系起来。在创建应用程序时,在第一步中选择 Single Document Interface, 并选用中文字库,在第 4 步中按下 Advanced,选择 Use Split Window 选项。设定应用程序名为 Animation。目前我们只有一个视类 CAnimationView,它将与动画窗口对应,此外我们还要生成具有对话功能的监视窗口(对应 CShowView 类 )和控制窗口(对应 CControlV
3、iew 类 )。在 Resource View 中调出上下文菜单并选择 Insert,选择属性为 IDD_FORMVIEW,创建监视对话框 IDD_SHOWVIEW 和控制对话框 IDD_CONTROLVIEW,并单击鼠标右键在 Properties 选择项中选择中文字库。然后编辑 IDD_CONTROLVIEW:利 用 Visual C+提供的控件生成器生成 ID名为 IDC_SETTEMPERATURE 的文字编辑域,并生成 Caption 为“设置温度初始值”。再利用button 生成器,生成控件 IDC_WATERIN, IDC_CONFIRM, Caption 分别为“进水”和“确认
4、”。再利用 ClassWizard,创建基于 CFormView类的新类,分别为 CShowView和 CControlView类,并将它们与刚创建的两个对话框联系。 在 CMainFrame 类中,重载 OnCreateClient()函数创建三个静态 分离窗口,先在 MainFrame.h中声明所需变量: protected: CSplitterWnd m_wndSplitter2; 转入 MainFrame.cpp 程序,在开头处包含头文件“ ShowView.h”,向下找到函数 OnCreateClient(),添加如下代码,生成两个窗口,三个视图: BOOL CMainFrame:O
5、nCreateClient(LPCREATESTRUCT/*lpcs*/,CCreateContext* pContext) /先分裂成两个窗口,一行两列 if(!m_wndSplitter.CreateStatic(this,1,2) TRACE0(“Failed to CreateStaticSplittern“); return FALSE; / 加上动画窗口,将其放在左边 if(!m_wndSplitter.CreateView(0,0,pContext-m_pNewViewClass,CSize(425,50),pContext) TRACE0(“Failed to create f
6、irst panen“); return FALSE; /将第二个窗口再一分为二 if(!m_wndSplitter2.CreateStatic( return FALSE; /增加两个视图,并调整视图大小 if(!m_wndSplitter2.CreateView(0,0, RUNTIME_CLASS(CShowView),CSize(0,175),pContext) TRACE0(“Failed to create second panen“); return FALSE; if(!m_wndSplitter2.CreateView(1,0,RUNTIME_CLASS(CControlVi
7、ew),CSize(0,0),pContext) TRACE0(“Failed to create third panen“); return FALSE; return TRUE; 再转入 Animation.cpp 中,修改 InitInstance()函数,将其中的 m_pMainWnd-ShowWindow (SW_SHOW),改为 m_pMainWnd-ShowWindow(SW_SHOWMAXIMIZED);至此,我们可以生成图 1的界面框架。 二、动画显示窗口的实现 动画是通过调用一幅幅的图片来实现的,因此先将所需的画面载入资源 BITMAP 中,并按顺序编辑它们的 ID 号,然
8、后在定时器中,每隔一定的时间调用一次动画函数。第一步先生成定时器,用 ClassWizard 给 CAnimationView 添加消息处理程序: OnCreate()函数对应于消息WM_CREATE, OnTimer()函数对应于消息 WM_TIMER。编辑函数 OnCreate(),生成每隔 0.1 秒的时钟。 int CAnimationView:OnCreate(LPCREATESTRUCT lpCreateStruct) if(CView:OnCreate(lpCreateStruct)= = -1) return -1; SetTimer(2,100,NULL); /产生每隔 0.
9、1 秒的时钟 return 0; 在函数 OnTimer()中,调用动画服务函数 ServicedAnimation(),该函数根据系统情况作出无闪烁动画,并可以根据不同的功能,选择画面。 void CAnimationView:OnTimer(UINT nIDEvent) CClientDC ClientDC(this); ServicedAnimation( /调用动画服务函数 CView:OnTimer(nIDEvent); ServicedAnimation()用于检查系统的时钟,并能计算从发生最后一个动画事件开始计算起所经过的时间,然后这个函数检查本帧动画的延迟时间,并决定是否到达了
10、另一次更新的时间,若到了另一次更新的时间,那么 m_nCurrentFrame 变量就增加,此时若进水标 志成功,则调用 DrawWaterinAnimation(),开始新一帧动画画面。如果由于其它原因造成了到达ServicedAnimation()函数的延迟也计算在内。 先在 CAnimationView 中,定义变量: public: DWORD mLastEventServiced; BOOL m_bPause, m_bDone; /暂停、动画终点标志 int m_nCurrentFrame, m_nFrame; /当前的动画帧数,总动画帧数 构造函数中,添加: CAnimationV
11、iew:CAnimationView() mLastEventServiced=0; m_bPause=FALSE; m_bDone=FALSE; m_nCurrentFrame=0; m_nFrame=100; /图画的帧数设为 100 幅 编辑 ServicedAnimation()函数: void CAnimationView:ServicedAnimation(CDC* pDC) DWORD Elapsed=0L; DWORD FirstSample=:GetTickCount(); DWORD WorkValue=mLastEventServiced; CAnimationDoc *
12、pDoc=(CAnimationDoc*) GetDocument(); ASSERT(pDoc-IsKindOf(TUNTIME_CLASS(CAnimationDoc); if(m_bPause|m_bDone) return; /如果动画被暂停或结束,就返回 while(FirstSample!=WorkValue) WorkValue+; Elapsed+; /如果 Elapsed 达到其阈值点,并满足或超过这帧动画的延迟值,则可进入下一个代码 /信息块,这样,动画顺序将移动到下一帧: if(Elapsed=1L) /设置每帧动画 的延迟值都为 1 m_nCurrentFrame+;
13、if(m_nCurrentFrame=m_nFrame) m_nCurrentFrame=0; /将要开始时,应把帧记数置于零 if(!pDoc-m_bWaterInFlagDoc) m_nCurrentFrame=m_nFrame-1; m_bDone=TRUE; if(pDoc-m_bWaterInFlagDoc m_bWaterInFlagDoc=FALSE; mLastEventServicced=:GetTickCount(); / 把系统当前的时钟记录下来 DrawAnimationWaterIn()为显示不同的进水水位的画面的函数,要连续显示画面,应将一幅幅位图声明成一个静态数组
14、。 Static int bitmapswaterin= IDB_WATERIN1, IDB_WATERIN2 IDB_WATERIN100; 利用 ClassWizard 声明 CAnimationView 的变量: protected: CBitmap bmpWI100; CDC dcMemWI100; BITMAP bmWI; 在 CAnimationView 的构造函数里添加如下语句: for(int i=0;iBitBlt(recct.right-bmWIm_nCurrentFrame.bmWidth)/2, (rect.bottom-bmWIm_nCurrentFrame.bmHe
15、ight)/2,/位图放在视窗正中 bmWIm_nCurrentFrame.bmWidth,bmWIm_nCurrentFrame.bmHeight, 三、控制窗口功能的实现 利用 ClassWizard 中的 Member Variables 标签为 CControlView 增加成员变量。 控件 控件 ID 类型 成员 预设温度值 IDC_SETTEMPERATURE int m_nSetTemperature 在 CControlView 中增加消息处理函数: 对象 对象 ID 函数 消息 进水按键 IDC_WATERIN OnWaterIn() BN_CLICKED 确认按键 IDC_
16、CONFIRM OnComfirm() BN_CLICKED 下面以输入预设温度值和进水响应为例,来讲述如何实现控制功能。当控制视窗(CControlView 类)中预置温度设定之后,按下确认键即响应消息 OnConfirm(),在监视视窗( CShowView 类)中的状态监 测图中画一条预设温度的横线。当控制视窗( CControlView 类)中按下进水键,便在动画视图( CAnimationView 类)有进水动画产生。 这时控制视图要向监视视图、动画视图传送数据,但它们之间无法直接实现数据共享。 MFC类库中 CDocument 类及其派生类用来管理工作数据,它能够读写和存储视图所要
17、观察和处理的数据,并可以同时拥有多个视图。所以,此处用 CDocument 的派生类 CAnimationDoc 作为数据传输的中介,来实现不同视窗之间的数据传递。 m_nSetTemperature m_nSetTemperatureDoc 在 CAnimationDoc 中,设置公共变量: public: int m_nSetTemperatureDoc; /用来传递 m_nSetTemperature 的 Document 派生类变量 BOOL m_bSetTemperatureFlagDoc; /温度设置成功标志 BOOL m_bWaterInFlagDoc; /进水标志 在 CAni
18、mationDoc 的构造函数中初始 化变量: CAnimationDoc:CAnimationDoc() m_nSetTemperatureDoc=0; m_bSetTemperatureFlagDoc=FALSE; m_bWaterInFlagDoc=FALSE; 编辑 CControlView 的 OnConfirm()函数: void CControlView:OnConfirm() CAnimationDoc* pDoc=(CAnimationDoc*) GetDocument(); ASSERT(pDoc-IsKindOf(RUNTIME_CLASS(CAnimationDoc);
19、/找到当前的 /CDocument 类 UpdateData(); /更新视图数据 pDoc-m_nSetTemperatureDoc=m_nSetTemperature; pDoc-m_bSetTemperatureFlagDoc=TRUE; /预设温度 成功标志 编辑 CControlView 的 OnWaterIn()函数: void CControlView:OnWater() CAnimationDoc*pDoc=(CAnimationDoc*)GetDocument(); ASSERT(pDoc-IsKindOf(RUNTIME_CLASS(CAnimationDoc); pDoc
20、-m_bWaterInFlagDoc=TRUE; /进水响应成功 最后在 ControlView.cpp 的开头包含“ AnimationDoc.h“。 然后转入 CShowView 中,为了要能达到实时监测的目的,数据的接收要做在定时器中,这样才可以不断地检测是否有新的数据输入。检测到温度设置标志后,画一条横线。 void CShowView:OnTimer(UINT nIDEvent) CClientDC dc(this); CAnimationDoc*pDoc=(CAnimationDoc*)GetDocument(); ASSERT(pDoc-IsKindOf(RUNTIME_CLAS
21、S(CAnimationDoc); UpdateData(); If(pDoc-m_bSetTemperatureFlagDoc) dc.MoveTo(0,145-pDoc-m_nSetTemperatureDoc); dc.LineTo(400,145-pDoc-m_nSetTemperatureDoc); /画一条横 线 pDoc-m_bSetTemperatureFlagDoc=FALSE; /保证只做一次 CFormView:OnTimer(nIDEvent); 同样,在 CAnimationView 中,编辑 OnTimer()函数调用的过程 ServicedAnimation(),检测到进水键响应成功后,调用进水动画例程 DrawAnimationWaterIn()。 四、监视窗口功能的实现 温度、水位跟踪实现的原理类似于 控制功能,它实现的是 CAnimatinDoc 与 CShowView 类之间的数据传递,当系统采样的温度与水位变化时, CShowView 中的 m_nTemperature(温度变量)即随之变化,在利用 MoveTo()与 LineTo()函数,便可将其画在图上。再把这些做在定时器当中,就可以实时监测。