介紹
在工業控制系統開發過程中,圖形顯示方面佔有著很重要的作用。比起很多專用的組態軟體,他們有著強大的在圖形系統,能夠組態出來非常漂亮的系統。現在的很多的工業圖形開發包都需要支付費用,很多漂亮的控制項比如儀錶等只能看圖興歎了。前些天一個朋友做一個泵站的監控系統,由於缺少相關的控制項,在研究了該類控制項的編程方法上,借鑒網路上的一些編程資料,完成了一些可用於工業控制系統開發使用的控制項。
本文
前面的兩篇文章中我已經用執行個體介紹了如何利用雙緩衝技術編寫圓盤儀錶的方法。具體見
工控軟體圖形介面-控制項實現(圓形儀錶控制項) 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();
}
至此,基本繪製工作完成,我們添加一些介面以便能在程式中進行程式參數的改變。