介绍
在工业控制系统开发过程中,图形显示方面占有着很重要的作用。比起很多专用的组态软件,他们有着强大的在图形系统,能够组态出来非常漂亮的系统。现在的很多的工业图形开发包都需要支付费用,很多漂亮的控件比如仪表等只能看图兴叹了。前些天一个朋友做一个泵站的监控系统,由于缺少相关的控件,在研究了该类控件的编程方法上,借鉴网络上的一些编程资料,完成了一些可用于工业控制系统开发使用的控件。
正文
前面的两篇文章中我已经用实例介绍了如何利用双缓冲技术编写圆盘仪表的方法。具体见
工控软件图形界面-控件实现(圆形仪表控件) http://www.vchelp.net/itbookreview/view_paper.asp?paper_id=1671
工控软件图形界面-控件实现(圆形仪表控件二) http://www.vchelp.net/itbookreview/view_paper.asp?paper_id=1672
实际仪表的编写原理可以大致差不多,区别就是编写背景或者指针或者显示实时值的时候采用的不同方法而已。而在工业控制程序中,如果采用了多种不同的仿真仪表来实现现场的模拟,将会在人机接口方面更加的友好。这里继续在前面的基础上编写一种新型的仪表,并应用到工控现场的仪表。
我们还是按照编写的步骤,建立一个集成CStatic的仪表类。然后出发WM_PAINT等消息。然后在OnPaint中间完成仪表背景,仪表指针,仪表实时值的显示工作。在背景编写,指针和文字显示的时候,我们采用叠加的方法。图形叠加或者文字叠加方法其实很简单,在上篇文章已经有介绍,即是我们利用在相同的区域或者偏移量区域进行相同内容的绘制,绘制时候采用不同颜色,由于绘图的先后顺序,我们可以产生视觉上的立体叠加效果,具体实现见 工控软件图形界面-控件实现(圆形仪表控件二) http://www.vchelp.net/itbookreview/view_paper.asp?paper_id=1672
相关核心代码如下:
绘制仪表背景
//绘制仪表背景
void CPanelMeter::DrawMeterBackground(CDC *pDC, CRect &rect)
{
CBrush m_brushBack, pBackBrush, *pOldBrush;
pDC->SetBkColor(m_BackColor);
m_brushBack.CreateSolidBrush(m_BackColor);
pOldBrush = (CBrush *)pDC->SelectObject(&m_brushBack);
pDC->FillRect(rect, &m_brushBack); //绘制背景
pDC->Rectangle(rect); //绘制一个边框
pDC->SelectObject(pOldBrush);
m_brushBack.DeleteObject();
int yHalf = rect.bottom - 8;
float fa = (float)rect.Width() / 2;
float fb = (float)rect.Height() - 8;
CFont fScaleFont;
fScaleFont.CreateFont(10, 12,
0,
0,
FW_NORMAL,
FALSE,
FALSE,
FALSE,
DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH | FF_DONTCARE,
"Arial");
if (m_dValidValueMin >= 0)
{
CBrush brshGreen;
CBrush brshRed;
brshGreen.CreateSolidBrush(RGB(0, 192, 0));
brshRed.CreateSolidBrush(RGB(230, 100, 100));
float fStartAngle = (float)(m_dValidValueMin - m_dMinValue) / (float)(m_dMaxValue - m_dMinValue) * 3.14f;
float fEndAngle = (float)(m_dValidValueMax - m_dMinValue) / (float)(m_dMaxValue - m_dMinValue) * 3.14f;
pDC->MoveTo(rect.left, yHalf);
pDC->LineTo(rect.left + 20, yHalf);
pDC->MoveTo(rect.right - 1, yHalf);
pDC->LineTo(rect.right - 22, yHalf);
pDC->MoveTo(rect.left, yHalf);
for (float f = 0; f <= 3.14; f += 0.01f)
{
pDC->LineTo(rect.Width() / 2 - (int)(fa * cos(f)) + rect.left, yHalf - (int)(fb * sin(f)) + rect.top);
}
pDC->MoveTo(rect.left + 20, yHalf);
for (f = 0; f <= 3.14; f += 0.01f)
{
pDC->LineTo(rect.Width() / 2 - (int)((fa - 20) * cos(f)) + rect.left, yHalf - (int)((fb - 20) * sin(f)) + rect.top);
}
CBrush *pOldBrush = pDC->SelectObject(&brshRed);
pDC->FloodFill(rect.left + 10, yHalf - 10, RGB(0, 0, 0));
fStartAngle *= 100;
fStartAngle = (float)((int)fStartAngle) / 100;
fEndAngle *= 100;
fEndAngle = (float)((int)fEndAngle) / 100;
pDC->MoveTo(rect.Width() / 2 - (int)(fa * cos(fStartAngle)) + rect.left, yHalf - (int)(fb * sin(fStartAngle)) + rect.top);
pDC->LineTo(rect.Width() / 2 - (int)((fa - 20) * cos(fStartAngle)) + rect.left, yHalf - (int)((fb - 20) * sin(fStartAngle)) + rect.top);
pDC->MoveTo(rect.Width() / 2 - (int)(fa * cos(fEndAngle)) + rect.left, yHalf - (int)(fb * sin(fEndAngle)) + rect.top);
pDC->LineTo(rect.Width() / 2 - (int)((fa - 20) * cos(fEndAngle)) + rect.left, yHalf - (int)((fb - 20) * sin(fEndAngle)) + rect.top);
pDC->SelectObject(brshGreen);
if (fStartAngle > 0 && fEndAngle < 3.14)
{
pDC->FloodFill(rect.Width() / 2 - (int)((fa - 5) * cos((fStartAngle + fEndAngle) / 2)) + rect.left, yHalf - (int)((fb - 5) * sin((fStartAngle + fEndAngle) / 2)) + rect.top, RGB(0,0,0));
}
pDC->SelectObject(pOldBrush);
}
CPen pen;
pen.CreatePen(PS_SOLID, 3, RGB(255,255,255));
CPen *pOldPen = pDC->SelectObject(&pen);
CFont *pOldFont = pDC->SelectObject(&fScaleFont);
pDC->SetBkMode(TRANSPARENT);
fa -= 10;
fb -= 10;
pDC->MoveTo(rect.left + 10, yHalf);
for (float f = 0; f <= 3.14; f += 0.01f)
{
pDC->LineTo(rect.Width() / 2 - (int)(fa * cos(f)) + rect.left, yHalf - (int)(fb * sin(f)) + rect.top);
}
float fBigStep = 3.14f / ((float)(m_dMaxValue - m_dMinValue) / (float)m_dInterval) / 4;
int nDiv = 0;
int nScale = (int)m_dMinValue;
for (f = 0; f <= 3.15; f += fBigStep)
{
pDC->MoveTo(rect.Width() / 2 - (int)(fa * cos(f)) + rect.left, yHalf - (int)(fb * sin(f)) + rect.top);
if (nDiv == 0)
{
pDC->LineTo(rect.Width() / 2 - (int)((fa - 15) * cos(f)) + rect.left, yHalf - (int)((fb - 15) * sin(f)) + rect.top);
CString str;
str.Format("%d", nScale);
nScale += (int)m_dInterval;
CSize size = pDC->GetOutputTextExtent(str);
pDC->SetTextColor(RGB(0,0,0));
pDC->TextOut(rect.Width() / 2 - (int)((fa - 33) * cos(f)) + rect.left - size.cx / 2 + 1, yHalf - (int)((fb - 33) * sin(f)) + rect.top - size.cy / 2 + 1, str);
pDC->SetTextColor(RGB(255, 255, 255));
pDC->TextOut(rect.Width() / 2 - (int)((fa - 33) * cos(f)) + rect.left - size.cx / 2, yHalf - (int)((fb - 33) * sin(f)) + rect.top - size.cy / 2, str);
nDiv++;
}
else
{
pDC->LineTo(rect.Width() / 2 - (int)((fa - 10) * cos(f)) + rect.left, yHalf - (int)((fb - 10) * sin(f)) + rect.top);
nDiv++;
if (nDiv == 4)
{
nDiv = 0;
}
}
}
pDC->SelectObject(pOldFont);
fScaleFont.DeleteObject();
CPoint m_ptMeterCenter;
m_ptMeterCenter.x = rect.left + rect.Width() / 2;
m_ptMeterCenter.y = rect.bottom;
int nRadiusFrame = rect.Height();
CFont pUnitFont;
LOGFONT lf;
lf.lfEscapement = 0;
lf.lfItalic = NULL;
lf.lfUnderline = NULL;
lf.lfStrikeOut = NULL;
lf.lfCharSet = DEFAULT_CHARSET;
lf.lfHeight = nRadiusFrame / 4;
strcpy(lf.lfFaceName, "隶书");
pUnitFont.CreateFontIndirect(&lf);
pOldFont = (CFont *)pDC->SelectObject(&pUnitFont);
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(RGB(0, 0, 0));
CRect rectUnits(int(m_ptMeterCenter.x - nRadiusFrame * 0.30f + 2),
int(m_ptMeterCenter.y - nRadiusFrame * 0.70f + 2),
int(m_ptMeterCenter.x + nRadiusFrame * 0.30f + 2),
int(m_ptMeterCenter.y - nRadiusFrame * 0.20f + 2));
pDC->DrawText(m_strUnits, &rectUnits, DT_CENTER | DT_SINGLELINE | DT_VCENTER);
pDC->SetTextColor(RGB( 255, 255, 255));
rectUnits.SetRect(int(m_ptMeterCenter.x - nRadiusFrame * 0.30f),
int(m_ptMeterCenter.y - nRadiusFrame * 0.70f),
int(m_ptMeterCenter.x + nRadiusFrame * 0.30f),
int(m_ptMeterCenter.y - nRadiusFrame * 0.20f));
pDC->DrawText(m_strUnits, rectUnits, DT_CENTER | DT_SINGLELINE | DT_VCENTER);
pDC->SelectObject(pOldPen);
pDC->SelectObject(pOldFont);
pUnitFont.DeleteObject();
}
绘制仪表指针,采用叠加立体效果的处理技术
void CPanelMeter::DrawNeedle(CDC *pDC, CRect &rect)
{
if (m_dCurrentValue > m_dMaxValue)
{
m_dCurrentValue = m_dMaxValue;
}
else if (m_dCurrentValue < m_dMinValue)
{
m_dCurrentValue = m_dMinValue;
}
double dAngle = (m_dCurrentValue - m_dMinValue) / (m_dMaxValue - m_dMinValue) * 3.14f;
float fa = (float)rect.Width() / 2;
float fb = (float)rect.Height() - 8;
CBrush brshWhite;
CBrush brshShadow;
brshWhite.CreateSolidBrush(RGB(255, 255, 255));
brshShadow.CreateSolidBrush(RGB(0, 0, 0));
CBrush *pOldBrush = pDC->SelectObject(&brshShadow);
pDC->Ellipse(rect.Width() / 2 - 9 + rect.left, rect.Height() - 20 + rect.top, rect.Width() / 2 + 11 + rect.left, rect.Height() + rect.top);
pDC->SelectObject(&brshWhite);
pDC->Ellipse(rect.Width() / 2 - 10 + rect.left, rect.Height() - 21 + rect.top, rect.Width() / 2 + 10 + rect.left, rect.Height() - 1 + rect.top);
pDC->SelectObject(pOldBrush);
CPen penThick;
CPen penThin;
CPen penShadow;
penThick.CreatePen(PS_SOLID, 5, RGB(255, 100, 50));
penThin.CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
penShadow.CreatePen(PS_SOLID, 5, RGB(0, 0, 0));
CPen *pOldPen = pDC->SelectObject(&penShadow);
pDC->MoveTo(rect.Width() / 2 - (int)((fa - 8) * cos(dAngle)) + rect.left + 1, rect.Height() - 11 - (int)((fb - 8) * sin(dAngle)) + rect.top + 1);
pDC->LineTo(rect.Width() / 2 + rect.left + 1, rect.Height() - 11 + rect.top + 1);
pDC->SelectObject(&penThick);
pDC->MoveTo(rect.Width() / 2 - (int)((fa - 8) * cos(dAngle)) + rect.left, rect.Height() - 11 - (int)((fb - 8) * sin(dAngle)) + rect.top);
pDC->LineTo(rect.Width() / 2 + rect.left, rect.Height() - 11 + rect.top);
pDC->SelectObject(&penThin);
pDC->MoveTo(rect.Width() / 2 - (int)((fa - 8) * cos(dAngle)) + rect.left, rect.Height() - 11 - (int)((fb - 8) * sin(dAngle)) + rect.top);
pDC->LineTo(rect.Width() / 2 + rect.left, rect.Height() - 11 + rect.top);
pDC->SelectObject(pOldPen);
}
绘制实时值,同样采用叠加技术
void CPanelMeter::DrawValue(CDC *pDC, CRect &rect)
{
char strCurrentValue[10];
memset(strCurrentValue, 0, sizeof(strCurrentValue));
sprintf(strCurrentValue, "%.2f", m_dCurrentValue);
CPoint m_ptMeterCenter;
m_ptMeterCenter.x = rect.left + rect.Width() / 2;
m_ptMeterCenter.y = rect.bottom;
int nRadiusFrame = rect.Height();
CFont pUnitFont, *pOldFont;
LOGFONT lf;
lf.lfEscapement = 0;
lf.lfItalic = NULL;
lf.lfUnderline = NULL;
lf.lfStrikeOut = NULL;
lf.lfCharSet = DEFAULT_CHARSET;
lf.lfHeight = nRadiusFrame / 6;
strcpy(lf.lfFaceName, "Arial");
pUnitFont.CreateFontIndirect(&lf);
pOldFont = (CFont *)pDC->SelectObject(&pUnitFont);
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(RGB(0, 0, 0));
CRect rectUnits(int(m_ptMeterCenter.x - nRadiusFrame * 0.30f + 2),
int(m_ptMeterCenter.y - nRadiusFrame * 0.40f + 2),
int(m_ptMeterCenter.x + nRadiusFrame * 0.30f + 2),
int(m_ptMeterCenter.y - nRadiusFrame * 0.10f + 2));
pDC->DrawText(strCurrentValue, &rectUnits, DT_CENTER | DT_SINGLELINE | DT_VCENTER);
pDC->SetTextColor(RGB( 255, 255, 255));
rectUnits.SetRect(int(m_ptMeterCenter.x - nRadiusFrame * 0.30f),
int(m_ptMeterCenter.y - nRadiusFrame * 0.40f),
int(m_ptMeterCenter.x + nRadiusFrame * 0.30f),
int(m_ptMeterCenter.y - nRadiusFrame * 0.10f));
pDC->DrawText(strCurrentValue, rectUnits, DT_CENTER | DT_SINGLELINE | DT_VCENTER);
pDC->SelectObject(pOldFont);
pUnitFont.DeleteObject();
}
至此,基本绘制工作完成,我们添加一些接口以便能在程序中进行程序参数的改变。