大家知道, LINUX 下有两大桌面环境,一个是 KDE (K Desktop Enviroment),基于 Qt 图形库,另一个是 GNOME (GNU Network Object Model Environment),基于 Gtk+ 图形库.由于 Qt 库不遵从 GPL 协议,并且获取许可证很不方便,而 GNOME 则很不错,所以应该避免使用 Qt 库和 KDE.
从用户的角度看, GNOME 是一个集成桌面环境和应用程序的套件;从程序员的角度来看,它是一个应用程序开发框架(由数目众多的实用函数库组成).即使用户不运行 GNOME 桌面环境,用 Gnome 编写的应用程序一样可以正常运行.但是这些应用程序是可以很好的和 Gnome 桌面环境集成的.Gnome 的开发结构使开发一致的,易用的和可互操作的应用程序成为可能.
下面我来讲一下如何用 perl 来写一个基于 Gnome 的程序,虽然很简单,但却是完整的,包含有标准的通用界面.
GNOME拥有许多的库和组件,但现在我们只需知道其中的两件:GTK+ 和 GNOME. 你可能听说过 TK ,这是另一种可以使用 perl 来写图形界面应用程序的工具包.TK 的任务是告诉 X Server 如何来画按钮,菜单,对话框等等,并返回一些信号来触发相应的 perl 函数来处理一些变化.作为 perl 程序和 X Server 之间的媒介.而 GTK+ 做着类似的工作,但事实证明,它工作得更加漂亮,它可以提供所有的图形元素,诸如:按钮,文本标签,文本输入等等,使用循环等待事件的方式实现交互.GNOME 库建立了一个抽象于 GTK+ 之上的一层,提供了更高级的图形对象,比如:主应用程序窗口, about 窗口,按钮面板,对话框,颜色与字体选择框,并且提供了与其他 GNOME 环境程序(如spelling checkers, calculators)协作的接口.
此外,Glade 是一种在 Linux 下的类似 Visual Basic 的快速开发环境(IDE),可以使用它方便的写出 perl 代码的 GNOME 应用程序,但为了能明白代码的含义,请继续往下看.
现在先来看一下"hello world"程序,这里有两个版本,第一个使用 GTK+ ,第二个使用 GNOME :
------------------------------------
1 #!/usr/bin/perl -w
2
3 use strict;
4 use Gnome;
5
6 my $NAME = 'Hello World';
7
8 init Gnome $NAME;
9
10 my $w = new Gtk::Window -toplevel;
11
12 my $label = new Gtk::Label "Hello, world";
13 $w->add($label);
14
15 show_all $w;
16
17 main Gtk;
--------------------------
第4行 载入主 GNOME 模块;这样会同时载入 GTK+ 模块
第8行 设置本次会话及注册本程序所需的所有东西,这里我们把程序的名称传给了 init 方法
第10行 创建主窗口;这是一个顶层的窗口,也就是该应用程序的根窗口
第12行 创建一个消息标签,包含文字"hello, world";所有要放在窗口上的文字必须放在 Gtk::Label 对象中
第13行 此时,$label 对象什么事也没有做,也不知道该放在什么地方,所以这里我们使用window对象的 add 方法来加入这个对象
第15行 显示 window 对象,及其他所包含的对象,在这里,就是 label 对象,我们使用了window 对象的 show_all 方法,注意,该方法并不是真正的在屏幕上显示窗口,而是做一个初始的说明,
第17行 使用 GTK+ 的 main event loop 来运行,这里才是真正的开始画窗口出来,并且等待事件的发生.此后,程序的控制权就交给了 GTK+ 了,程序的运行取决于用户的行为了,而你的程序就处于被动状态(等待激活)了.
再来看看GNOME的版本
------------------------------------
1 #!/usr/bin/perl -w
2
3 use strict;
4 use Gnome;
5
6 my $NAME = 'Hello World';
7
8 init Gnome $NAME;
9
10 my $app = new Gnome::App $NAME, $NAME;
11
12 my $label = new Gtk::Label "Hello, world";
13 $app->set_contents($label);
14
15 show_all $app;
16
17 main Gtk;
------------------------------------
大多数都一样,但改了几行:
第10行 原先只是创建一个 window 对象,而现在则是提高了一个层次,我们说这里已经创建了整个应用程序,我们把应用程序的名字两次传递给了 new 方法,第一次说明 window 对象的 title 的值;第二次则是在 GNOME 环境中注册该应用程序
第13行 为什么是 set_contents 而不是 add 了呢? 原因在于 GTK+ 在 window 中放置图形元素(widget)的方式基于容器(container)的思想,简单的放置,只允许你在 window 中放一个窗口小部件(widget),所幸的是,一些 widget 允许包含其他的 widget.
现在,你可能会发现,当你使用 close 按钮时,perl 程序并没有结束,而我们必须使用类似 ^C 的方式来结束运行;而使用 GNOME 时,GNOME 应用程序在收到window 对象的 close 消息时, GNOME 会给我们发送一个信号,但这不是真正的 UNIX 核心所发出的信号,而我们需要安装信号的处理句柄来干净的结束程序的运行,可以这样做:
------------------------------------
my $app = new Gnome::App $NAME, $NAME;
signal_connect $app 'delete_event',
sub { Gtk->main_quit; return 0 };
------------------------------------
这里,我们把 delete_event 信号和一个匿名函数关联,也就是说,当程序接受到 Gnome 环境送来的 delete_event 信号时,触发执行该匿名函数,而该函数则调用标准的 main_quit 方法来结束 main loop, 最后真正的退出运行.
下面我们来加上菜单栏.前面已经说过,Gnome 构建于 Gtk+ 之上,它提供许多标准的东西使我们在写程序的时候更方便,更具可扩展性.下面的程序建立了一个标准的菜单栏:
------------------------------------
$app->create_menus(
{type => 'subtree',
label => '_File',
subtree => [
{type => 'item',
label => 'E_xit',
pixmap_type => 'stock',
pixmap_info => 'Menu_Quit'
}
]
},
{type => 'subtree',
label => '_Help',
subtree => [
{type => 'item',
label => '_About...',
pixmap_type => 'stock',
pixmap_info => 'Menu_About'
}
]
}
);
------------------------------------
在这里,我们传递了一系列的匿名哈西结构给 create_menus 方法,每一个就是一个菜单项(tab),type 为 subtree 时说明该项具有子菜单;为 item 时说明该项为一个菜单内容;label 的值也就是菜单上显示的提示文字,而下划线后的字母则为键盘快捷键,如 _About 则说明按下 Alt-A 就激活该项;关键字 subtree 指定该项的子菜单的内容,是一个包含一系列匿名哈西菜单结构的匿名数组;子菜单项类型为 item 时,显示时使用一个图标作为按钮,在这里,我们使用 GNOME 图标库 stock ,使用关键字 pixmap_type 来指定;而 pixmap_info 所指定的值即为鼠标移到该图标按钮上时显示的提示文字.
原来你应该是用 perl hello.pl 的方式运行该程序的吧?那么现在再试试使用 LANG=fr_FR perl hello.pl ,有没有看到菜单文字被翻译成了法语? 再试试 pt_PT , de_DE , el_GR ,很神奇吧!
上面的程序只创建了菜单,但是还不能做些什么,那么看一下:
------------------------------------
{type => 'item',
label => 'E_xit',
pixmap_type => 'stock',
pixmap_info => 'Menu_Quit',
callback => sub {Gtk->main_quit; return 0 }
}
------------------------------------
现在,我们把退出程序时要做的事情封装在一个匿名函数里并作为 callback 的值,就这样简单!在 about 项里,则可以使用 callback=>&about_box ,另外创建一个自己的 about_box 函数:
------------------------------------
sub about_box {
my $about = new Gnome::About $NAME, "v1.0",
"(C) Simon Cozens, 2000", ["Simon Cozens"],
"This program is released under the same terms as Perl itself";
show $about;
}
------------------------------------
Gnome::About 类给我们提供了一个接口,我们只需将本应用程序的名字,版本号,版权信息,作者名字(使用匿名数组,因为可能有不只一个作者)等传递给它即可,最后使用 show 方法将之显现.运行时,你会看到点击 about 会弹出 about 信息窗口,含有 OK 按钮,点击该按钮则自动关闭该窗口.
一般的应用程序还有工具栏和状态栏,那么,与上面的类似,加入以下代码:
------------------------------------
$app->create_toolbar(
{
type => 'item',
label => 'Exit',
pixmap_type => 'stock',
pixmap_info => 'Quit',
hint => "Click here to quit",
callback => sub { Gtk->main_quit },
}, {
type => 'item',
label => 'About...',
pixmap_type => 'stock',
pixmap_info => 'About',
hint => "More information about this app",
callback => &about_box
}
);
------------------------------------
其中,hint 所指定的值即为鼠标移到该图标按钮上时显示的提示文字,接着是状态栏:
------------------------------------
my $bar = new Gnome::AppBar 0,1,"user" ;
$bar->set_status(" Welcome ");
$app->set_statusbar( $bar );
------------------------------------
先创建一个 AppBar 对象,然后使用 set_status 方法来初始化状态栏中显示的内容,然后使用 app 对象的 set_statusbar 方法来显示它.
好啦,现在来完整的看一下这个程序:
------------------------------------
#!/usr/bin/perl -w
use strict;
use Gnome;
my $NAME = 'Hello World';
init Gnome $NAME;
my $app = new Gnome::App $NAME, $NAME;
signal_connect $app 'delete_event', sub { Gtk->main_quit; return 0 };
$app->create_menus(
{type => 'subtree',
label => '_File',
subtree => [
{type => 'item',
label => 'E_xit',
pixmap_type => 'stock',
pixmap_info => 'Menu_Quit',
callback => sub { Gtk->main_quit; return 0 }
}
]
},
{type => 'subtree',
label => '_Help',
subtree => [
{type => 'item',
label => '_About...',
pixmap_type => 'stock',
pixmap_info => 'Menu_About',
callback => &about_box
}
]
}
);
$app->create_toolbar(
{
type => 'item',
label => 'Exit',
pixmap_type => 'stock',
pixmap_info => 'Quit',
hint => "Click here to quit",
callback => sub { Gtk->main_quit },
}, {
type => 'item',
label => 'About...',
pixmap_type => 'stock',
pixmap_info => 'About',
hint => "More information about this app",
callback => &about_box
}
);
my $label = new Gtk::Label "Hello, world";
$app->set_contents($label);
my $bar = new Gnome::AppBar 0,1,"user" ;
$bar->set_status(" Welcome ");
$app->set_statusbar( $bar );
show_all $app;
main Gtk;
sub about_box {
my $about = new Gnome::About $NAME, "v1.0",
"(C) Simon Cozens, 2000", ["Simon Cozens"],
"This program is released under the same terms as Perl itself";
show $about;
}
------------------------------------
到这里,我们已经写出了一个十分简单的基于 perl 的 Gnome 环境下的应用程序,很简单吧?下次,我再来讲解更进一步的内容(如何使用其他图形元素等).