介绍
2004年下半年我被公司派往刚果完成中国援外项目中间水电、变电、调度系统开发任务,由于开发系统中一般公司采用组态王等组态软件完成,这类组态软件虽然在界面表现形式方面比较丰富,但是由于考虑到系统得运行效率和功能的扩张,我采用VC自己编写上位机软件程序的方法实现。其中一个人完成上位机监控系统软件,通讯部分程序,调度自动化部分程序(遥控、遥信、遥测),由于项目没有全部验收,今年好要过去,但是这次开发中对我软件开发能力有了很大的提高,其中一些的处理过程和技术实现我特写出来,希望对致力于电力监控系统开发和寻求相关系统开发的人有帮助。由于篇幅限制,特讲项目分块发布。
正文
上一遍讲述了框架的形成,框架的编程只是一个大的架子的形成,我们需要在里面添加各种各样的枝叶才能形成一个完成的东西。这一遍我们来将模拟量信息显示界面的实现一个方法。模拟量信息的显示我们一般可以采用的方法是数字直接显示和图形界面的方法显示。采用数字的方法比较简单,直接调用GDI的函数进行基本的输出就可以了。但是考虑到程序的界面的美观性和程序Style的一致性,我们可以选用图形界面的形式表现。记得在以前我使用过CODEPROJECT上面的一些图形控件做过,比如温度表盘控件或者旋转表盘控件等,这里由于数据量比较多,同时由于界面空间的不够,我采用了LED的数值模拟显示的方法实现。相关界面我们可以见下:
计量信息显示:
测量信息显示:
这里用到的控件是CodeProject上面的数值显示控件,这个控件类比较复杂,我们没有必要去研究他里面是如何实现功能的,我们只需要知道如何应用这些已经写好的东西,如何调用他提供的接口参数。首先我们来看一个简单调用.
下面是我在论坛里面一个人要我给他作的一个模拟倒计时的程序,中间就用到了这个功能控件,程序运行窗体是下面这样的:
实现方法:
将下面控件类文件拷贝到程序目录下,
cdxCDynamicDialog.h
cdxCDynamicDialog.cpp
cdxCDynamicWnd.cpp
cdxCDynamicWnd.h
cdxCDynamicWndEx.cpp
cdxCDynamicWndEx.h
cdxCSizeIconCtrl.cpp
cdxCSizeIconCtrl.h
Curvefit.cpp,Curvefit.h
Digistatic.cpp
Digistatic.h
DigiUtil.cpp
DigiUtil.h
MemDC.h
Rgbcolor.h
然后采用“工程”-》“添加工程”-》“File”吧这些文件添加到工程里面来。
下面就是设计我们的界面了,放置适当位置的两个STATIC控件,然后通过CLASSWIZARD建立关联CONTROL变量;
然后定位到程序变量定义部分,将CStatic修改成为CDigiStatic和CDigiClock,不过在这里我们要包含几个头文件:
#include "Digistatic.h"
#include "DigiUtil.h"
#include "cdxCDynamicDialog.h"
然后将对话框的派生类CDialog修改成为cdxCDynamicDialog这样就能够编译通过了。
初始化里面进行控件初始化和启动即使起工作
AddSzControl(m_CurTime, 0, 0, 50, 0); AddSzControl(m_WayTime, 50, 0, 100, 0); m_WayTime.SetBkColor(LIGHTBLUE); m_WayTime.SetColor(LIGHTBLUE, WHITE); SetTimer(1,1000,NULL); void CMy11Dlg::OnTimer(UINT nIDEvent) { // TODO: Add your message handler code here and/or call default int nHour,nMinute,nSecond; CString strTime; CTime Tm=CTime::GetCurrentTime(); nHour=Tm.GetHour(); nMinute=Tm.GetMinute(); nSecond=Tm.GetSecond(); if(nHour<20) nHour=20-nHour; nMinute=60-nMinute; if(nMinute>0) nHour--; nSecond=60-nSecond; if(nSecond>0) nMinute--; strTime.Format("%.2d:%.2d:%.2d",nHour,nMinute,nSecond); m_WayTime.SetText(strTime); MessageBeep(-1); CDialog::OnTimer(nIDEvent); } |
这样,这个控件应用的程序就实现了。
要将这个控件应用与我们的监控系统中间还要有一点麻烦,因为基于对话框的程序比较容易操作,直接放置控件并调节位置然后建立关联变量设置就可以了,但是在SDI程序中,没有FORM,所以不能采用对话框的方法实现。我采用的是动态创建的方法实现。在很多大型程序中,动态创建控件是一个很重要也是很有效的途径。我们在这里就可以进行这些控件的动态创建工作,这里我们只需要进行模拟量信息的背景图片的设计工作就可以了。这里如果您的美工水平比较高,就能够设计得出来比我漂亮多了得界面了。(我那是时间有点匆忙,一个人加班加点弄的,当然说不上什么美工了)。这里由于创建控件的时候没有那个彩色的边框,所以单放上去是不好看的,我在进行背景图片设计的时候进行了适当的修改,在每个要动态创建控件的地方进行了边框的加工工作,如果各位下了这个演示程序的话就可以看到背景图片的设计是怎样的了。
由于我们的程序不是一个很小的程序,所以我们要考虑程序的封装,把功能的模块封装到各个功能类或者功能DLL中间去,这里由于没有很多的DLL,所以我采用了封装功能类的方法实现。即把一个程序要显示的界面封装成为一个派生于CWND的新类里面,然后进行这个类的操作等工作。这里比如我们派生一个A类。
相关实现如下,如果各位看不明白可以下载演示版本。
CRect m_PartEditRect[14][6];
CDigiStatic m_PartEdit[14][6];
初始化这些控件区域,将他们写到一个函数里面可以利于我们程序的重用,我们可以在以后类似的程序中间调用相同的处理方法,这里只需要修改相应的坐标位置就可以了
void CJiLiangStateWnd::InitDeviceRect() { this->m_PartEditRect[0][0].left = 185; this->m_PartEditRect[0][0].top = 163; this->m_PartEditRect[0][0].right = 257; this->m_PartEditRect[0][0].bottom = 184; for(int i=0;i<14;i++) { for(int j=0;j<6;j++) { this->m_PartEditRect[i][j].left = this->m_PartEditRect[0][0].left+89*j; this->m_PartEditRect[i][j].top = this->m_PartEditRect[0][0].top +35*i; this->m_PartEditRect[i][j].right = this->m_PartEditRect[0][0].right +89*j; this->m_PartEditRect[i][j].bottom = this->m_PartEditRect[0][0].bottom +35*i; } } } |
动态创建这些控件
void CJiLiangStateWnd::CreateDevice() { for(int i=0;i<14;i++) { for(int j=0;j<6;j++) { this->m_PartEdit[i][j].Create("",WS_CHILD|WS_VISIBLE|WS_BORDER|WS_TABSTOP,this->m_PartEditRect[i][j],AfxGetMainWnd(),2500+i+j); this->m_PartEdit[i][j].SetBkColor(BLACK); this->m_PartEdit[i][j].SetColor(DARKGREEN,LIGHTGREEN); this->m_PartEdit[i][j].SetText("2583.58"); this->m_PartEdit[i][j].ShowWindow(SW_HIDE); } } } |
下面是控制主程序部分是否显示这些控件,因为当前画面不是要求显示模拟量信息的时候,我们需要将这些控件进行ShowHide;
void CJiLiangStateWnd::ShowDevice(BOOL bShow) { if( bShow ) { for(int i=0;i<14;i++) for(int j=0;j<6;j++) this->m_PartEdit[i][j].ShowWindow(SW_SHOW); } else { for(int i=0;i<14;i++) for(int j=0;j<6;j++) this->m_PartEdit[i][j].ShowWindow(SW_HIDE); } } |
在这里,我们需要进行控件的刷新工作,如果以有数据,我们可以通过建立一个接口函数传递显示刷新的数据值,这里我们用于模拟显示,我们刷新随即数据就可以了
#define GetRandom( min, max ) ((rand() % (int)(((max)+1) - (min))) + (min)) void CJiLiangStateWnd::RefreshMsg() { CString szVal; for(int i=0;i<14;i++) { for(int j=0;j<6;j++) { this->fVal[i][j] = float(GetRandom(0,100000))/100; szVal.Format("%.2f",fVal[i][j]); this->m_PartEdit[i][j].SetText(szVal); } } } |
这样,到现在为止,我们的一些基本工作都已经完成了,运行我们的程序就可以看到结果了。此类方法可以供各位参考一下,可能各位还有更好的方法把。
详细代码书写请看我作的示范工程,由于时间匆忙,中间有很多的语句表达可能不是很清楚,您可以在下面的留言中提出来或者,如果有什么问题的话青与我联系:
正文完