介紹
工業控制系統編程過程中,在模擬現場等環境系統中,管道控件是一個非常重要並且非常有用的控件。很多工業組態軟件能夠利用他的圖形庫制作出來非常漂亮的三維現場畫面,這樣給程序注入了很多的生機。而在個人編寫的工業程序中,由於沒有圖形庫等支持,那麼顯示就顯得非常的單調。如果我們可以加入此類控件的支持,則會使得我們的程序變得更加的專業。
正文
現在網絡上也有一部分提供了管道控件的編程方法,不過基本上都是比較粗糙,效果不理想,而且在管道實體經常有各種的交叉等形狀,而網絡上面幾乎找不到那種支持各種形狀的管道控件(一般就提供了橫向和縱向兩種形狀的支持)。利用這些控件完成的界面中,當那些復雜的拐角等區域就會顯得非常的別扭。在分析和收集這些信息的基礎上,我對管道等進行了一些控件的開發,完成了各種拐角等交叉形狀的 編寫。希望我的這些經驗對從事這個方面或者尋求相關信息的人有幫助。
首先是管道控件的實現。管道就是一種圓形的設備,從外觀上看我們大致可以看到是一種中間向兩邊變暗的過程。在我的另一篇文章“一個工業控制管道流動控件的實現”中采用了漸變的方法實現了實體的繪制。那個適合於簡單的管道的實現。比如橫向和縱向等。但是在復雜的控件實現中,這種方法實現拐角等是非常的麻煩,所以我們不采用這種方法。
我們在windows的畫圖程序中可以做這樣的試驗,我們用畫刷采用不同的顏色繪制一些不同顏色的直線,當我們的顏色采用一種顏色向另外一種顏色漸變的時候就會產生視覺上的一種“凹凸”的效果。由於管道一般是黑白顏色,我們這裡可以用黑白的大致漸變顏色進行繪制,顯示效果見下:大致可以看出,用這種不同的顏色進行一些區域的繪制就可以形成那種視覺效果上的管道的繪制。
上圖所示的我們可以得到是一種能夠類似橫向管道的控件繪制的方法,那麼同樣道理,我們可以這樣得到縱向管道的控件的繪制方法。當時在拐角的實現中,我們要進行一些別的處理。很多時候我們看到一些師傅進行實體連接的時候,當進行管道拐角的時候,需要將兩根管道在接口處進行鞋45度的切割,然後就實現了不同管道的交叉了。我們可以這種方法進行交叉的處理就可以了。相信可以見後面的代碼實現。
通過對管道的分析,大致可以得到管道的一些通用的形狀,大概氛圍下面11種情況,左上的拐角、右上的拐角、右下的拐角、左下的拐角、豎向、橫向、中央交叉、左邊交叉、頂邊交叉、上部交叉、底邊交叉。通過設置控件的屬性進行不同形狀的繪制工作。我們可以在這裡進行形狀的大致抽象//管道的形狀
下面就是我們管道的繪制工作了,觸發WM_PAINT消息完成繪制工作。
typedef enum _PipeFace
{
PF_CORNER_LEFT_TOP = 0, //左上的拐角
PF_CORNER_RIGHT_TOP = 2, //右上的拐角
PF_CORNER_RIGHT_BOTTOM = 3, //右下的拐角
PF_CORNER_LEFT_BOTTOM = 4, //左下的拐角
PF_VERTICAL = 5, //豎向
PF_HORIZONTAL = 6, //橫向
PF_CROSS_CENTER = 7, //中央交叉
PF_CROSS_LEFT = 8, //左邊交叉
PF_CROSS_TOP = 9, //頂邊交叉
PF_CROSS_RIGHT = 10, //上邊交叉
PF_CROSS_BOTTOM = 11 //底邊交叉
}PIPEFACE;void CPipeCtrl::OnPaint()
{
CPaintDC dc(this);
RECT rcClient;
GetClientRect(&rcClient);
switch (m_enumFace)
{
case PF_HORIZONTAL:
DrawHorizontal(&dc, rcClient);
break;
case PF_VERTICAL:
DrawVertical(&dc, rcClient);
break;
case PF_CORNER_LEFT_TOP:
DrawCornerLeftTop(&dc, rcClient);
break;
case PF_CORNER_RIGHT_TOP:
DrawCornerRightTop(&dc, rcClient);
break;
case PF_CORNER_RIGHT_BOTTOM:
DrawCornerRightBottom(&dc, rcClient);
break;
case PF_CORNER_LEFT_BOTTOM:
DrawCornerLeftBottom(&dc, rcClient);
break;
case PF_CROSS_CENTER:
DrawCrossCenter(&dc, rcClient);
break;
case PF_CROSS_TOP:
DrawCrossTop(&dc, rcClient);
break;
case PF_CROSS_RIGHT:
DrawCrossRight(&dc, rcClient);
break;
case PF_CROSS_BOTTOM:
DrawCrossBottom(&dc, rcClient);
break;
case PF_CROSS_LEFT:
DrawCrossLeft(&dc, rcClient);
break;
}
}
下面是關於管道的詳細繪制方法代碼,屬於這個控件繪制的核心部分
void CPipeCtrl::DrawHorizontal(CDC *pDC, RECT &rectClient)
{
RECT rcSect;
CBrush pBackBrush, *pOldBrush;
int nSectWidth = m_nWidth / SECTCOUNT;
for (int i=0; i < SECTCOUNT; i++)
{
pBackBrush.CreateSolidBrush(GetSectColor(i));
pOldBrush = (CBrush *)pDC->SelectObject(&pBackBrush);
SetRect(&rcSect, rectClient.left, rectClient.top + i * nSectWidth,
rectClient.right, rectClient.top + (i + 1) * nSectWidth);
pDC->FillRect(&rcSect, &pBackBrush);
pDC->SelectObject(pOldBrush);
pBackBrush.DeleteObject();
}
}
void CPipeCtrl::DrawVertical(CDC *pDC, RECT &rectClient)
{
RECT rcSect;
CBrush pBackBrush, *pOldBrush;
int nSectWidth = m_nWidth / SECTCOUNT;
for (int i=0; i<SECTCOUNT; i++)
{
pBackBrush.CreateSolidBrush(GetSectColor(i));
pOldBrush = (CBrush *)pDC->SelectObject(&pBackBrush);
SetRect(&rcSect, rectClient.left + i * nSectWidth, rectClient.top,
rectClient.left + (i + 1) * nSectWidth, rectClient.bottom);
pDC->FillRect(&rcSect, &pBackBrush);
pDC->SelectObject(pOldBrush);
pBackBrush.DeleteObject();
}
}
void CPipeCtrl::DrawCornerLeftTop(CDC *pDC, RECT &rectClient)
{
RECT rcSect;
CBrush pBackBrush, *pOldBrush;
int nSectWidth = m_nWidth / SECTCOUNT;
for (int i=0; i<SECTCOUNT; i++)
{
pBackBrush.CreateSolidBrush(GetSectColor(i));
pOldBrush = (CBrush *)pDC->SelectObject(&pBackBrush);
//繪制橫向
SetRect(&rcSect, rectClient.left + i * nSectWidth, rectClient.top + i * nSectWidth,
rectClient.right, rectClient.top + (i + 1) * nSectWidth);
pDC->FillRect(&rcSect, &pBackBrush);
//繪制豎向
SetRect(&rcSect, rectClient.left + i * nSectWidth, rectClient.top + i * nSectWidth,
rectClient.left + (i + 1) * nSectWidth, rectClient.bottom);
pDC->FillRect(&rcSect, &pBackBrush);
pDC->SelectObject(pOldBrush);
pBackBrush.DeleteObject();
}
}
void CPipeCtrl::DrawCornerRightBottom(CDC *pDC, RECT &rectClient)
{
RECT rcSect;
CBrush pBackBrush, *pOldBrush;
int nSectWidth = m_nWidth / SECTCOUNT;
for (int i=0; i<SECTCOUNT; i++)
{
pBackBrush.CreateSolidBrush(GetSectColor(i));
pOldBrush = (CBrush *)pDC->SelectObject(&pBackBrush);
//繪制橫向
SetRect(&rcSect, rectClient.left, rectClient.bottom - (SECTCOUNT - i) * nSectWidth,
rectClient.right - (SECTCOUNT - i) * nSectWidth, rectClient.bottom - (SECTCOUNT - i - 1) * nSectWidth);
pDC->FillRect(&rcSect, &pBackBrush);
//繪制豎向
SetRect(&rcSect, rectClient.right - (SECTCOUNT - i) * nSectWidth, rectClient.top,
rectClient.right - (SECTCOUNT - i - 1) * nSectWidth, rectClient.bottom - (SECTCOUNT - i - 1) * nSectWidth);
pDC->FillRect(&rcSect, &pBackBrush);
pDC->SelectObject(pOldBrush);
pBackBrush.DeleteObject();
}
}
void CPipeCtrl::DrawCornerRightTop(CDC *pDC, RECT &rectClient)
{
RECT rcSect;
CBrush pBackBrush, *pOldBrush;
int nSectWidth = m_nWidth / SECTCOUNT;
for (int i=0; i<SECTCOUNT; i++)
{
pBackBrush.CreateSolidBrush(GetSectColor(i));
pOldBrush = (CBrush *)pDC->SelectObject(&pBackBrush);
//繪制橫向
SetRect(&rcSect, rectClient.left, rectClient.top + i * nSectWidth,
rectClient.right - i * nSectWidth, rectClient.top + (i + 1) * nSectWidth);
pDC->FillRect(&rcSect, &pBackBrush);
//繪制豎向
SetRect(&rcSect, rectClient.right - (SECTCOUNT -i) * nSectWidth, rectClient.top + (SECTCOUNT - i) * nSectWidth,
rectClient.right - (SECTCOUNT - i - 1) * nSectWidth, rectClient.bottom);
pDC->FillRect(&rcSect, &pBackBrush);
pDC->SelectObject(pOldBrush);
pBackBrush.DeleteObject();
}
}
void CPipeCtrl::DrawCornerLeftBottom(CDC *pDC, RECT &rectClient)
{
RECT rcSect;
CBrush pBackBrush, *pOldBrush;
int nSectWidth = m_nWidth / SECTCOUNT;
for (int i=0; i<SECTCOUNT; i++)
{
pBackBrush.CreateSolidBrush(GetSectColor(i));
pOldBrush = (CBrush *)pDC->SelectObject(&pBackBrush);
//繪制橫向
SetRect(&rcSect, rectClient.left + (SECTCOUNT - i) * nSectWidth, rectClient.bottom - (SECTCOUNT - i) * nSectWidth,
rectClient.right, rectClient.bottom);
pDC->FillRect(&rcSect, &pBackBrush);
//繪制豎向
SetRect(&rcSect, rectClient.left + i * nSectWidth, rectClient.top,
rectClient.left + (i + 1) * nSectWidth, rectClient.bottom - i * nSectWidth);
pDC->FillRect(&rcSect, &pBackBrush);
pDC->SelectObject(pOldBrush);
pBackBrush.DeleteObject();
}
}
void CPipeCtrl::DrawCrossCenter(CDC *pDC, RECT &rectClient)
{
RECT rcSect;
CBrush pBackBrush, *pOldBrush;
int nSectWidth = m_nWidth / SECTCOUNT;
int xCenter = rectClient.left + (rectClient.right - rectClient.left) / 2;
int yCenter = rectClient.top + (rectClient.bottom - rectClient.top) / 2;
int xLeft = xCenter - SECTCOUNT * nSectWidth / 2;
int yTop = yCenter - SECTCOUNT * nSectWidth / 2;
//繪制橫向
for (int i=0; i<SECTCOUNT; i++)
{
pBackBrush.CreateSolidBrush(GetSectColor(i));
pOldBrush = (CBrush *)pDC->SelectObject(&pBackBrush);
SetRect(&rcSect, rectClient.left, yTop + i * nSectWidth,
rectClient.right, yTop + (i + 1) * nSectWidth);
pDC->FillRect(&rcSect, &pBackBrush);
pDC->SelectObject(pOldBrush);
pBackBrush.DeleteObject();
}
//繪制豎向
for (i=0; i<=SECTCOUNT / 2; i++)
{
pBackBrush.CreateSolidBrush(GetSectColor(i));
pOldBrush = (CBrush *)pDC->SelectObject(&pBackBrush);
//上左
SetRect(&rcSect, xLeft + i * nSectWidth, rectClient.top,
xLeft + (i + 1) * nSectWidth, yTop + i * nSectWidth);
pDC->FillRect(&rcSect, &pBackBrush);
//上右
SetRect(&rcSect, xLeft + (SECTCOUNT - i -1) * nSectWidth, rectClient.top,
xLeft + (SECTCOUNT - i) * nSectWidth, yTop + i * nSectWidth);
pDC->FillRect(&rcSect, &pBackBrush);
//下左
SetRect(&rcSect, xLeft + i * nSectWidth, yTop + (SECTCOUNT - i) * nSectWidth,
xLeft + (i + 1) * nSectWidth, rectClient.bottom);
pDC->FillRect(&rcSect, &pBackBrush);
//下右
SetRect(&rcSect, xLeft + (SECTCOUNT - i -1) * nSectWidth, yTop + (SECTCOUNT - i) * nSectWidth,
xLeft + (SECTCOUNT - i) * nSectWidth, rectClient.bottom);
pDC->FillRect(&rcSect, &pBackBrush);
pDC->SelectObject(pOldBrush);
pBackBrush.DeleteObject();
}
}
void CPipeCtrl::DrawCrossTop(CDC *pDC, RECT &rectClient)
{
RECT rcSect;
CBrush pBackBrush, *pOldBrush;
int nSectWidth = m_nWidth / SECTCOUNT;
int xCenter = rectClient.left + (rectClient.right - rectClient.left) / 2;
//繪制橫向
for (int i=0; i<SECTCOUNT; i++)
{
pBackBrush.CreateSolidBrush(GetSectColor(i));
pOldBrush = (CBrush *)pDC->SelectObject(&pBackBrush);
SetRect(&rcSect, rectClient.left, rectClient.top + i * nSectWidth,
rectClient.right, rectClient.top + (i + 1) * nSectWidth);
pDC->FillRect(&rcSect, &pBackBrush);
pDC->SelectObject(pOldBrush);
pBackBrush.DeleteObject();
}
//繪制豎向
int xLeft = xCenter - SECTCOUNT * nSectWidth / 2;
for (i=0; i<=SECTCOUNT / 2; i++)
{
pBackBrush.CreateSolidBrush(GetSectColor(i));
pOldBrush = (CBrush *)pDC->SelectObject(&pBackBrush);
//下左
SetRect(&rcSect, xLeft + i * nSectWidth, rectClient.top + (SECTCOUNT - i) * nSectWidth,
xLeft + (i + 1) * nSectWidth, rectClient.bottom);
pDC->FillRect(&rcSect, &pBackBrush);
//下右
SetRect(&rcSect, xLeft + (SECTCOUNT - i - 1) * nSectWidth, rectClient.top + (SECTCOUNT - i) * nSectWidth,
xLeft + (SECTCOUNT - i) * nSectWidth, rectClient.bottom);
pDC->FillRect(&rcSect, &pBackBrush);
pDC->SelectObject(pOldBrush);
pBackBrush.DeleteObject();
}
}
void CPipeCtrl::DrawCrossRight(CDC *pDC, RECT &rectClient)
{
RECT rcSect;
CBrush pBackBrush, *pOldBrush;
int nSectWidth = m_nWidth / SECTCOUNT;
int yCenter = rectClient.top + (rectClient.bottom - rectClient.top) / 2;
//繪制豎向
for (int i=0; i<SECTCOUNT; i++)
{
pBackBrush.CreateSolidBrush(GetSectColor(i));
pOldBrush = (CBrush *)pDC->SelectObject(&pBackBrush);
SetRect(&rcSect, rectClient.right - (SECTCOUNT - i) * nSectWidth, rectClient.top,
rectClient.right - (SECTCOUNT - i - 1) * nSectWidth, rectClient.bottom);
pDC->FillRect(&rcSect, &pBackBrush);
pDC->SelectObject(pOldBrush);
pBackBrush.DeleteObject();
}
//繪制橫向
int yTop = yCenter - SECTCOUNT * nSectWidth / 2;
for (i=0; i<=SECTCOUNT / 2; i++)
{
pBackBrush.CreateSolidBrush(GetSectColor(i));
pOldBrush = (CBrush *)pDC->SelectObject(&pBackBrush);
//上
SetRect(&rcSect, rectClient.left, yTop + i * nSectWidth,
rectClient.right - (SECTCOUNT - i) * nSectWidth, yTop + (SECTCOUNT - i - 1) * nSectWidth);
pDC->FillRect(&rcSect, &pBackBrush);
//下
SetRect(&rcSect, rectClient.left, yTop + (SECTCOUNT - i) * nSectWidth,
rectClient.right - (SECTCOUNT - i) * nSectWidth, yTop + (SECTCOUNT - i - 1) * nSectWidth);
pDC->FillRect(&rcSect, &pBackBrush);
pDC->SelectObject(pOldBrush);
pBackBrush.DeleteObject();
}
}
void CPipeCtrl::DrawCrossBottom(CDC *pDC, RECT &rectClient)
{
RECT rcSect;
CBrush pBackBrush, *pOldBrush;
int nSectWidth = m_nWidth / SECTCOUNT;
int xCenter = rectClient.left + (rectClient.right - rectClient.left) / 2;
//繪制橫向
for (int i=0; i<SECTCOUNT; i++)
{
pBackBrush.CreateSolidBrush(GetSectColor(i));
pOldBrush = (CBrush *)pDC->SelectObject(&pBackBrush);
SetRect(&rcSect, rectClient.left, rectClient.bottom - (i + 1) * nSectWidth,
rectClient.right, rectClient.bottom - i * nSectWidth);
pDC->FillRect(&rcSect, &pBackBrush);
pDC->SelectObject(pOldBrush);
pBackBrush.DeleteObject();
}
//繪制豎向
int xLeft = xCenter - SECTCOUNT * nSectWidth / 2;
for (i=0; i<=SECTCOUNT / 2; i++)
{
pBackBrush.CreateSolidBrush(GetSectColor(i));
pOldBrush = (CBrush *)pDC->SelectObject(&pBackBrush);
//下左
SetRect(&rcSect, xLeft + i * nSectWidth, rectClient.top,
xLeft + (i + 1) * nSectWidth, rectClient.bottom - (SECTCOUNT - i) * nSectWidth);
pDC->FillRect(&rcSect, &pBackBrush);
//下右
SetRect(&rcSect, xLeft + (SECTCOUNT - i - 1) * nSectWidth, rectClient.top,
xLeft + (SECTCOUNT - i) * nSectWidth, rectClient.bottom - (SECTCOUNT - i) * nSectWidth);
pDC->FillRect(&rcSect, &pBackBrush);
pDC->SelectObject(pOldBrush);
pBackBrush.DeleteObject();
}
}
void CPipeCtrl::DrawCrossLeft(CDC *pDC, RECT &rectClient)
{
RECT rcSect;
CBrush pBackBrush, *pOldBrush;
int nSectWidth = m_nWidth / SECTCOUNT;
int yCenter = rectClient.top + (rectClient.bottom - rectClient.top) / 2;
//繪制豎向
for (int i=0; i<SECTCOUNT; i++)
{
pBackBrush.CreateSolidBrush(GetSectColor(i));
pOldBrush = (CBrush *)pDC->SelectObject(&pBackBrush);
SetRect(&rcSect, rectClient.left + (SECTCOUNT - i) * nSectWidth, rectClient.top,
rectClient.left + (SECTCOUNT - i + 1) * nSectWidth, rectClient.bottom);
pDC->FillRect(&rcSect, &pBackBrush);
pDC->SelectObject(pOldBrush);
pBackBrush.DeleteObject();
}
//繪制橫向
int yTop = yCenter - SECTCOUNT * nSectWidth / 2;
for (i=0; i<=SECTCOUNT / 2; i++)
{
pBackBrush.CreateSolidBrush(GetSectColor(i));
pOldBrush = (CBrush *)pDC->SelectObject(&pBackBrush);
//上
SetRect(&rcSect, rectClient.left + (SECTCOUNT - i) * nSectWidth, yTop + i * nSectWidth,
rectClient.right, yTop + (SECTCOUNT - i - 1) * nSectWidth);
pDC->FillRect(&rcSect, &pBackBrush);
//下
SetRect(&rcSect, rectClient.left + (SECTCOUNT - i) * nSectWidth, yTop + (SECTCOUNT - i) * nSectWidth,
rectClient.right, yTop + (SECTCOUNT - i - 1) * nSectWidth);
pDC->FillRect(&rcSect, &pBackBrush);
pDC->SelectObject(pOldBrush);
pBackBrush.DeleteObject();
}
}
到這裡,我們的定義的各種形狀就繪制完畢了,編譯控件運行,我們可以看到下面的效果:
由於時間關系,這裡控件就固定采用了黑白的模式,沒有提供顏色動態修改等方法,然後控件的交叉大致實現了基本的各種交叉,可能還有一些別的形狀等。各位如有好的實現方法請完成實現並共享您的經驗。
您可以和我聯系:
QQ:5516853
正文完
本文配套源碼