Resive world

Come world to Record life


  • Home

  • Tags

  • Categories

  • Archives

  • Sitemap

  • Search

JQuery分析及实现part5之事件模块功能及实现

Posted on 2016-11-23 | In javascript

JQuery模块分析及其实现第五部分事件部分功能及实现,接第四部分!

Read more »

Makefile常用模板

Posted on 2016-11-22 | In C/C++

写Makefile大概是每一个合格的C/C++程序员的基本功吧,几乎所有C语言写的开源项目都会用Makefile或者类似的Cmake来组织和编译,可见这个是有多重要。不过说白了,Makefile其实下面就简单记录下Makefile的常用模板,并且附带了自己总结的注意点方便以后查找使用。下面这两个模板是用来搞guisan的。

简单项目模板

简单小程序的makefile,一般情况下编译少量的、不分头文件的项目的话用下面的模板就够用了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
CC      =g++
INCLUDE =-I../include
LDFLAGS =-L../lib
LIBS =-lSDL2 -lSDL2_ttf -lSDL2_mixer -lguisan -lSDL2_image
CFLAGS =-g -w -O3 $(INCLUDE) $(LDFLAGS)
CXXFLAGS=$(CFLAGS)

.PHONY:all clean

all :sdlhelloworld sdlaction sdlwidgets

sdlhelloworld:sdlhelloworld.o
$(CC) $(CXXFLAGS) -o sdlhelloworld sdlhelloworld.o $(LIBS)

sdlaction:sdlaction.o
$(CC) $(CXXFLAGS) -o sdlaction sdlaction.o $(LIBS)

sdlwidgets:sdlwidgets.o
$(CC) $(CXXFLAGS) -o sdlsdlwidgets sdlwidgets.o $(LIBS)

clean:
rm -rf sdlhelloworld sdlaction sdlwidgets *.o

上面这段代码摘自我在guisan中写的Makefile,分别编译生成三个可执行文件。下面进行简要分析。

其实个人认为Makefile其实就是加了一些特定格式的shell脚本,只是针对编译这个任务做了特定的处理,Makefile自己并不是编译器,只是将编译命令整合在一起罢了。

makefile核心的部分就是文件依赖了,这些基本都是一些套路,即可执行文件依赖.o文件,.o文件依赖.cpp文件,这个过程其实就是先编译、后链接的具体表现。但是如果严格这样写的话,得写两次依赖,很不方便,因此makefile在编译(链接的过程仍然需要自己写)的过程中能够自动进行推导。说是自动,其实也很简单,就是如果他发现一个叫xxx.o的文件当前目录下没有,那他就会自动的找对应的xxx.cpp(或者xxx.c)文件,并且自动进行编译。也就是说,我们不需要关心如何编译生成.o文件,只需要关心怎么链接生成可执行文件即可。

上面有个问题,就是在自动推导将.cpp文件生成.o文件的过程中,我们如何控制编译参数呢?这里就需要牵涉到一些常量的使用了。

在makefile文件的开头定义了一些常量,这当中有的是make命令默认能够识别的,比如CC(编译器)、CFLAGS(gcc编译参数)、CXXFLAGS(g++编译参数);有些是我们自己定义的或者说是习惯定义的,比如LIBS(链接库),LDFLAGS(库目录),INCLUDE(头文件目录)。在自动推导过程中,makefile会根据CC来确定是将CFLAGS还是CXXFLAGS加入到编译命令中,这样,我们就可以轻松的控制自动推导过程中的编译参数了。我在上面的CFLAGS里加了-g -w -o3,分别表示调试模式、禁止warning和启用O3优化,除此之外,一般还会加INCLUDE和LDFLAGS这两个参数,因为在自动编译的过程中需要必要的包含目录和库目录;而且一般不加LIBS,因为编译的过程中不需要链接库文件。

链接静态库和动态库其实是一样的,只要都是要用-lxxx来链接,不过要记得如果他们不在系统的环境变量的话,就得把他们所在的目录带上-L参数加到LDFLAGS的里。

如果在LIBS里面有多个链接库,需要注意如果这些库当中有依赖关系,也要保证被依赖的要写在后面,比如这里的guisan是依赖于SDL2_image的,我们就得把他挪到SDL2_image前面。。。

