在实验和生产中,我们常常需要对被监测的对象进行实时监控,比如对现场的温度等环境因素进行实时数据采集,然后传输到主控制计算机,以动态曲线的方式显示出来,便于人们对现场的了解和控制。
2.用Visual C++实现动态曲线的绘制有4种方法。(1)使用消隐。(2)使用重绘。其中重绘按照原理的不同又分为3种。
2.1消隐。其实消隐的方法很简单,主要使用CDC类的成员函数SetROP2 。该函数原型为int SetROP2( int nDrawMode )。参数nDrawMode为新的绘制方式。该函数用来设置当前绘制方式,绘制方式说明画笔和被填充对象的内部如何与屏幕表面已有的颜色组合。我们选用R2_XORPEN绘制方式——画笔颜色和屏幕颜色的组合,但不同时在两者之中,最终像素=画笔XOR屏幕像素。要实现动态曲线只需在这种绘制方式下在原有曲线的位置上再绘一次,以消隐掉原有曲线,再绘制新的曲线,如此重复,就可形成动态曲线。
2.2重绘。在重绘中都要使用到这样一个函数:CDC类成员函数BitBlt 。原型为BOOL BitBlt( int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, DWORD dwRop )。该函数将一个位图从源设备描述表拷贝到CDC的设备描述表中。
2.2.1原理1:设置一个后台设备环境。所有的绘图工作都在后台完成,然后通过BitBlt 函数拷贝到当前设备环境。这种方法要求后台每次都全部重绘,包括坐标、字符说明、曲线等。
2.2.2应用举例:
在OnTimer 函数中每隔一定时间进行重绘。
void CDrawView::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
DrawPicture(); // 画图函数
}
void CDrawView::DrawPicture()
{
pBackDC->PatBlt(0,0,winx, winy, PATCOPY);
//画坐标轴及刻度
DrawCoordinate(pBackDC ,BLACKPEN, MainWindowLeft,MainWindowBottom - MAINHEIGHT, MainWindowLeft + MAINWIDTH, MainWindowBottom);
//标出x坐标刻度值
WriteCoordinateX(pBackDC, BLACKPEN, MainWindowLeft,MainWindowLeft + MAINWIDTH, MainWindowBottom);
//标出y坐标刻度值
WriteCoordinateY(pBackDC,BLACKPEN,MainWindowLeft,MainWindowBottom );
//输出实际宽度值
WriteMessage(pBackDC,MainWindowLeft,MainWindowBottom,MAINHEIGHT );
if ( pBackDC != NULL )
{
//画曲线图
DrawGraph(pBackDC,REDPEN,MainWindowLeft+MAINWIDTH,MainWindowBottom);
CDC *pDC = GetDC();
if (pDC != NULL)
{
//调用OnDraw()函数,将曲线图显示在屏幕中
OnDraw(pDC);
ReleaseDC(pDC);
}
}
}
void CDrawView::OnDraw(CDC* pDC)
{
CDrawDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
if (pDC != NULL)
pDC->BitBlt(0,0,winx, winy, pBackDC, 0, 0, SRCCOPY);
}
2.2.3原理2:设置一个后台设备环境(如图2),其大小与当前设备环境相同(如图1)。但主要绘图工作都在当前设备环境进行。例如设当前设备环境大小为200×100,曲线点之间的间距offset=5,左上角顶点在屏幕中的坐标为(200,100)。当所绘制的曲线横坐标X﹤400时,就在当前设备环境绘制曲线;当横坐标X﹥400时,就从前设备环境把坐标点(205,100)的右下部分用函数BitBlt拷贝到矩形区范围为(0,0,195,100)的后台设备环境中,然后清除当前设备环境,接着再用函数BitBlt从后台设备环境中把曲线拷贝到矩形区范围为(200,100,195,100)的当前设备环境中,最后再在曲线尾画上当前的数据点,如此循环。
(200,100) (0,0)
200×100 200×100
图1:当前设备环境 图2:后台设备环境
2.2.4应用举例:
void CAnimateLineView::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
CClientDC dc(this);
static int x=200;
static int y=200;
CPen pen1(PS_SOLID,0,RGB(255,255,255));
CPen *oldpen1=NULL;
oldpen1=dc.SelectObject(&pen1);
x=x+offsetx;
if(x<=400)
{
dc.MoveTo(x-offsetx,y);
y=200-rand()%90;
dc.LineTo(x,y);
}
else{
CRect rect(200,100,400,200);
CBrush bkbrush(HS_CROSS,RGB(0,128,0));
m_dc.BitBlt(0,0,195,100,&dc,205,100,SRCCOPY);
dc.SetBkColor(RGB(0,0,0));
dc.FillRect(rect,&bkbrush);
dc.BitBlt(200,100,195,100,&m_dc,0,0,SRCCOPY);
dc.MoveTo(395,y);
y=200-rand()%90;
dc.LineTo(400,y);}
dc.SelectObject(oldpen1);
CView::OnTimer(nIDEvent);
}
2.2.5原理3:设置一个后台设备环境,其宽度为当前设备环境的两倍,高度相同。绘图工作都在后台设备环境进行。例如设当前设备环境大小为200×100(如图1),则后台设备环境为400×100(如图2),曲线点之间的间距offset=10,左上角顶点在屏幕中的坐标为(200,100)。①当在后台设备环境中所绘制的曲线横坐标0﹤X﹤200时只需将前半部分区域拷贝到当前设备环境就行了;②当横坐标200﹤X﹤400时,将宽度为200的矩形区域依次向后半部分移动10个单位取出拷贝到当前设备环境;③当X﹥400时,将后半部分拷贝到当前设备环境,并清除前半部分。④当第2次0﹤X﹤200时,将后半部分矩形区域依次向后半部分移动10个单位拷贝到当前设备环境,并将实时曲线画在前半部分然后将其拷贝到当前设备环境的末尾;⑤当200﹤X﹤400时,清除后半部分,并将前半部分矩形区域依次向后移动10个单位拷贝到当前设备环境,在后半部分绘制实时曲线然后将其拷贝到当前设备环境的末尾;⑥当再次X﹥400时,回到④,如此循环下去就能形成动态曲线。
(200,100)
200×100
图1:当前设备环境
宽度为200,不停的向右移
(0,0) 200 400
(200+200)×100
图2:后台设备环境
2.2.6应用举例:
void CLineView::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
static int x=0;
static int y=0;
static int control=0;
CClientDC dc(this);
CRect rect(0,0,200,100);
CRect rect1(200,0,400,100);
CBrush bkbrush(HS_CROSS,RGB(0,128,0));
x=x+offset;
CPen pen1(PS_SOLID,0,RGB(255,255,255));
CPen *oldpen1=NULL;
oldpen1=m_dc.SelectObject(&pen1);
if(x<=200)
{
m_dc.MoveTo(x-offset,y);
y=100-rand()%90;
m_dc.LineTo(x,y);
if(control==0)
dc.BitBlt(200,100,200,100,&m_dc,0,0,SRCCOPY);
else
{
dc.BitBlt(200,100,200-x,100,&m_dc,x+200,0,SRCCOPY);
dc.BitBlt(400-x,100,x,100,&m_dc,0,0,SRCCOPY);
}
}
if(x<=400&&x>200)
{
if(control==1)
{
dc.BitBlt(200,100,200,100,&m_dc,x-200,0,SRCCOPY);
m_dc.FillRect(rect1,&bkbrush);
control=0;
}
m_dc.MoveTo(x-offset,y);
y=100-rand()%90;
m_dc.LineTo(x,y);
dc.BitBlt(200,100,200,100,&m_dc,x-200,0,SRCCOPY);
}
if(x>400)
{
x=0;
dc.BitBlt(200,100,200-x,100,&m_dc,x+200,0,SRCCOPY);
m_dc.SetBkColor(RGB(0,0,0));
m_dc.FillRect(rect,&bkbrush);
control=1;
}
dc.SelectObject(oldpen1);
CView::OnTimer(nIDEvent);
}
2.3总结
消隐的方法虽然简单,但不适用于实时监控。当图形区域要求参数说明和时间同时移动时,重绘中的第2和第3种方法实现起来比较麻烦。重绘中的第1种方法适用性较强,效果最好。