MyGui札记(1)建立第一个工程

MyGui笔记(1)建立第一个工程

        记录下学习​MyGui的一些笔记,从建立第一个工程开始。
环境:MyGui3.2.0(OpenGL平台)

步骤:

1.右键MYGUI解决方案,添加→新建项目,选择“Win32 项目”,名称为:TestHello。下一步,勾选“空项目”。

2.设置工程Debug版本属性。“调试”→“工作目录”填入如下: 

1
F:\MyCode\MyGUI_SVN\Build\bin\debug

“C/C++”→“常规”→“附加包含目录”填入以下: 

1
2
3
4
5
F:\MyCode\MyGUI_SVN\MyGUIEngine\include
F:\MyCode\MyGUI_SVN\Common
F:\MyCode\MyGUI_SVN\Common\Base\OpenGL
F:\MyCode\MyGUI_SVN\Platforms\OpenGL\OpenGLPlatform\include
F:\MyCode\MyGUI_SVN\Common\Input\Win32

“预处理器”→“预处理器定义”填入如下:  

1
WIN32;_WINDOWS;_DEBUG;_CRT_SECURE_NO_WARNINGS;MYGUI_OPENGL_PLATFORM;MYGUI_SAMPLES_INPUT_WIN32;

“链接器”→“常规”→“输出文件”填入如下 

1
F:\MyCode\MyGUI_SVN\Build\bin\Debug\$(ProjectName).exe

“附加库目录”填入如下: 

1
2
F:\MyCode\MyGUI_SVN\Build\lib\Debug
F:\MyCode\MyGUI_SVN\Dependencies\lib\Debug

“输入”→“附加依赖项”填入如下:  

1
kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib gdiplus.lib glu32.lib opengl32.lib Common_d.lib MyGUI.OpenGLPlatform_d.lib MyGUIEngine_d.lib freetype2311_D.lib

3.添加C++类TestKeeper,派生自base::BaseDemoManager,在TestKeeper.h 文件添加如下头文件:  

1
#include "Base/BaseDemoManager.h"

在TestKeeper.cpp文件添加如下头文件: 

1
#include "Base/Main.h"

在最底下添加如下代码: 

1
MYGUI_APP(TestKeeper)

这是一个宏,实质是Win32应用程序的入口函数,源码如下: 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#if MYGUI_PLATFORM == MYGUI_PLATFORM_WIN32
#   ifdef MYGUI_CHECK_MEMORY_LEAKS
#       define MYGUI_APP(cls) INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT argc) { _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); return startApp<cls>(); }
#   else
#       define MYGUI_APP(cls) INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT argc) { return startApp<cls>(); }
#   endif
#else
#   define MYGUI_APP(cls) int main(int argc, char **argv) { return startApp<cls>(); }
#endif