还有个.PHONY参数,这个参数又叫伪指令,实际上就是制定了在命令行下输入make指令后能带的参数,当然不写这个基本也没事,用这个的主要目的就是为了防止命令解释器把参数当成同目录下的同名文件。通常得支持all和clean这两个命令。

在编写具体依赖的过程中,由于编译的过程已经由自动推导代劳了,我们只需要编写链接过程的命令,我们需要手动加上CC和CXXFLAGS这样的命令,并且要在最后添加链接库LIBS。

最后写一个clean,自己给自己揩屁股。。。

这样一来,我们就能用make all来编译和链接,make clean来清除了。

在编译的过程也会原封不动的回显出来:

1
2
3
4
5
6
g++ -g -w -O3 -I../include -L../lib   -c -o sdlhelloworld.o sdlhelloworld.cpp
g++ -g -w -O3 -I../include -L../lib -o sdlhelloworld sdlhelloworld.o -lSDL2 -lSDL2_ttf -lSDL2_mixer -lguisan -lSDL2_image
g++ -g -w -O3 -I../include -L../lib -c -o sdlaction.o sdlaction.cpp
g++ -g -w -O3 -I../include -L../lib -o sdlaction sdlaction.o -lSDL2 -lSDL2_ttf -lSDL2_mixer -lguisan -lSDL2_image
g++ -g -w -O3 -I../include -L../lib -c -o sdlwidgets.o sdlwidgets.cpp
g++ -g -w -O3 -I../include -L../lib -o sdlsdlwidgets sdlwidgets.o -lSDL2 -lSDL2_ttf -lSDL2_mixer -lguisan -lSDL2_image

这里可以十分清楚地看到自动推导的编译过程和自己编写的链接过程。

大型项目模板

复杂、较大程序的编译以及打包成库,通常需要下面的模板来弄(以生成静态库为例):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
TARGET  = lib/libguisan.a

AR = ar

