一个支持各种交叉等形状工业控制管道控件的实现

2016-06-18

介绍

工业控制系统编程过程中,在模拟现场等环境系统中,管道控件是一个非常重要并且非常有用的控件。很多工业组态软件能够利用他的图形库制作出来非常漂亮的三维现场画面,这样给程序注入了很多的生机。而在个人编写的工业程序中,由于没有图形库等支持,那么显示就显得非常的单调。如果我们可以加入此类控件的支持,则会使得我们的程序变得更加的专业。

正文

现在网络上也有一部分提供了管道控件的编程方法,不过基本上都是比较粗糙,效果不理想,而且在管道实体经常有各种的交叉等形状,而网络上面几乎找不到那种支持各种形状的管道控件(一般就提供了横向和纵向两种形状的支持)。利用这些控件完成的界面中,当那些复杂的拐角等区域就会显得非常的别扭。在分析和收集这些信息的基础上,我对管道等进行了一些控件的开发,完成了各种拐角等交叉形状的 编写。希望我的这些经验对从事这个方面或者寻求相关信息的人有帮助。

首先是管道控件的实现。管道就是一种圆形的设备,从外观上看我们大致可以看到是一种中间向两边变暗的过程。在我的另一篇文章“一个工业控制管道流动控件的实现”中采用了渐变的方法实现了实体的绘制。那个适合于简单的管道的实现。比如横向和纵向等。但是在复杂的控件实现中,这种方法实现拐角等是非常的麻烦,所以我们不采用这种方法。

我们在windows的画图程序中可以做这样的试验,我们用画刷采用不同的颜色绘制一些不同颜色的直线,当我们的颜色采用一种颜色向另外一种颜色渐变的时候就会产生视觉上的一种“凹凸”的效果。由于管道一般是黑白颜色,我们这里可以用黑白的大致渐变颜色进行绘制,显示效果见下:大致可以看出,用这种不同的颜色进行一些区域的绘制就可以形成那种视觉效果上的管道的绘制。

上图所示的我们可以得到是一种能够类似横向管道的控件绘制的方法,那么同样道理,我们可以这样得到纵向管道的控件的绘制方法。当时在拐角的实现中,我们要进行一些别的处理。很多时候我们看到一些师傅进行实体连接的时候,当进行管道拐角的时候,需要将两根管道在接口处进行鞋45度的切割,然后就实现了不同管道的交叉了。我们可以这种方法进行交叉的处理就可以了。相信可以见后面的代码实现。

通过对管道的分析,大致可以得到管道的一些通用的形状,大概氛围下面11种情况,左上的拐角、右上的拐角、右下的拐角、左下的拐角、竖向、横向、中央交叉、左边交叉、顶边交叉、上部交叉、底边交叉。通过设置控件的属性进行不同形状的绘制工作。我们可以在这里进行形状的大致抽象

//管道的形状
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;

下面就是我们管道的绘制工作了,触发WM_PAINT消息完成绘制工作。

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; iSelectObject(&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; iSelectObject(&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; iSelectObject(&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; iSelectObject(&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; iSelectObject(&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; iSelectObject(&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; iSelectObject(&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; iSelectObject(&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; iSelectObject(&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; iSelectObject(&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(); 	
	}
}

到这里,我们的定义的各种形状就绘制完毕了,编译控件运行,我们可以看到下面的效果:

由于时间关系,这里控件就固定采用了黑白的模式,没有提供颜色动态修改等方法,然后控件的交叉大致实现了基本的各种交叉,可能还有一些别的形状等。各位如有好的实现方法请完成实现并共享您的经验。

您可以和我联系:

successq_g@163.com

QQ:5516853

正文完