template <class AppClass>
int startApp()
{
    try
    {
        AppClass* app = new AppClass();
        app->prepare();
        if (app->create())
        {
            app->run();
            app->destroy();
        }
        delete app;
        app = 0;
    }
    catch (MyGUI::Exception& _e)
    {
#if MYGUI_PLATFORM == MYGUI_PLATFORM_WIN32
        MessageBoxA( NULL, _e.getFullDescription().c_str(), "An exception has occured", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
        std::cerr << "An exception has occured" << " : " << _e.getFullDescription().c_str();
#endif
        throw;
    }
    return 0;
}

可以看到,首先调用了prepare方法进行了准备工作,这是个虚函数,没有具体实现,派生类可以实现自己需要的工作。接着调用create方法进行创建窗口,内部代码部分注释如下: 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
bool BaseManager::create(int _width, int _height)
{
    //中间省略
    hWnd = CreateWindow(wc.lpszClassName, TEXT("OpenGL Render Window"), WS_POPUP,
        0000, GetDesktopWindow(), NULL, wc.hInstance, this);
    //中间省略
    
    //创建渲染  根据所选的渲染系统,创建渲染显示
    if (!createRender(width, height, windowed))
    {
        return false;
    }
    //创建Gui  包含加载资源setupResources
    createGui();
    //创建输入管理器  调用的是基类的InputManager::createInput
    createInput((size_t)hWnd);
    //创建指针管理器  调用的是基类的PointerManager::createPointerManager
    createPointerManager((size_t)hWnd);
    //创建场景  没有实现,具体由派生类来实现
    createScene();
    //内部函数  窗口变化大小时,会触发来重新设置渲染窗口大小
    _windowResized();

    return true;
}

之后就是调用run方法来进行消息的处理了,代码如下: 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void BaseManager::run()
{
    MSG msg;
    while (true)
    {
        while (PeekMessage(&msg, NULL00, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        if (mExit)
            break;
        else if (msg.message == WM_QUIT)
            break;

        captureInput(); //捕获输入  由派生类来实现
        drawOneFrame(); //绘制一帧  调用相应的渲染方法

        if (GetActiveWindow() != hWnd)
            ::Sleep(50);
    }
}

当收到退出的消息,就会退出循环,调用destroy方法,相应地将create创建出来的东西倒序销毁掉,代码如下: 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void BaseManager::destroy()
{
    destroyScene();

    destroyPointerManager();

    destroyInput();

    destroyGui();

    destroyRender();

    if (hWnd)
    {
        DestroyWindow(hWnd);
        hWnd = 0;
    }

    UnregisterClass(WND_CLASS_NAME, hInstance);
}

此时,编译运行程序的话,将会看到一个黑色的窗口。

4.简单地显示一个对话框和按钮。重载createScene方法,代码如下: 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void TestKeeper::createScene()
{
    base::BaseDemoManager::createScene();

    //创建一个主对话框
    MyGUI::Window* window = MyGUI::Gui::getInstance().createWidget<MyGUI::Window>(
        "WindowCS",                         //皮肤
        MyGUI::IntCoord(1015120130),  //坐标、宽高
        MyGUI::Align::Default,              //对齐方式
        "Main"                              //创建于哪个层上,定义在MyGUI_Layers.xml
        );
    window->setCaption("Frame");            //标题名称
    window->setMinSize(8080);             //最小的大小

    //创建位于对话框里的按钮
    MyGUI::Button* button = window->createWidget<MyGUI::Button>(
        "Button",
        MyGUI::IntCoord(30355030), 
        MyGUI::Align::Default
        );
    button->setCaption("Button");
}

createScene方法是用来初始化时创建场景的,在这里可以进行添加控件。编译运行,可看到如下结果:
MyGui札记(1)建立第一个工程

5.分析资源的载入和控件的创建。资源的载入从createGui方法开始,代码如下: 

1
2
3
4
5
6
7
8
9
10
void BaseManager::createGui()
{
    mPlatform = new MyGUI::OpenGLPlatform();
    mPlatform->initialise(this);

    setupResources();

    mGUI = new MyGUI::Gui();
    mGUI->initialise(mResourceFileName);
}

createGui方法内部调用了setupResources方法,setupResources方法用来设定资源的路径,默认读取应用程序同目录下的resources.xml配置,配置内容如下: 

1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8"?>

<Paths>
  <Path root="true">F:/MyCode/MyGUI_SVN/Media</Path>
  <Path>F:/MyCode/MyGUI_SVN/Media/MyGUI_Media</Path>
</Paths>

标签Path代表一个路径,带有root="true"属性的,表示这是个根资源路径,底下包含着多个子资源目录,默认不指定递归,每当需要对某个子资源目录添加为程序的资源加载路径时,使用类似如下代码: 

1
addResourceLocation(getRootMedia() + "/Common/Base");

设定资源路径之后,创建Gui,并以资源文件名进行初始化,资源文件名默认为MyGUI_Core.xml,这时会从设定的资源路径去寻找这个文件,在F:\MyCode\MyGUI_SVN\Media\MyGUI_Media路径下找到它,内容如下: 

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<MyGUI type="List">
    <List file="MyGUI_GeneratedFonts.xml"/>
    <List file="MyGUI_Fonts.xml"/>
    <List file="MyGUI_CommonSkins.xml"/>
    <List file="MyGUI_BlueWhiteTheme.xml"/>
    <List file="MyGUI_PointerImages.xml"/>
    <List file="MyGUI_Pointers.xml"/>
    <List file="MyGUI_Layers.xml"/>
    <List file="MyGUI_Settings.xml"/>
</MyGUI>

列表里的XML文件都保存各自不同的信息,具体这些配置是做什么用的,等之后有涉及到再说明。ResourceManager资源管理器加载这些XML配置。下面分析下控件创建的过程,根控件以MyGUI::Gui::getInstance().createWidget来创建,这是个模板方法,代码如下: 

1
2
3
4
5
template <typename T>
T* createWidget(const std::string& _skin, const IntCoord& _coord, Align _align, const std::string& _layer, const std::string& _name = "")
{
    return static_cast<T*>(createWidgetT(T::getClassTypeName(), _skin, _coord, _align, _layer, _name));
}

实质是调用createWidgetT方法,但不同的是这个方法返回的是相应的控件指针。每个参数的意思如下:

  • _type 控件类型(可在WidgetManager::initialise()查看所有的控件类型)
  • _skin 控件皮肤(先查找MyGUI_BlueWhiteTemplates.xml,没有的话再查找MyGUI_BlueWhiteSkins.xml文件)
  • _coord 控件坐标 (左、上、宽、高)
  • _align 控件对齐方式 (定义在Align,包括居左、居右、水平拉伸等等)
  • _layer 控件将被创建到哪一层(所有的层定义在MyGUI_Layers.xml文件)
  • _name 控件名称(可以通过这个名称来寻找控件)

每个控件类都含有MYGUI_RTTI_DERIVED(DerivedType)声明,这是一个宏,用来实现类型RTTI,其含有getClassTypeName方法用来获得类名,即控件类型DerivedType。下一篇继续……​


​更多资料:

  1. MyGUI_Orge官网教程翻译 http://blog.****.net/adfansong/article/category/1566083
  2. MyGUI 学习笔记 http://blog.****.net/jean7155/article/category/1333155
  3. MyGUI 架构介绍 http://blog.****.net/geometry_/article/category/1084100
  4. MyGUI wiki翻译 http://blog.****.net/efulao/article/category/1611683
  5. MyGUI 3.2资源文件的分析 http://blog.sina.com.cn/s/blog_be4206db01018w7v.html