手机内存卡系统技术架构探索

这一周来为了验证手机内存卡系统的技术框架可行性,探索和初步学习了各种图形界面和编程语言等各种选择的适用性。包括:tinycore linux, fltk, gtk, qt, zenity, c, c++, bash, glade 等。最后决定用 tinycore linux + gtk2 + bash + zenity 来实现手机内存卡系统的 Demo 0.1 版本。

2011年01月04日 星期二
浏览 C++ 标准库
sudo apt-get install libstdc++6 libstdc++6-4.5-doc stl-manual
c++ 标准库html文档 file:///usr/share/doc/libstdc++6-4.5-doc/libstdc++/html/index.html
c++ 标准模板库文档 file:///usr/share/doc/stl-manual/html/index.html
简而言之,C++ 标准库就是一系列成熟的通用工具、算法、容器,我们可以根据需要直接使用,而不必重新“发明轮子”。

“c++ 标准库“和“c++ 标准模板库”的关系,后者属于前者的一部分:
据《C++标准程序库》(侯捷/孟岩 译)上讲:自从1998年 C++ Standard 定案以后,C++ 程序库便有了大幅扩充。原先为大家所熟知、标准规格定案前酝酿已久的 STL (Standard Template Library,标准模板程序库),不再被单独对待,而是被纳入整个 C++ 标准程序库(Standard Library)。

因为我不是系统的学习 C++ ,而只是想用 C++ 快速的做一个简单的东西,所以不必浏览整个库,遇到有需要时在网上搜一搜,抄抄利用标准库的代码完成功能就行了。

下面浏览一下 FLTK 图形界面编程的大概内容。

sudo apt-get install fltk1.1-doc libfltk1.1 libfltk1.1-dev fltk1.1-games fluid

编译 FLTK 程序的命令:
fltk-config --compile test.cpp 或者 g++ $(fltk-config --cxxflags) -lfltk -o test test.cpp

一个有关中文的补丁(已经存在于最新版本中1.3.*):
svn co http://svn.easysw.com/public/fltk/fltk/branches/branch-1.3/ fltk-1.3 --revision 6662
http://www.fltk.org/applications/str.php?L2154+P0+S-2+C0+I0+E0+Q
fltk-1.3-svn6662-gbk_and_xim.patch http://www.fltk.org/applications/strfiles/2154/fltk-1.3-svn6662-gbk_and_xim.patch

2011年01月05日 星期三

用最新的 FLTK1.3 测试了一下,不知是哪步出了问题,不能使那个 hello.cxx 例子程序显示中文,可能是要对字体设置一下;那个 fonts.cxx 在上面那个框选了中文字体后可以显示,但是左下部分那个字体中文名称却不能显示出来。在代码中修改了下面两段进行测试:

 strcpy(label, "代码页只支持单字节和双字\n");

