• VC++窗口创建过程,图形绘制,时钟程序


    创建窗口步骤:

    (1)注册窗口类(RegisterClassEx)

    (2)创建窗口(CreateWindowEx

    (3)在桌面显示窗口(ShowWindow)

    (4)更新窗口客户区(UpdateWindow)

    (5)进入无限的消息获取和处理的循环:获取消息(GetMessage);分派消息至窗口函数处理(DisPatchMessage);

       如果是WM_QUIT,函数(GetMessage)返回False,消息循环结束,程序退出。

    注册窗口类需要初始化一个窗口类结构,将其写成一个函数如下:

    ATOM MyRegisterClass(HINSTANCE hInstance)
    {
        WNDCLASSEXW wcex;  //窗口类结构
        wcex.cbSize = sizeof(WNDCLASSEX); //结构大小
        wcex.style          = CS_HREDRAW | CS_VREDRAW|CS_OWNDC;//CS:class style此窗口类派生的窗口具有的风格
        //CS_OWNDC使得windows将每次对DC的设置保存下来,CS_HREDRAW | CS_VREDRAW窗口大小变化就刷新
        wcex.lpfnWndProc    = WndProc;    //窗口函数指针                     
        wcex.cbClsExtra     = 0;  //紧跟在窗口类结构后的附加字节数
        wcex.cbWndExtra     = 0;  //紧跟在窗口事例后的附加字节数
        wcex.hInstance      = hInstance;//本模块的实例句柄
        wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_CLOCKWINDOW));//窗口左上角图标句柄
        wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);//光标句柄
        wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW); //背景画刷句柄
        wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_CLOCKWINDOW);//菜单名
        //wcex.lpszMenuName   =NULL;  //无菜单
        wcex.lpszClassName  = szWindowClass;  //此窗口类名
        wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));//小图标句柄
        return RegisterClassExW(&wcex);  //注册此窗口类
    }

    创建窗口、显示窗口、刷新客户区写成一个函数如下:

    BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
    {
        hInst = hInstance; // 将实例句柄存储在全局变量中
       HWND hWnd = CreateWindowW(szWindowClass,       //窗口类名
                                 szTitle,             //窗口标题
                                 WS_OVERLAPPEDWINDOW, //窗口风格 WS:window style                    
                                 CW_USEDEFAULT,       //初始位置X坐标
                                 CW_USEDEFAULT,       //初始位置Y坐标
                                 500,                 //宽度
                                 500,                 //高度
                                 nullptr,             //父窗口句柄
                                 nullptr,             //菜单句柄
                                 hInstance,           //程序实例句柄
                                 nullptr);            //用户数据
       if (!hWnd)
       {
          return FALSE;
       }
       ShowWindow(hWnd, nCmdShow);  //显示窗口
       UpdateWindow(hWnd);          //刷新客户区
       return TRUE;
    }

    因此主函数可以写为:

    int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                         _In_opt_ HINSTANCE hPrevInstance,
                         _In_ LPWSTR    lpCmdLine,
                         _In_ int       nCmdShow)
    {
        UNREFERENCED_PARAMETER(hPrevInstance);
        UNREFERENCED_PARAMETER(lpCmdLine);
        // 初始化全局字符串
        LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
        LoadStringW(hInstance, IDC_CLOCKWINDOW, szWindowClass, MAX_LOADSTRING);
    
        MyRegisterClass(hInstance);
        // 执行应用程序初始化: 
        if (!InitInstance (hInstance, nCmdShow))
        {
            return FALSE;
        }
    
        HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CLOCKWINDOW));
    
        MSG msg;
        // 主消息循环: 
        while (GetMessage(&msg, nullptr, 0, 0))//GetMessage返回False程序结束
        {
            if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
        return (int) msg.wParam;  //程序结束时返回最后一个MSG的wParam。
    }

    在窗口作图

    测试一:先打开notepad,之后运行此代码

        HWND hWnd;
        HDC hDC;
        hWnd = ::FindWindow(_TEXT("Notepad"), NULL);
        hDC = ::GetDC(hWnd);
        while (::IsWindow(hWnd))
        {    
            ::SetTextColor(hDC, RGB(255, 0, 0));
            ::SetBkColor(hDC, RGB(0, 0, 0));
            ::TextOut(hDC, 10, 10, _T("this is a test."), sizeof("this is a test.") - 2);
    
            HPEN hPen = CreatePen(NULL,4,RGB(255,255,255));
            ::SelectObject(hDC, hPen);
            ::SelectObject(hDC, ::GetStockObject(BLACK_BRUSH));
            Ellipse(hDC, 0, 0, 500, 500);
            Sleep(1000);
            ::SelectObject(hDC, ::GetStockObject(WHITE_BRUSH));
            Ellipse(hDC, 0, 0, 500, 500);
            Sleep(1000);
            ::SelectObject(hDC, ::GetStockObject(LTGRAY_BRUSH));
            Ellipse(hDC, 0, 0, 500, 500);
            Sleep(1000);
            ::SelectObject(hDC, ::GetStockObject(GRAY_BRUSH));
            Ellipse(hDC, 0, 0, 500, 500);
            Sleep(1000);
            ::SelectObject(hDC, ::GetStockObject(DKGRAY_BRUSH));
            Ellipse(hDC, 0, 0, 500, 500);
            Sleep(1000);
            ::SelectObject(hDC, ::GetStockObject(DKGRAY_BRUSH));
            Ellipse(hDC, 0, 0, 500, 500);
            Sleep(1000);
        }
        ReleaseDC(hWnd, hDC);

    绘图首先要取得设备环境句柄

                PAINTSTRUCT ps;
                HDC hdc = BeginPaint(hWnd, &ps);  //取得(窗口客户区无效区域)设备环境句柄
                //绘图代码
                //EndPaint(hWnd, &ps);
                HDC hdcW = ::GetWindowDC(hWnd);   //取得整个窗口设备环境句柄
                HDC hdcD = ::GetDC(hWnd);         //取得窗口客户区设备环境句柄(函数可用在任何地方)        

    接着有时为了作图方便需要修改默认坐标系

    void SetIsotropic(HWND hWnd,HDC hdc)
    {
        RECT rect;
        GetClientRect(hWnd, &rect);       //取得客户区大小
        ::SetMapMode(hdc, MM_ANISOTROPIC);  //自定义坐标系MM_ANISOTROPIC:X!=Y,MM_ISOTROPIC:X=Y
        ::SetWindowExtEx(hdc, 500, 500, NULL);   //逻辑刻度,第四个参数可用于返回原来的大小
        ::SetViewportExtEx(hdc, rect.right, -rect.bottom, NULL); //像素刻度(坐标方向),第四个参数可用于返回原来的大小
        ::SetViewportOrgEx(hdc, rect.right / 2, rect.bottom / 2, NULL);  
        //修改坐标系(参照之前坐标系的位置坐标),第四个参数可用于返回原来的原点
    }

    修改完坐标系后开始绘制时钟表面

    void paintClock(HWND hWnd,HDC hdc)
    {
        HPEN hPenTri, hPenTime, hPenCent;
        HBRUSH hBrushTr, hBrushTime, hBrushCent;
        int colorUseR = (rand() % (255 - 0 + 1)) + 0;   //(rand() % (b-a+1))+ a; a~b随机数
        int colorUseG = (rand() % (255 - 0 + 1)) + 0;
        int colorUseB = (rand() % (255 - 0 + 1)) + 0;
        hPenTri = CreatePen(NULL, 4, RGB(255, colorUseG, 240));
        hBrushTr = ::CreateSolidBrush(RGB(255, colorUseG, 240));
        bankColor = RGB(255, colorUseG, 240);
        hPenTime = CreatePen(NULL, 4, RGB(colorUseR, 240, 240));
        hBrushTime = ::CreateSolidBrush(RGB(colorUseR, 240, 240));
        hPenCent = ::CreatePen(NULL, 4, RGB(240, 240, colorUseB));
        hBrushCent = ::CreateSolidBrush(RGB(240, 240, colorUseB));
    
        SetIsotropic(hWnd,hdc);
    ///三角形////////////////////////////////////////////////////////////////////////////////////
        //SetDCBrushColor(hdc, RGB(255, 255, 255));//(无效)
        ::SelectObject(hdc, hPenTri);
        ::SelectObject(hdc, hBrushTr);
      //POINT Mypoint[12] = { (250,10),(375,33.5),(466.5,125),(490,250),(466.5,375),(375,466.5),(250,490),(125,466.5),(33.5,375),(10,250),(33.5,125),(125,33.5) };//写法错误
        POINT Mypoint[12] = { 0,240,120,207.84,207.84,120,240,0,207.84,-120,120,-207.84,0,-240,-120,-207.84,-207.84,-120,-240,0,-207.84,120,-120,207.84 };
     //::ClientToScreen(hWnd, point);   //投射到屏幕坐标,返回至point
        POINT Mypoint1[3] = { Mypoint[0],Mypoint[4],Mypoint[8] };
        POINT Mypoint2[3] = { Mypoint[1],Mypoint[5],Mypoint[9] };
        POINT Mypoint3[3] = { Mypoint[2],Mypoint[6],Mypoint[10] };
        POINT Mypoint4[3] = { Mypoint[3],Mypoint[7],Mypoint[11] };
        //POINT Mypoint5[4]={ Mypoint1 ,Mypoint2,Mypoint3,Mypoint4}//不好用for循环简化操作,二维数组用不了??
        Polygon(hdc, Mypoint1, 3);
        Polygon(hdc, Mypoint2, 3);
        Polygon(hdc, Mypoint3, 3);
        Polygon(hdc, Mypoint4, 3);
    //圆形===============================================================
    
        ::SelectObject(hdc, hPenTime);
        ::SelectObject(hdc, hBrushTime);//图形填充色
        for (int i = 0; i < 12; i++)
        {
            Ellipse(hdc, Mypoint[i].x - 10, Mypoint[i].y - 10, Mypoint[i].x + 10, Mypoint[i].y + 10);
        }
    //中心点加文本=======================================================
        ::SelectObject(hdc, hPenCent);
        ::SelectObject(hdc, hBrushCent);
        ::Ellipse(hdc, -5, 5, 5, -5);
        ::SetTextColor(hdc, RGB(0, 0, 0));  //文字颜色
      //::SetBkColor(hdc, TRANSPARENT);  //文字背景色,设置为透明无效
        ::SetBkMode(hdc, TRANSPARENT);  //文字背景透明
    
        CString strText = _T("时钟");
        TextOut(hdc, -240, 240, (LPCWSTR)strText, sizeof(strText) - 2);
    }

    绘制时钟的指针函数

    void DrawHand(HDC hdc, int nLength, int nWidth, int nDegrees, COLORREF clrColor)
    {
        double nRadians = (double)nDegrees * 2 * 3.1415926 / 360;
        POINT pt[2];
        pt[0].x = (int)(nLength*sin(nRadians));
        pt[0].y = (int)(nLength*cos(nRadians));
        pt[1].x = pt[0].x /-5;
        pt[1].y = pt[0].y /-5;
        HPEN hPen = ::CreatePen(PS_SOLID, nWidth, clrColor);
        HPEN oldPen = (HPEN)::SelectObject(hdc, hPen);
        ::MoveToEx(hdc, pt[0].x, pt[0].y, NULL);
        LineTo(hdc, pt[1].x, pt[1].y);
    
        ::SelectObject(hdc, oldPen);
        ::DeleteObject(hPen);
    }

    在窗口函数WM_CREATE下设置定时器

        case WM_CREATE: 
    //当一个应用程序通过CreateWindowEx函数或者CreateWindow函数请求创建窗口时发送此消息,(此消息在函数返回之前发送)。
            SYSTEMTIME time;
            ::GetLocalTime(&time);
            s_PreHour = time.wHour%12;//取余
            s_PreMinute = time.wMinute;
            s_PreSecond = time.wSecond;
            SetTimer(hWnd,   //窗口句柄
                    IDT_CLOCK,//TimerID
                    1000,     //间隔
                    NULL);     //计时器函数指针
            return 0;
            //它应当返回0以使得窗口的创建过程得以继续。如果对于这个消息程序返回-1,窗口将会被销毁。
            //并且CreateWindowEx或者CreateWindow函数将会返回一个值为NULL的句柄。

    在WM_TIMER下控制指针的变化与重画

        case WM_TIMER:
            paintClock(hWnd, hDC);
            //paintClock(hWnd, hDC);重画了时钟面板,使得覆盖了旧的时钟指针
            if (::IsIconic(hWnd))   //如果窗口是最小化
            {
                return 0;
            }
            ::GetLocalTime(&time);
            //HDC hDC = ::GetDC(hWnd);//(无法写在这里)
            SetIsotropic(hWnd, hDC);//建立坐标系
            if (time.wMinute != s_PreMinute)
            {            
                //DrawHand(hDC, 140, 8, s_PreHour * 30 + s_PreMinute / 2, bankColor);//使用时钟面板颜色覆盖原指针
                //DrawHand(hDC, 170, 6, s_PreMinute * 6, bankColor);  
                //覆盖失败(不能使得指针颜色完全消失,如同在相同的两点好几条画好几条线,看起来线条变粗并不像一条直线)
                
                s_PreHour = time.wHour;
                s_PreMinute = time.wMinute;
            }
            if (time.wSecond != s_PreSecond)
            {
                //DrawHand(hDC, 170, 1, s_PreSecond * 6, bankColor);//使用时钟面板颜色覆盖原指针
                DrawHand(hDC, 80, 6, s_PreHour * 30 + s_PreMinute / 2, RGB(0,0,0));
                DrawHand(hDC, 120, 4, s_PreMinute * 6, RGB(0, 0, 0));
                DrawHand(hDC, 170, 2, s_PreSecond * 6, RGB(0, 0, 0));
                s_PreSecond = time.wSecond;
            }
            //::MessageBeep(MB_ICONASTERISK);//发出“嘟嘟”声
            break;

    效果图

  • 相关阅读:
    ubuntu安装pgAdmin 4
    python 读取文件
    byobu copy
    vim快捷键汇总
    python 停止线程
    python执行外部命令并获取输出
    gevent mysql
    python类型转换
    量化交易
    Java集合:HashMap底层实现和原理(源码解析)
  • 原文地址:https://www.cnblogs.com/cteng-common/p/Window_Clock.html
Copyright © 2020-2023  润新知