GUI程序设计原理

  GUI(Graphical User Interface)即图形用户接口,是指用图形方式显示计算机操作的用户界面。相比于早期的计算机使用的命令行,图形界面对于用户来讲更易于接受。

1. 命令行应用程序

  命令行应用程序是一种基于顺序执行结构的可执行程序,如Linux操作系统上的ls、gcc、ifconfig命令。这种可执行程序在执行过程中并不需要与用户交互,程序执行到最后后用户运行结果,如产生一个可执行程序或者给出错误信息。程序的运行有固定的开始和固定的结束。

2. 图形界面应用程序

  随着计算机技术的发展,计算机日趋平民化,计算机用户不再是专业的计算机工作者。为非计算机专业的用户操作计算机,就产生了图形界面应用程序。GUI程序是一种基于消息驱动模型的可执行程序,程序的执行依赖于和用户的交互,实时响应用户操作。GUI程序执行后不会主动退出。

  GUI应用程序都是基于窗口的,其程序流程伪代码如下:

void mian(int argc, char** argv)
{
    //1. 定义主窗口

    //2. 创建主窗口

    //3. 创建主窗口内的元素

    //4. 显示主窗口

    //5. 进入消息接收/处理循环    
}

  这段伪代码揭示了GUI应用程序的原理。不论是基于跨平台的Qt GUI应用程序,还是基于Windows的MFC等其他GUI应用程序,原理都是如此。

  GUI程序执行后不会主动退出,都停留在接收消息,根据消息执行相应操作的循环。消息处理模型如下:
这里写图片描述

  以触摸屏为例,当用户点击触摸屏,首先感知到屏幕上被触摸的XY坐标是操作系统内核空间的触摸屏设备驱动程序,然后设备驱动程序会将用户操作封装成消息传递给GUI程序运行时创建的消息队列,GUI程序在运行过程中需要实时处理队列中的消息,当队列没有消息时,程序将处于停滞状态。

3. windows平台上GUI程序示例

  在Windows上实现GUI有很多方法,每一种方法都有着自己的一套开发理念和工具,常见的有:
  a. Windows API:直接调用Windows底层绘图函数。涉及到底层的基本用c语言写的,这些API也是如此。
  b. MFC:使用Windows API封装成控件类
  c. Windows Form:基于.net的GUI开发,完全组件化但是需要.Net运行库支持

  其中基于Windows API开发的GUI程序,即是函数调用 + Windows消息处理的方法,这是所有GUI程序的原理。

  Windows API写GUI最基本的函数有:
  a. 向系统注册GUI窗口RegisterClass(定义GUI窗口样式)
  原型:

#define RegisterClass  RegisterClassW
ATOM WINAPI RegisterClassW(__in CONST WNDCLASSW *lpWndClass);

  b. 创建窗口或窗口元素CreateWindow()

#define CreateWindow  CreateWindowW
#define CreateWindowW(lpClassName, lpWindowName, dwStyle, x, y,\
nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam)     CreateWindowExW(0L,\
lpClassName, lpWindowName, dwStyle, x, y,\
nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam)

  用户调用CreateWindow()函数,实则调用

CreateWindowW(
    __in_opt LPCWSTR lpClassName,   //创建窗口
    __in_opt LPCWSTR lpWindowName,  //窗口的标题
    __in DWORD dwStyle,             //窗口的显示风格
    __in int X,                     //窗口左上角x坐标
    __in int Y,                     //窗口左上角y坐标
    __in int nWidth,                //窗口的宽度
    __in int nHeight,               //窗口的高度
    __in_opt HWND hWndParent,       //父窗口的句柄
    __in_opt HMENU hMenu,           //窗口菜单栏/子窗口的标识符
    __in_opt HINSTANCE hInstance,   //应用程序实例的句柄
    __in_opt LPVOID lpParam         //窗口的创建数据
);

  CreateWindowW调用的是CreateWindowExW()函数,CreateWindowExW()将CreateWindowW()的首参数置为0:

HWND WINAPI CreateWindowExW(
    __in DWORD dwExStyle,           
    __in_opt LPCWSTR lpClassName,   
    __in_opt LPCWSTR lpWindowName,  
    __in DWORD dwStyle,             
    __in int X,
    __in int Y,
    __in int nWidth,
    __in int nHeight,
    __in_opt HWND hWndParent,
    __in_opt HMENU hMenu,
    __in_opt HINSTANCE hInstance,
    __in_opt LPVOID lpParam);

  c. 显示创建好的窗口ShowWindow()

BOOL WINAPI ShowWindow(__in HWND hWnd, __in int nCmdShow);  

  hWnd指窗口句柄,nCmdShow指定窗口如何显示

  d. 刷新屏幕上的窗口UpdateWindow()

WINUSERAPI BOOL WINAPI UpdateWindow(__in HWND hWnd);

  e. 获取程序中消息队列中的消息GetMessage()

#define GetMessage  GetMessageW
BOOL WINAPI GetMessageW(__out LPMSG lpMsg, __in_opt HWND hWnd, __in UINT wMsgFilterMin, __in UINT wMsgFilterMax);

  f. 翻译系统消息TranslateMessage()

WINUSERAPI BOOL WINAPI TranslateMessage(__in CONST MSG *lpMsg);

  内核空间发到用户空间的消息需要调用此函数翻译后才能传给GUI程序对应的消息处理函数

  g. 将消息发给窗口处理函数DispatchMessage()