从字体的整数索引得到系统字体名称:

  for (i = 0; i < k; i++) {
    int t; const char *name = Fl::get_font_name((Fl_Font)i,&t);
    cout<

试了两天,放弃 FLTK。

试了下 GTK,GTK1 不准备考虑了,因为这是过时的东西,各方面的应用支持肯定很少了。QT是 C++ 写的,不过说体积比 GTK2 大不少,暂不考虑。GTK 是 c 语言写的,不过用的是面向对象的构建技术。 Gtkmm 对 GTK 作了一个 c++ 的封装,但是体积可能要加个3~5M。暂时用 GTKmm 来写图形界面。

大概安装了下面一些相关的东西:

sudo apt-get install libgtk2.0-common libgtk2.0-dev libgtk2.0-doc gtk2.0-examples libgtkmm-2.4-1c2a libgtkmm-2.4-dev 
libgtkmm-2.4-doc libgtkmm-utils-dev libgtkmm-utils2

编译 GTK 程序:c

cc `pkg-config --cflags --libs gtk+-2.0` hello.c -o hello

编译 gtkmm 程序:c++

g++ program.cc -o program `pkg-config --cflags --libs gtkmm-2.4`

试了 gtk 下的 entry.c 和 gtkmm 下面的 simple entry example ,不论是 zh_CN.GBK 或者是 zh_CN.UTF-8 的 locale 下面,只要源文件是 UTF-8 编码,其中的中文字符串均能正常显示出来。而且输入框中复制、粘贴、输入中文都没有问题(GBK locale 下暂没有试输入法输入中文,Ubuntu 10.10 在这个 locale 下进入不了桌面,进入 Fluxbox 窗口管理器时 ibus 输入法不知怎么调不出来)。

2011年01月06日 星期四
在 ubuntu 10.10 下的 bash 4.1.5(1) 下,
/abs-guide/internal.html#EVALRISK 的例子不用 eval 仍然是正确的,eval 在这里是必要的吗?

bash$ command_string="ps ax"
bash$ process="ps ax"
bash$ eval "$command_string" | grep "$process"

后面那句换成:

bash$ $command_string | grep "$process"

也行。

2011年01月07日 星期五
浏览: GTK+ 2.0 Tutorial http://library.gnome.org/devel/gtk-tutorial/2.22/book1.html

http://library.gnome.org/devel/gtk-tutorial/2.22/c24.html
GTK 基本上是面向对象 API。尽管完全用 C 语言写成,但使用了类和回调函数的思路。

GTK 信号处理函数

gulong g_signal_connect( gpointer      *object,
                         const gchar   *name,
                         GCallback     func,
                         gpointer      func_data );

可以这样理解:用户通过图形界面的一个按纽(object)发送一个特定类型的指令(name)给一个函数(func),并且附带了函数需要的参数(func_data)。
这可以设计一个通用的事件处理调度器:事件发送者,事件,事件内容,事件处理者

gtk-tutorial 2.22 中 helloworld 例子程序的一些要点:
1、为 GTK 程序写一个 static void destroy( GtkWidget *widget, gpointer data ) 函数是必须的,不然不会退出程序;
2、static gboolean delete_event( GtkWidget *widget, GdkEvent *event, gpointer data ),调用窗体关闭事件的函数,返回值为 TURE 不会关闭窗体,返回值为 FALSE 会关闭窗体,但是仍不会关闭程序,只有定义了上面的 destroy() 函数后,才会自动调用 destroy() 关闭程序;
3、两种信号处理函数的区别(初步理解,可能有误):
g_signal_connect (button, "clicked", G_CALLBACK (hello), "大家好!\n"); 这个函数一般是自己定义回调函数的(这里是hello()),回调函数的第一个参数就是发出事件的窗体构件,第二个参数就是我们要传给回调函数的参数(就是这里的"大家好!\n",原例子中没有传递参数给回调函数,是 NULL)。
   
g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_widget_destroy), window); 这个信号处理函数的回调函数一般是 GTK 窗体自己的,不需要我们定义,它的回调函数与我们自定义的回调函数不同,这里的回调函数的第一个参数是一个窗体构件或其它对象,第二个参数应该是信号发出者的窗体构件自己(没有查到资料,应该是这样吧?),与上面那个事件处理函数的回调函数的参数是相反的(所以处理函数名字后面加了一个 swapped)。如这个例子,点击事件是按纽构件发出的,传给回调函数的构件是主窗体,作用是让回调函数关掉主窗体,而不是关掉按纽。如果把这里的 window 换成 button,就会让主窗体 window 里的 button 消失,如果把 window 变成 NULL,点击 button 程序就会报“不会GTK构件”的错。
如果把 g_signal_connect_swapped 换成前面的 g_signal_connect,那么传给 gtk_widget_destroy 的回调函数的第一个参数就变成了 button,window 成了这个回调函数的第二个参数。起作用的是第一个参数 button,所以这里就只能关闭 button ,让 button 消失,而不会让 window 消失。
void gtk_widget_destroy (GtkWidget *widget); GTK 系统定义的 gtk_widget_destroy 回调函数只接受第一个参数,把 g_signal_connect_swapped 换成前面的 g_signal_connect 的第一个参数就是 button ;
4、我们自定义的 hello 回调函数的第二参数是 gpointer data ,这是一个指向数据的指针,可能 gpointer 里面有自己的封装,不管我们传进去什么数据都行,例如,传的是上面的字符串“大家好!\n” g_signal_connect (button, "clicked", G_CALLBACK (hello), "大家好!\n"); 那么在 hello 回调函数里面这样写:

g_print ("%s",(char *)data);

可能 g_print 也是对 c 里面的 printf() 格式输出函数的封装,所以用 printf ("%s",(char *)data); 也行。 可能 GTK 系统在什么地方 include "stdio.h" 了的,所以这里可以直接使用 printf() 函数。