DIRS =$(shell find ./src -maxdepth 3 -type d)
SOURCE = $(foreach dir,$(DIRS),$(wildcard $(dir)/*.cpp))
OBJS = $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE)))

INCLUDE =-I./include
CFLAGS = -g -w -O3 $(INCLUDE)
CXXFLAGS= $(CFLAGS)

.PHONY : all clean

$(TARGET) : $(OBJS)
$(AR) cr $(TARGET) $(OBJS)

all : $(TARGET)

clean :
find . -name *.o |xargs rm -f
rm -rf $(TARGET)

简要说明下,首先我们的目的是在lib下生成一个静态库叫guisan,因此我用一个常量来突出下;打包生成静态库的命令是ar,我也用默认的常量来表示下;然后配置自动编译需要的参数,很好理解。

下面就是编译大型项目的关键了,由于文件可能比较多,一个一个写依赖显然很麻烦,而且不好维护,因此这里采用了递归查找的方法。首先是DIRS常量,这其实就是用shell命令写的,他表示递归查找./src目录下三层以内的所有文件夹;在此基础上,SOURCE常量就定义为DIRS下的文件中所有以.cpp为后缀的文件,这实际上就是所有的源文件;最后,OBJS常量定义为SOURCE里的所有文件把.cpp都改为.o后的文件名,这其实就是预测的所有目标文件。。。个人觉得这个小技巧还是非常帅气的。

然后就是打包命令,看一下就好了。

最后在clean里还要用脚本递归删除所有生成的文件。

还有一个问题,就是通常我们可能会在项目主目录下写一个Makefile,这个默认是编译主项目的,不过有时候我们可能会在主目录下加一个demo、或者example之类的小项目作为例子,显然他们应当有自己独立的makefile,但是我们很想在主目录下的Makefile里同时编译他们,怎么弄呢?其实很简单,我们只要把makefile当成shell脚本来用就好了:

1
2
3
4
5
6
7
8
9
10
11
......

all : $(TARGET)
cd demo&&make all&&cd ..
cd examples&&make all&&cd ..

clean :
cd demo&&make clean&&cd ..
cd examples&&make clean&&cd ..
find . -name *.o |xargs rm -f
rm -rf $(TARGET)

最后还有一个小窍门,我们知道在编译大型目录的时候经常编译要等好久,这就很坑爹了,其实make是支持多线程编译的,比如可以用make -j4来指定用四个线程进行编译。不过不要以为线程数越多效果就越好,操作系统课告诉我们多线程计算的效果是受cpu核的个数限制的,并行数显然不会超过cpu核的个数。因此,我们可以用make -j$(nproc)来优雅的确定线程数,其中$(nproc)这个常量就是表示cpu支持的并行进程数,效果谁用谁知道。

JQuery分析及实现part4之DOM操作模块功能及实现

Posted on 2016-11-21 | In javascript

JQuery模块分析及其实现第四部分属性部分功能及实现,接第三部分!

Read more »

JQuery分析及实现part3之属性模块功能及实现

Posted on 2016-11-20 | In javascript

JQuery模块分析及其实现第三部分属性模块功能及实现,接第二部分!

Read more »

SDL2的GUISAN库简介

Posted on 2016-11-19 | In C/C++

简介

GUISAN是一个基于SDL2的开源的GUI控件库,原本是为了一个叫GUICHAN的小游戏而编写的框架。虽然和Qt、C#中的GUI控件没法比,但是他更加简单,可以更好的通过他的代码来进行框架的研究学习。

源码

官方的版本托管在kallisti5的github上,不过由于它是用Sconscript来编译的,对于大多数人来说不是很习惯,因此我把他用makefile重新编译了一遍,把静态库独立出来方便以后的使用,同时顺便删掉了一些文件,并且用doxygen生成了一个文档方便学习。

我把修改后的版本放在了我的github上。

简要分析

GUISAN的include文件夹内容如下:

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
include
├── guisan
│   ├── actionevent.hpp
│   ├── actionlistener.hpp
│   ├── basiccontainer.hpp
│   ├── cliprectangle.hpp
│   ├── color.hpp
│   ├── deathlistener.hpp
│   ├── defaultfont.hpp
│   ├── event.hpp
│   ├── exception.hpp
│   ├── focushandler.hpp
│   ├── focuslistener.hpp
│   ├── font.hpp
│   ├── genericinput.hpp
│   ├── glut.hpp
│   ├── graphics.hpp
│   ├── gui.hpp
│   ├── imagefont.hpp
│   ├── image.hpp
│   ├── imageloader.hpp
│   ├── inputevent.hpp
│   ├── input.hpp
│   ├── keyevent.hpp
│   ├── key.hpp
│   ├── keyinput.hpp
│   ├── keylistener.hpp
│   ├── listmodel.hpp
│   ├── mouseevent.hpp
│   ├── mouseinput.hpp
│   ├── mouselistener.hpp
│   ├── opengl
│   │   ├── openglgraphics.hpp
│   │   ├── openglimage.hpp
│   │   └── openglsdlimageloader.hpp
│   ├── opengl.hpp
│   ├── platform.hpp
│   ├── rectangle.hpp
│   ├── sdl
│   │   ├── sdlgraphics.hpp
│   │   ├── sdlimage.hpp
│   │   ├── sdlimageloader.hpp
│   │   ├── sdlinput.hpp
│   │   ├── sdlpixel.hpp
│   │   └── sdltruetypefont.hpp
│   ├── sdl.hpp
│   ├── selectionevent.hpp
│   ├── selectionlistener.hpp
│   ├── widget.hpp
│   ├── widgetlistener.hpp
│   ├── widgets
│   │   ├── button.hpp
│   │   ├── checkbox.hpp
│   │   ├── container.hpp
│   │   ├── dropdown.hpp
│   │   ├── icon.hpp
│   │   ├── imagebutton.hpp
│   │   ├── label.hpp
│   │   ├── listbox.hpp
│   │   ├── radiobutton.hpp
│   │   ├── scrollarea.hpp
│   │   ├── slider.hpp
│   │   ├── tabbedarea.hpp
│   │   ├── tab.hpp
│   │   ├── textbox.hpp
│   │   ├── textfield.hpp
│   │   └── window.hpp
│   └── x.hpp
└── guisan.hpp

4 directories, 64 files

总体上来说,大概可以分为事件处理、基本控件和显示等辅助部分;对于显示部分,他这里不仅可以使用SDL2,还可以直接使用opengl。最后用一个头文件guisan.hpp对整个框架代码进行统一包含。

src文件夹和include文件夹的内容相互对应,不再细说。

事件处理

GUISAN的事件处理主要基于gcn::Event这个虚基类,派生的类图如下:

作为一个UI库,他设计的事件处理机制比SDL2相对庞大的机制相比已经简化很多了,也更加专注于与用户进行交互的事件。

基础控件

GUISAN的控件基本继承自gcn::Widget这个虚基类,派生的类图如下:

可以看到,控件都是用多重继承,既继承自Widget,又根据任务逻辑继承自各个事件监听器。这当中最常见的应该就是gcn::Container这个类了,这是存放所有其他控件的地方。当然,我们还可以根据需要自定义控件,例如上图中的FFXXX,这是demo里自定义的控件。

其他

除了上面这两个方面,GUISAN还提供了很多辅助的工具,比如gcn::Color,gcn::Exception,gcn::Image等等,以及一些必不可少的与SDL2相关的类。

样例

下面是GUISAN自带的最简单的例子,作为GUISAN框架使用的模板:

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/**
* SDL Hello World example for Guichan.
*/

// Include all necessary headers.
#include <iostream>
#include <guisan.hpp>
#include <guisan/sdl.hpp>
#include "SDL2/SDL.h"

/*
* Common stuff we need
*/
bool running = true;

/*
* SDL Stuff we need
*/
SDL_Window* window;
SDL_Surface* screen;
SDL_Event event;

/*
* Guichan SDL stuff we need
*/
gcn::SDLInput* input; // Input driver
gcn::SDLGraphics* graphics; // Graphics driver
gcn::SDLImageLoader* imageLoader; // For loading images

/*
* Guichan stuff we need
*/
gcn::Gui* gui; // A Gui object - binds it all together
gcn::Container* top; // A top container
gcn::ImageFont* font; // A font
gcn::Label* label; // And a label for the Hello World text

/**
* Initializes the Hello World
*/
void init()
{
/*
* Here we initialize SDL as we would do with any SDL application.
*/
SDL_Init(SDL_INIT_VIDEO);
window = SDL_CreateWindow("guisan SDL2 hello world",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480,
0);

screen = SDL_GetWindowSurface(window);

// We want to enable key repeat
//SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);

/*
* Now it's time for Guichan SDL stuff
*/
imageLoader = new gcn::SDLImageLoader();
// The ImageLoader in use is static and must be set to be
// able to load images
gcn::Image::setImageLoader(imageLoader);
graphics = new gcn::SDLGraphics();
// Set the target for the graphics object to be the screen.
// In other words, we will draw to the screen.
// Note, any surface will do, it doesn't have to be the screen.
graphics->setTarget(screen);
input = new gcn::SDLInput();

/*
* Last but not least it's time to initialize and create the gui
* with Guichan stuff.
*/
top = new gcn::Container();
// Set the dimension of the top container to match the screen.
top->setDimension(gcn::Rectangle(0, 0, 640, 480));
gui = new gcn::Gui();
// Set gui to use the SDLGraphics object.
gui->setGraphics(graphics);
// Set gui to use the SDLInput object
gui->setInput(input);
// Set the top container
gui->setTop(top);
// Load the image font.
font = new gcn::ImageFont("fixedfont.bmp", " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
// The global font is static and must be set.
gcn::Widget::setGlobalFont(font);

// Create a label with test hello world
label = new gcn::Label("Hello World");
// Set the labels position
label->setPosition(280, 220);
// Add the label to the top container
top->add(label);
}

/**
* Halts the application
*/
void halt()
{
/*
* Destroy Guichan stuff
*/
delete label;
delete font;
delete top;
delete gui;

/*
* Destroy Guichan SDL stuff
*/
delete input;
delete graphics;
delete imageLoader;

/*
* Destroy SDL stuff
*/
SDL_DestroyWindow(window);
SDL_Quit();
}

/**
* Checks input. On escape halt the application.
*/
void checkInput()
{
/*
* Poll SDL events
*/
while(SDL_PollEvent(&event))
{
if (event.type == SDL_KEYDOWN)
{
if (event.key.keysym.sym == SDLK_ESCAPE)
{
running = false;
}
if (event.key.keysym.sym == SDLK_f)
{
if (event.key.keysym.mod & KMOD_CTRL)
{
printf("TODO: Toggle full screen!\n");
}
}
}
else if(event.type == SDL_QUIT)
{
running = false;
}

/*
* Now that we are done polling and using SDL events we pass
* the leftovers to the SDLInput object to later be handled by
* the Gui. (This example doesn't require us to do this 'cause a
* label doesn't use input. But will do it anyway to show how to
* set up an SDL application with Guichan.)
*/
input->pushInput(event);
}
}

/**
* Runs the application
*/
void run()
{
while(running)
{
// Poll input
checkInput();
// Let the gui perform it's logic (like handle input)
gui->logic();
// Draw the gui
gui->draw();
// Update the screen
SDL_UpdateWindowSurface(window);
}
}

int main(int argc, char **argv)
{
try
{
init();
run();
halt();
}
/*
* Catch all Guichan exceptions
*/
catch (gcn::Exception e)
{
std::cerr << e.getMessage() << std::endl;
return 1;
}
/*
* Catch all Std exceptions
*/
catch (std::exception e)
{
std::cerr << "Std exception: " << e.what() << std::endl;
return 1;
}
/*
* Catch all Unknown exceptions
*/
catch (...)
{
std::cerr << "Unknown exception" << std::endl;
return 1;
}

return 0;
}

编译的时候注意要将事先编译好的静态库加到PATH里或者写在编译命令里,并且要用-I参数包含头文件夹。

运行输出:

当然,下面这个样例展示了更多的控件:

JQuery分析及实现part2之css部分功能及实现

Posted on 2016-11-19 | In javascript

JQuery模块分析及其实现第二部分css部分功能及实现,接第一部分!

Read more »

JQuery分析及实现part1框架结构

Posted on 2016-11-18 | In javascript

JQuery 模块分析及其实现第一部分!

Read more »

Doxygen工具简单使用

Posted on 2016-11-17 | In C/C++

简述

Doxygen是一款非常方便的文档生成工具,以类似JavaDoc风格描述的文档系统,完全支持C、C++、Java等语言,据说也支持python等。用他不仅可以根据注释生成文档,而且还能利用graphviz工具生成类图以及类中的函数调用关系,并且支持html、latex、rtf等格式的输出。

安装

有apt支持,直接$sudo apt install doxygen即可。

编写注释

即使不是刻意采用doxygen工具的标准语法,我们也是可以用doxygen生成文档的,只是他提供的信息可能不是很完整,分类也不是很恰当。不过如果使用了doxygen支持的类javadoc的注释方法,那么生成的文档就会相当好看了。

关于简单的注释规范可以参考这篇文章: 基于Doxygen的C/C++注释原则。由于最近没有什么需求,暂时不研究。

生成文档

最简单的生成文档的方法,就是指定项目目录,输入$doxygen 项目根目录即可在该目录下生成一个html文件夹和latex文件夹,这里面放的就是该项目的文档。不过这样有一个问题,就是doxygen默认不会去递归整个文件树,而是只查找当前目录下的代码,这就很讨厌了,很多情况下只能找到一两个头文件。。。

为了更好的生成文档,doxygen需要首先生成一个配置文件,利用$doxygen -g 命令来生成一个名叫Doxyfile的文件。这个文件里有诸多选项,包括PROJECT_NAME、PROJECT_BRIEF、OUTPUT_DIRECTORY等直观的配置,当然也有很多配置细节,可以参考doxygen使用总结这篇博文。

有了这个Doxyfile,我们就可以在当中找到下面这段配置:

1
2
3
4
5
# The RECURSIVE tag can be used to specify whether or not subdirectories should
# be searched for input files as well.
# The default value is: NO.

RECURSIVE = NO

把这个NO改成YES即可使doxygen递归整个文件树来查找代码。

这样我们就能进行最简单的项目文档的生成了。

样例

下面是对一个叫guisan的项目利用doxygen进行文档生成的结果:







事实上doxygen不仅能自动生成类的属性以及方法的说明,还能画出非常复杂的类之间的继承与聚合等关系图,在分析大型项目的时候还是非常靠谱的。

Canvas部分知识总结

Posted on 2016-11-15 | In Canvas

Canvas笔记总结!!
接前部分~

Read more »

Canvas知识整理part2

Posted on 2016-11-14 | In Canvas

Html5Canvas笔记整理,接part1篇!!

绘制图形不仅仅是利用线条来实现绘图, 还可以有快捷的绘制图形的办法!

  1. 绘制矩形
  2. 绘制圆弧
  3. 绘制文本
  4. 绘制图片
  5. 绘制动画
Read more »
1…353637…58

574 posts
69 categories
286 tags
© 2024 Companyd
Powered by Hexo
|
Theme — NexT.Muse v5.1.4