第五次毕业设计任务书
1.以后每周具体任务暂定如下:
计划安排时间 | 计划完成内容 | 是否完成 |
2017.03.11~2017.04.14(4-8周) |
1.继续学习相关内容,并实际用到开发环境中去编写测试代码; 2.把核心代码写好; 3.并开始准备毕业论文; |
进行中 |
第七周 | 继续上周的内容,研究图形化界面显示信息。(因为难度就在这里,动态实时显示数据信息之类的) | 完成 |
第八周 |
1.继续编写代码 2.开始准备毕业论文 |
待完成 |
第九周 |
1.完成1.0版本 2.着手编写毕业论文 |
待完成 |
第十周 |
1.修改程序bug、优化程序等 2.补充完善论文 |
待完成 |
第十一周 |
1.完成最终版本的程序 2.补充完善论文 |
待完成 |
第十二周 |
完成毕业论文 |
待完成 |
第十三周 |
做好相关答辩准备 |
待完成 |
第十四周 |
答辩 |
待完成 |
2.本周任务:
本周主要是对程序的图形化界面信息的动态显示的研究和进行功能的代码编写。
通过双缓冲绘图来实现动画的效果:在图形图像显示过程中,计算机从显示缓冲区取数据然后显示,很多图形的操作都很复杂需要大量的计算,很难访问一次显示缓冲区就能写入待显示的完整图形数据,通常需要多次访问显示缓冲区,每次访问时写入最新计算的图形数据。而这样造成的后果是一个需要复杂计算的图形,你看到的效果可能是一部分一部分地显示出来的,造成很大的闪烁不连贯。而使用双缓冲,可以使你先将计算的中间结果存放在另一个缓冲区中,但全部的计算结束,该缓冲区已经存储了完整的图形之后,再将该缓冲区的图形数据一次性复制到显示缓冲区。
通过绘制一个矩形框,然后把矩形框的宽等分为100份(每1%为一份),矩形的高等分为5份(每20%为一份),然后通过一个大小为100的坐标数组用来保存每次在矩形框内绘制点,通过相邻的绘制点相连形成连续的折线,然后通过每一秒刷新数组的值,通过只改变每个坐标点的X轴的值,Y值不变,实现X轴的平移效果。
具体实现过程如下3提示。
3.代码及效果展示:
双缓冲绘图需要一个创建与当前DC兼容的内存DC和创建一块指定大小的位图,通过在内存DC里面完成想要的绘制,然后再通过BitBlt函数把内存DC绘制的内容全部复制到当前用于显示的DC上来达到刷新:
//大概创建双缓冲的简单例子如下 HDC wdc;//当前用于显示的DC HDC dc;//创建内存DC HBITMAP hbmp;//创建一张位图 wdc = GetDC(hwnd);//其中hwnd是程序的窗口句柄 //用双缓冲的话还要再定义一个位图对象吧,然后用CreateCompatibleBitmap建立一个与屏幕显示兼容的位图,再用SelectObject将位图选入到内存显示设备中 SelectObject(dc , CreateCompatibleBitmap(wdc , w , h));//w h 分别为wdc的宽高
然后根据获取系统时间来获得CPU的使用情况,显示CPU使用情况:
//获得系统时间: BOOL WINAPI GetSystemTimes( __out_opt LPFILETIME lpIdleTime, //空闲时间 __out_opt LPFILETIME lpKernelTime, //内核时间 __out_opt LPFILETIME lpUserTime //用户时间 );
// 获取几个时间 , 计算比例 得到当前CPU使用率 float GetCPUsage() { int u,k; float usage;//使用比例 static __int64 preIdle = 0, preUser = 0, preKernel = 0; __int64 Idle , User , Kernel ; //空闲时间//用户时间//内核时间 _GetSystemTimes((LPFILETIME)&Idle , (LPFILETIME)&User , (LPFILETIME)&Kernel);//获取 int i = Idle - preIdle ; u = User - preUser , k = Kernel - preKernel ; usage =1 - i * 1.0 / (k + u); preIdle = Idle , preUser = User , preKernel = Kernel ; usage=usage>1?1:usage;//有时会大于1 则至多返回1 usage=usage<0?0:usage;//有时会小于0 则至少返回0 return usage; }
GlobalMemoryStatus函数获取内存使用率:
void WINAPI GlobalMemoryStatus( __out LPMEMORYSTATUS lpBuffe; //传入一个MEMORYSTATUS结构体变量 )
作为参数传入的结构体变量定义:
typedef struct _MEMORYSTATUS { //注释来自百度 有一些不太懂的 我也没专门查资料了解 因为没用到 DWORD dwLength; // MEMORYSTATUS结构的大小,用来供函数检测结构的版本。 DWORD dwMemoryLoad; // 内存使用率(已用内存的百分比) DWORD dwTotalPhys; // 物理内存总量大小。 就是实际内存总量的大小 DWORD dwAvailPhys; // 可用物理内存大小。 DWORD dwTotalPageFile; // 交换文件总的大小。 不太懂是啥 我也没用到 DWORD dwAvailPageFile; // 交换文件中空闲部分大小。 还是不太懂 DWORD dwTotalVirtual; // 用户可用的地址空间 DWORD dwAvailVirtual; // 当前空闲的地址空间 } MEMORYSTATUS, *LPMEMORYSTATUS;
直接获取内存使用率:
MEMORYSTATUS memstat; GlobalMemoryStatus(&memstat); cout<<"当前内存使用率:\t"<<memstat.dwMemoryLoad<<"%"<<endl;
具体实现的代码如下:
#define _WIN32_WINNT 0x501 #include <windows.h> #include <stdio.h> #define KeyTest(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0) BOOL (WINAPI *_GetSystemTimes)(LPFILETIME,LPFILETIME,LPFILETIME); //获取系统时间 int x = 500 , y = 200, w = 400 , h = 800; //简单的封装一下一些属性 struct Somethod{ HWND hwnd; HDC wdc; HDC dc; HBITMAP hbmp; BITMAP bmp; HBRUSH hBrush; HPEN hPen; int cx,cy ; //绘制 Somethod(HWND _hwnd){ wdc = GetDC(hwnd = _hwnd); dc = CreateCompatibleDC(wdc); SelectObject(dc , CreateCompatibleBitmap(wdc , w , h)); SelectObject(dc , hBrush = (HBRUSH)GetStockObject(DC_BRUSH)); SelectObject(dc , hPen = (HPEN)GetStockObject(DC_PEN)); } ~Somethod(){ DeleteObject(hBrush);DeleteObject(hPen);DeleteObject(hbmp);ReleaseDC(hwnd , dc);} void Flush() { BitBlt(wdc, 0, 0, w, h, dc, 0, 0, SRCCOPY); } //连线 void Line(int x1,int y1 , int x2,int y2) { MoveToEx(dc,x1,y1,0); LineTo(dc,x2,y2); } //绘制矩形 而且是否填充 void Rectangle (int x1 , int y1 , int x2 , int y2 , int bFill = TRUE) { RECT rc = {x1,y1,x2,y2}; if(bFill) FillRect(dc , &rc , hBrush); else MoveToEx(dc,x1,y1,0),LineTo(dc,x2,y1),LineTo(dc,x2,y2),LineTo(dc,x1,y2),LineTo(dc,x1,y1); } //显示文字 使用了变参 void Print(int x , int y , const char*fmt , ...) { char buf[1000]; va_list args; va_start(args, fmt); vsprintf(buf, fmt, args); va_end(args); TextOut(dc , x , y , buf , strlen(buf)); } // 获取几个时间 , 计算比例 得到当前CPU使用率 float GetCPUsage() { int u,k; float usage;//使用比例 static __int64 preIdle = 0, preUser = 0, preKernel = 0; __int64 Idle , User , Kernel ; //空闲时间//用户时间//内核时间 _GetSystemTimes((LPFILETIME)&Idle , (LPFILETIME)&User , (LPFILETIME)&Kernel);//获取 int i = Idle - preIdle ; u = User - preUser , k = Kernel - preKernel ; usage =1 - i * 1.0 / (k + u); preIdle = Idle , preUser = User , preKernel = Kernel ; usage=usage>1?1:usage;//有时会大于1 则至多返回1 usage=usage<0?0:usage;//有时会小于0 则至少返回0 return usage; } void show() //测试用的绘制函数 { Rectangle(0, 0, w , h);//每次绘制重新填充整个程序窗口不然会出现覆盖现象 RECT rt = {25,300,220,400};//矩形位置 //设置颜色 SetTextColor(dc , RGB(0,255,0)); SetDCPenColor(dc, RGB(0,255,0)); //绘制矩形 Rectangle(rt.left, rt.top, rt.right, rt.bottom,FALSE); for (int i = 0 ; i <= 100 ; i += 20) { Print(0, rt.top+i-7, "%3d", 100 - i);} //绘制刻度 for (int j = 1; j<=4 ; j++) {Line(rt.left, rt.top+j*20, rt.left+5, rt.top+j*20);Line(rt.right, rt.top+j*20, rt.right-5, rt.top+j*20);} static MEMORYSTATUS memstat; GlobalMemoryStatus(&memstat);//获取内存信息 static POINT pt[2][100]; //绘制2条线 一条CPU使用率(绿色) 一条内存使用率(蓝色) static int n = 100; //分为100份 static int ii = 0; //当前已有点的个数 一开始是0个 增值最大100个 //通过数组实现 之前考虑过也试过用链表实现 但是过程很麻烦 还要处理内存泄露的问题 所以还是使用了简单的数组实现 //通过每次平移x,把上次绘图往左平移,每一秒刷新一次 看起来变达到简单的动画效果 if(ii == 0) //第一个点从右边开始 都在矩形右下角 { pt[0][0].x = rt.right; pt[0][0].y = rt.bottom; pt[1][0].x = rt.right; pt[1][0].y = rt.bottom; } for (int jj = ii; jj > 0; jj--) {//坐标点个数从1开始递增直100 数组下标0始终在最右端 直到下标为99到最左端 //每次把下一个的坐标的Y值赋值为上一个坐标的Y值 //而已有的坐标点X值不变 因为只有Y值再发生变化 pt[0][jj].y=pt[0][jj-1].y; pt[1][jj].y=pt[1][jj-1].y; if(jj == ii) {//但是每次第ii个点的坐标需平移得到当前第ii个坐标的固定x值 pt[0][jj].x= rt.right-(rt.right-rt.left) *ii/n;//平移(rt.right-rt.left) * ii/n x坐标 } if(jj == ii) {//如上 pt[1][jj].x= rt.right-(rt.right-rt.left) *ii/n; } } for (int jjj = ii; jjj > 0; jjj--) {//用来更新下标为0时的坐标 if (jjj == 1) { pt[0][0].x = rt.right; pt[0][0].y = rt.bottom-memstat.dwMemoryLoad;//内存使用率 pt[1][0].x = rt.right; pt[1][0].y = rt.bottom-100*GetCPUsage();//CPU使用率 } SetDCPenColor(dc, RGB(0,255,0));//绿色 CPU使用率 Line(pt[1][jjj].x,pt[1][jjj].y,pt[1][jjj-1].x,pt[1][jjj-1].y);//连接相邻的2个点 SetDCPenColor(dc, RGB(0,0,255));//蓝色 内存使用率 Line(pt[0][jjj].x,pt[0][jjj].y,pt[0][jjj-1].x,pt[0][jjj-1].y);//连接相邻的2个点 } ii = ii >= n ? n : ++ ii;//当个数>n时 始终为n } }; int main() { WNDCLASS wc = {0}; wc.style = CS_OWNDC ; wc.hInstance = GetModuleHandle(NULL); wc.lpfnWndProc = DefWindowProc; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL , IDC_ARROW); wc.lpszClassName = "rh"; if (!RegisterClass(&wc)) return 0; HWND hwnd = CreateWindow( wc.lpszClassName , wc.lpszClassName , WS_POPUP,x,y,w,h,0,0,wc.hInstance,0); if(hwnd == NULL) return 0; //SetWindowLong可以用来设置窗口的一些属性,透明 , WS_EX_TRANSPARENT = 鼠标穿透 , WS_EX_TOOLWINDOW = 不要在任务栏和 ALT+TAB 里显示 SetWindowLong ( hwnd , GWL_EXSTYLE ,WS_EX_TRANSPARENT + 0x80000 + WS_EX_TOOLWINDOW); ( (BOOL(WINAPI*)(HWND,COLORREF,BYTE,DWORD)) GetProcAddress(LoadLibrary("user32.dll") , "SetLayeredWindowAttributes") ) (hwnd , RGB(255,255,255) , 100 , 1); ShowWindow(hwnd, SW_NORMAL); // 最后显示窗口,使修改生效 _GetSystemTimes = (BOOL(WINAPI*)(LPFILETIME,LPFILETIME,LPFILETIME))GetProcAddress(GetModuleHandle("kernel32.dll"),"GetSystemTimes") ; Somethod md(hwnd); for(int i=0;;i++ ) { Sleep(100); //刷新频率 100 * 10 if(i%10 ==0) {md.show();md.Flush();} // 绘制完成后, 将窗口放到 HWND_BOTTOM = 底层 , HWND_TOPMOST = 顶层 SetWindowPos(hwnd , HWND_TOPMOST , 0 , 0 , 0 , 0 , SWP_NOMOVE + SWP_NOSIZE ); // 按ESC退出,测试用的: if(KeyTest(27)) {return 0 ;} } }
效果截图:(蓝色为内存 绿色为CPU)
4.心得与体会:
这次通过双缓冲绘图实现了简单的动画效果,感觉还是挺不错的体验。
5.参考文献:
双缓冲绘图原理:http://blog.csdn.net/acs713/article/details/16359551