整个 helloworld 程序的构成大概就是:
1、自定义事件的回调函数,如:static void hello( GtkWidget *widget, gpointer data )
2、main 函数用传入的参数初始化程序,gtk_init (&argc, &argv);
3、建立主窗体,window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
4、设置主窗体的事件处理(什么事件交给哪个回调函数),如:g_signal_connect (window, "delete-event", G_CALLBACK (delete_event), NULL);
5、设置主窗体自身的一些属性,如这里的边宽:gtk_container_set_border_width (GTK_CONTAINER (window), 10);
6、建立子窗体对象,如这里的按纽:button = gtk_button_new_with_label ("您好!Hello World");
7、设置按纽的事件处理(什么事件交给哪个回调函数),如:g_signal_connect (button, "clicked", G_CALLBACK (hello), "大家好!\n");
8、把子窗体对象按纽装进主窗体中: gtk_container_add (GTK_CONTAINER (window), button);
9、依次显示子窗体对象,主窗体:gtk_widget_show (button); gtk_widget_show (window);
10、让程序无限循环,根据上面的程序规则检测和处理构件接收到的事件(信号?):gtk_main ();
11、接收到关闭程序信号,退出程序:return 0;

另外,GTK 编程除了官方的教程和例子,还有一个已经翻译为中文的实例教程很不错: GTK+程序设计教程:http://zetcode.com/tutorials/gtktutorial/chinese/
2011年01月10日 星期一
用 glade3 制作 GTK 程序的图形界面的方法,glade3 把界面设计和逻辑代码完全分离。制作好的图形界面可以在各种语言写成的 GTK 程序中引用,如用 c 语言写的(http://blog.csdn.net/xbwee/archive/2009/03/28/4032652.aspx ,另:Ubuntu 10.10 中的Glade 3.6.7默认是 GtkBuilder 的 xml 格式图形界面文件,不需要由 libglade 的格式转换过来):

    builder = gtk_builder_new ();
    gtk_builder_add_from_file (builder, "tutorial.xml", NULL);
    window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
    gtk_builder_connect_signals (builder, NULL);
 
    g_object_unref (G_OBJECT (builder));

也可以把图形界面文件制作成 c 源文件中的字符串,用字符串的形式构建图形界面,但这时描述图形界面的字符串仍然可在二进制源码中提取出来,并不能达到封装图形界面源码的目的。(如果要封闭图形界面的源码,可以手工在代码中创建图形界面,好像老版本的 glade2 是直接生成源代码的,但没有测试)。下面是用字符串形式引用图形界面的 c 语言界面描述字符串(http://stackoverflow.com/questions/2787202/tool-to-convert-glade-or-xml-file-to-c-source):

/*
You don't need a tool. Just write a script in your favorite scripting language to format your glade file as a C string literal:

For example, let's call it glade_file.c:
*/
const gchar *my_glade_file = 
    ""
        ""
            ""
        ""
    "";

/*
Compile glade_file.c into your program, then do this when you build your interface:
*/

extern const gchar *my_glade_file;
result = gtk_builder_add_from_string(builder, my_glade_file, -1, &error);

把 glade3 生成的 gtkbuilder 格式的 xml 文件每行首尾加上双引号的命令:

sed -e "s/^/\"/g" -e "s/$/\"/g" -i filename

经过几天的技术构架测试,发现自己对图形界面的布局设计实在生疏,决定放弃使用 GTK 图形界面,用 GTK 下面的 Zenity 来实现向导式的图形界面,初步决定采用如下技术构架来做工具系统 Demo 0.1 版。
1)、基于 TinyCore Linux 图形界面版本作为基础平台;
2)、学习 GTK 用 c 实现面向对象的编程思想,以面向对象的思维方式用 Bash 脚本进行逻辑代码编写,因为系统比较小,只使用其中封装的概念;
3)、用 Zenity 作为图形界面;
4)、用混淆器和加密器对源码进行封闭;
5)、以后视情况用 c 或 c++,gtk 或 qt 重写。

上述决定基于如下考虑:
1)、系统的本意是以尽量少的用户干预用图形界面实现功能的傻瓜化操作,如果需要复杂的图形界面交互,那么使用上将变得复杂,与初衷相违。所以基于向导式的 Zenity 图形界面应该可以满足需要,只是界面只能是朴素的 Linux dialog 风格;
2)、逻辑上流程处理用 Bash 是可以满足需要的,而且,工具的主要方式是调用 Linux 系统平台已经有的各种命令和工具进行处理,用 Bash 脚本调用将更合适;
3)、尽管针对 Bash 脚本的混淆器和加密器并不能实现非常安全的闭源,但是任何封闭源码措施只能对遵守规则的人有效,如果一个软件真的非常普及,产生了破解的商业利益等,再高明的加密手段都应该是可破解的。加密的意思只是告诉人,我这个东西的源码还不想开放。