最近把两三年前做的个程序翻出来改了一下,虽然很久没搞VC了,算是恢复了一些记忆,,做这类的程序就它比较靠谱效率高体积小好把控不需要用户装这类装那的,主要就是实现发电站的接线组态一个显示。相应的闸刀和开关合上就电流指示和电机转动。当时到处找了一下现成的组态图软件发觉很贵,到处查资料自己分析了一下感觉也不难,就自己根据项目要求的实现了一下。当时时间紧动态效果指示不流畅反应不灵敏,最近应用户的建议进行了深化,优化了逻辑判断效果,记录一下以后免得忘记。也给有需要的兄弟要做这类似的提供一个小小的思路。
目前,实现了动态调整开关位置和电机位置,没有和生产系统连接(主要原因是电力发电系统的协议没获取到),点击开关来模拟的 ,想实现动态生产系统实时也是很简单的,进行协议解析,联动程序中相应的开关和电机,和生产中的开关这类的同步更新。
分辨率:1920*1080(为了显示不被压缩) 运行效果图:
由于以前习惯了VC6了 而且它也能实现这个项目的开发所以还是用的是vc6+win7来做的。大致记录一下实现的过程:
1、全屏显示,不能把分辨率改了就不改回来,为更好的用户体验,先记录计算机的原始分辨率,然后设置这个软件需要的分辨率1920*1080,程序退出就恢复到原来的分辨率。
实现改变分辨率:
void CxxxApp::ChangeScreenXY(int x, int y)
{
DEVMODE lpDevMode;
lpDevMode.dmBitsPerPel=32;
lpDevMode.dmPelsWidth=x;
lpDevMode.dmPelsHeight=y;
lpDevMode.dmSize=sizeof(lpDevMode);
lpDevMode.dmFields =DM_PELSWIDTH|DM_PELSHEIGHT|DM_BITSPERPEL;
LONG result;
result=ChangeDisplaySettings(&lpDevMode,0);
if (result==DISP_CHANGE_SUCCESSFUL)
{
ChangeDisplaySettings(&lpDevMode,CDS_UPDATEREGISTRY);
}
else
{
AfxMessageBox("修改分辨率失败,恢复原有设置!");
ChangeDisplaySettings(NULL,0);
}
}
程序进入先进行原始分辨率信息收集:
BOOL CxxxApp::InitInstance() 添加
m_nSreenX = ::GetSystemMetrics(SM_CXSCREEN);
m_nSreenY = ::GetSystemMetrics(SM_CYSCREEN);
if(m_nSreenX !=SCREENX && m_nSreenY != SCREENY){
ChangeScreenXY(SCREENX,SCREENY);
}
<pre name="code" class="cpp">
程序退出 善后处理:
int CxxxApp::ExitInstance()
if(m_nSreenX !=::GetSystemMetrics(SM_CXSCREEN) && m_nSreenY != ::GetSystemMetrics(SM_CYSCREEN) ){
ChangeScreenXY(m_nSreenX,m_nSreenY);
}
2、为了更通用实现背景和程序分离方式,通过读取外部图片的方式。
CPaintDC dc(this);
CRect rect;
GetClientRect(&rect);
CDC dcMem;
dcMem.CreateCompatibleDC(&dc);
CHAR FilePath[255];
GetModuleFileName(NULL,FilePath,255);
(strrchr(FilePath,'\\'))[1] = 0;
strcat(FilePath,"bg.bmp");
HBITMAP hBitmap=(HBITMAP)LoadImage(NULL,FilePath,IMAGE_BITMAP,0,0,LR_LOADFROMFILE|LR_CREATEDIBSECTION);
if(hBitmap ==NULL){
MessageBox("接线图没找到!");
}else{
CBitmap bmpBackground;
bmpBackground.Attach(hBitmap);
BITMAP bitmap;
bmpBackground.GetBitmap(&bitmap);
CBitmap *pbmpOld=dcMem.SelectObject(&bmpBackground);
dc.StretchBlt(0,0,rect.Width(),rect.Height(),&dcMem,0,0,bitmap.bmWidth,bitmap.bmHeight,SRCCOPY);
}
3、开关,发电机,电流组件通过拖动到相应的位置,信息记录保存在ini文件中。
发电机位置记录来了个循序实现代码:
<span style="white-space:pre"> </span>for(int i = 0 ; i<MAX_FAN ;i++){
<span style="white-space:pre"> </span>strName = "Motor";
<span style="white-space:pre"> </span>m_Dynamotor[i].GetWindowRect(&rect);
<span style="white-space:pre"> </span>strName +=itoa(i,ch1,10);
<span style="white-space:pre"> </span> COPini::WriteString(strName, "left", itoa(rect.left,ch1,10), "setinfo.ini");
<span style="white-space:pre"> </span>COPini::WriteString(strName, "top", itoa(rect.top,ch1,10), "setinfo.ini");
<span style="white-space:pre"> </span>COPini::WriteString(strName, "width", itoa(rect.right-rect.left,ch1,10), "setinfo.ini");<pre name="code" class="cpp">
COPini::WriteString(strName, "height", itoa(rect.bottom-rect.top,ch1,10), "setinfo.ini");
} 4、为了图上组件可以自动拖动 便于位置配置 从CStatic扩展了一个类CMoveCtrl;实际上很简单就是鼠标按下时加个定时器进行鼠标位置计算和重绘
void CMoveCtrl::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
if(nIDEvent==6)
{
//获得鼠标当前位置,转换为父窗口内的坐标
CPoint cursor_Pos;
GetCursorPos(&cursor_Pos);//该函数检取光标的位置,以屏幕坐标表示。
GetParent()->ScreenToClient(&cursor_Pos); //该函数把屏幕上指定点的屏幕坐标转换成用户坐标。
CPoint edit_Pos=pPoint;
ClientToScreen(&edit_Pos);//该函数将指定点的用户坐标转换成屏幕坐标
GetParent()->ScreenToClient(&edit_Pos);
// 获得控件的大小,并且转换到父窗口的坐标,主要是为了获得左上角的坐标,方便下面的movewindow调用
CRect edit_rect;
GetWindowRect(edit_rect);//该函数返回指定窗口的边框矩形的尺寸。该尺寸以相对于屏幕坐标左上角的屏幕坐标给出。
GetParent()->ScreenToClient(&edit_rect);
CRect parentRect;
GetParent()->GetClientRect(parentRect);
// 利用向量相等的性质求得鼠标移动后的新左上角坐标
int x=edit_rect.left+(cursor_Pos.x-edit_Pos.x);
int y=edit_rect.top+(cursor_Pos.y-edit_Pos.y);
if (x<0)
{
x=0;
}
if (y<0)
{
y=0;
}
if (x+edit_rect.Width()>parentRect.Width())
{
x = parentRect.Width() - edit_rect.Width();
}
if (y+edit_rect.Height()>parentRect.Height())
{
y = parentRect.Height() - edit_rect.Height();
}
MoveWindow(x,y,edit_rect.Width(),edit_rect.Height());
CStatic::OnTimer(nIDEvent);
}
}
5、电机的动态显示当时还费一点劲,显示原理很简单就是几张图片用定时器来控制来回的调用
if((nIDEvent == 15) && (m_dynameotor2 !=NULL)){
if(dynameotor_cout2 > 2147483000){ //为了溢出设置一个数 重置作用 不然溢出电机不转了
dynameotor_cout2 = 0;
}
m_Dynamotor[1].SetMeotorState(((dynameotor_cout2) % (STATE_NONE-1)),true );
dynameotor_cout2++;
}
6、就是开关联动,某几个开关进行判断触发相应的动作,这无非就是条件判断。
比如:
if( m_switch[11].GetSwitchState() && m_switch[12].GetSwitchState()){
if(m_dynameotor2 ==NULL){
m_dynameotor2=SetTimer(15,40,NULL);
}
小结:两三年前在xp下搞的我使用exit(0)退出程序能够恢复到原来的分辨率,在win7下ExitInstance() 就不执行了,最后发现是exit(0) 不好使。改用 PostQuitMessage(0)才好使用。最后得出结论
平台有差异,有些坑。程序慢慢改都是还是改到自己满意的。
演示程序: demo