#define DispatchMessage  DispatchMessageW
WINUSERAPI LRESULT WINAPI DispatchMessageW(__in CONST MSG *lpMsg);

  新建visual studio 2010的Win32工程:

#define STYLE_NAME    L"WIN"
#define BUTTON_ID     7777

//窗口消息处理函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    /* 调用系统提供的默认消息处理函数 */
    return DefWindowProc(hWnd, message, wParam, lParam);
}

/* 相当于c/c++的main函数 */
BOOL WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    MSG msg = {};           //定义一个消息队列
    HWND main_hwnd = NULL;  //窗口句柄
    HWND hwnd = NULL;       //窗口内的元素句柄
    WNDCLASS WndClass = {0};//定义描述窗口样式   

    //为窗口样式赋值
    WndClass.style         = 0;
    WndClass.cbClsExtra    = 0;
    WndClass.cbClsExtra    = 0;
    WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW);          // 定义窗口背景色
    WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);     // 定义鼠标样式
    WndClass.hIcon         = LoadIcon(NULL, IDI_APPLICATION); // 定义窗口左上角图标
    WndClass.hInstance     = hInstance;                       // 定义窗口式样属于当前应用程序
    WndClass.lpfnWndProc   = WndProc;                         // 窗口消息处理函数
    WndClass.lpszClassName = STYLE_NAME;                      // 窗口样式名
    WndClass.lpszMenuName  = NULL;

    //1. 将定义好的窗口式样注册到系统
    RegisterClass(&WndClass);

    //2. 创建窗口,返回窗口的句柄
    main_hwnd = CreateWindow(STYLE_NAME,       // 通过定义好的窗口式样创建主窗口
                        STYLE_NAME,            // 主窗口标题
                        WS_OVERLAPPEDWINDOW,   // 创建后主窗口的显示风格
                        CW_USEDEFAULT,         // 主窗口左上角 x 坐标
                        CW_USEDEFAULT,         // 主窗口左上角 y 坐标
                        CW_USEDEFAULT,         // 主窗口宽度
                        CW_USEDEFAULT,         // 主窗口高度
                        NULL,                  // 父窗口
                        NULL,                  // 窗口菜单栏
                        hInstance,             // 主窗口属于当前应用程序
                        NULL);                 // 窗口参数

    //获取该窗口的信息
    hInstance = (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE);

    //3. 创建窗口内元素,即按钮
    hwnd = CreateWindow(L"button",                               // 通过系统预定义式样创建窗口元素
                        L"My Button",                            // 窗口元素标题
                        WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,   // 窗口元素的显示风格
                        50,                                      // 窗口元素在窗口中的左上角 x 坐标
                        50,                                      // 窗口元素在窗口中的左上角 y 坐标
                        200,                                     // 窗口元素的宽度
                        60,                                      // 窗口元素的高度
                        main_hwnd,                               // 窗口元素所在的父窗口
                        (HMENU)BUTTON_ID,                        // 窗口元素 ID 值
                        hInstance,                               // 窗口元素属于当前应用程序
                        NULL);                                   // 窗口元素参数

    ShowWindow(main_hwnd,nCmdShow); // 显示窗口
    UpdateWindow(main_hwnd);        // 刷新窗口

    //4. 循环接收系统消息
    while (GetMessage(&msg, NULL, NULL, NULL) )
    {
        //翻译消息
        TranslateMessage(&msg);

        //将消息发给对应GUI的消息处理函数
        DispatchMessage(&msg);

    }
     return TRUE;
}

  编译运行:
这里写图片描述

  程序运行窗口如图。对该窗口可以利用鼠标操作进行最大/小化,拖动等。用户利用鼠标操作该窗口时,操作系统的内核会收到用户的输入信息并转换为消息发给当前的GUI程序,GUI程序的WndProc()函数收到消息后,执行相应处理。在WndProc()中,调用DefWindowProc()执行默认处理,这就是为什么程序对鼠标的最大/小化操作、拖动等能响应,而在代码中我们并没有位这些操作设置响应函数的原因。

  但是注意,点击关闭按钮后,窗口虽然被关闭了但是GUI程序并没有退出,显然这是因为DefWindowProc()对窗口关闭按钮并没有默认操作。在实际软件中有点击窗口关闭按钮并不一定是退出GUI程序,如windows的金山词霸,点击关闭按钮只是将其放到后台执行。另外,当鼠标点击“my button”按钮时并无反应,这也是因为消息处理函数并没有对应按钮消息的执行操作,修改如下:

HWND main_hwnd = NULL;  //窗口句柄

//窗口消息处理函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    //处理窗口关闭按钮
    if (message == WM_DESTROY)
        PostQuitMessage(0);

    //处理窗口内按钮点击操作
    if (message == WM_COMMAND)  //按钮按下操作属于WM_COMMAND
    {
        if (HIWORD(WM_COMMAND) == BN_CLICKED) //HIWORD(WM_COMMAND)表示从WM_COMMAND中拿到按钮是点击
                                              //拖动还是长按,BN_CLICKED表示点击
        {
            //弹出对话框
            MessageBox(main_hwnd, L"Hello Button!", L"Message", 0);
        }
    }

    /* 其他操作调用系统提供的默认消息处理函数 */
    return DefWindowProc(hWnd, message, wParam, lParam);
}

  这样就能使得GUI程序能处理关闭按钮消息和按钮点击消息了。

  综上,GUI程序的开发,分为两部分:
  (1) 在代码中用程序创建窗口及窗口内元素
  (2) 在消息处理函数中根据内核空间发来的消息做出对应的响应

  • 32
    点赞
  • 129
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值