用 Perl 进行 GNOME 编程(转中国perl协会)

软件和网站开发以及相关技术探讨
回复
admin
论坛管理员
帖子: 8
注册时间: 2005-03-25 22:56

用 Perl 进行 GNOME 编程(转中国perl协会)

#1

帖子 admin » 2005-04-21 11:30

译者/作者:saladjonk
出处:中国Perl协会 FPC(Foundation of Perlchina)
作者:Simon Cozens
原名:Perl_com Programming GNOME Applications with Perl
原文:http://www.perl.com/pub/a/2000/10/gnome.html
发表:October 16,2000
请保护作者的著作权,维护作者劳动的结晶。


GNOME 是一个 UNIX 下的桌面系统,它提供了在 Unix 下编写图形程序的环境,支持鼠标拖放,程序间通信,CORBA 组件(也就是在 Windows 里面的 OLE),CORBA 组件是一组标准的,美观的编程接口,以及所有其它现代的图形应用程序应该包含的特征。所有这些都能在 Perl 的完成,也就是 Perl 程序员能够用GNOME 库来编写整洁漂亮的应用程序,但是这儿也有一点小小的问题。。。

% perldoc GNOME
No documentation found for "GNOME".

没有GNOME编程的帮助文档!。我最近需要写一个基于 GNOME 库的图形界面应用程序,就遇到了这个问题。我不得不自己解决所有的问题。所以我写了这个教程,来帮助亲爱的读者朋友,使你们不用也事事亲为。首先,放松一下,我们将创建一个极为简单,但是却很完整,很标准的 GNOME 应用程序。

GNOME系统构成简介:

整个 GNOME 系统非常的复杂,由很多的库和组件组成。幸运的是,教学及日常大部分的程序,你都只需要知道其中的两个部分: GTK+ 和 GNOME.

可能你已经听说过了 TK,另一个 Perl 程序员经常用到的图形工具库.Tk 所扮演的角色主要是与 X 窗口服务器通信,告诉它怎样画一个按钮,菜单,控制栏,对话框等,根据用户的点击情况执行相应的 Perl 子程序.Tk库一方面用到了 Perl 的便捷,另一方面又不得不与虽然强大但是却很原始难懂的X窗口系统打交道.

GTK+ 也完成相似的任务,但是却更简单。GTK+ 提供所有的窗口,按钮,文本标记,文本输入框,一切我们的程序用的到的图形控件。而且,严格的说还提供主窗口事件等待循环,以便监视用户的动作。

GNOME 库则在 GTK+ 的基础上进行了更高一个层次的抽象,为我们提供更高层的图形对象,比如主应用程序窗口,弹出窗口,按钮板,对话框,颜色字体选择器,甚至还提供与GNOME环境中其它部分的粘合交互通信,比如拼写检查,计算器及其它应用程序资源。
* 这里值得指出,有一个VB风格的集成图形编程环境,叫 Glade,支持像 VB 一样拖放控件。它能自动产生 Perl 代码,用 Glade 你会发现开发图形界面十分简单,但是我们还是建议你应该把这个教程看下去,这样你才能看懂那些产生的 perl 代码。

Hello,World

这里我们展示经典的 “Hello,World” 程序的两个版本,一个用 GTK+ 实现,一个用 GNOME 实现

GTK+ 版:

代码: 全选

   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;
在第四行,装载 Gnome 模块,这样就将一并装载其下的 GTK+ 模块。第八行注册并初始化这个程序,分配我们在该程序中需要的资源,init 语句后面根这个程序的标题,它将显示在标题栏。
在第十行,创建主窗口,也就是最顶层的窗口,它不是任何其它组件的子窗口。然后,我们创建内容为 “Hello,world” 的消息文本标签,任何我们想要在窗口中显示的文本都必须是派生于 Gtk::Label 对象的实例,在第十二行我们就是这样做的,并把派生的实例命名为 $label。但是仅仅是第十二行的一条语句没有任何作用,它仅仅创建了文本,却不能显示在任何地方显示出来。未了在窗口中显示它,我们必须用窗口的 add 方法把这个标签实例添加到待显示列。

然后,我们决定在程序启动的时候应该显示上面。如果我们想要显示窗口中包含的所有的东西,在这里也就是那个消息文本标签,我们就需要调用窗口的 show_all 方法。注意,调用了 show_all 后窗口仍不会被显示,show_all 方法只是申明了程序运行的时候待显示的内容。

最后,main Gtk 这条语句将把程序的控制权交给 GTK+ 的主事件等待循环,GTK+ 的主事件等待循环首先在屏幕上绘出窗口,在窗口里显示出文本,然后就等待用户的点击等动作了。

*一旦运行到 main Gtk 语句,程序的状态就不确定了,以后发生的一切都要依赖于用户的动作。不象程序员通常编程时为整个程序设计好所有的运行流程,现在我们必须要适应这个被动的角色,要更具用户的动作来做出相应的响应,而要实现这一点,正如我们在后面看到的一样,多少得靠子函数。再次申明,当程序运行到 main Gtk 的时候,我们的工作就算完成了,而 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;
和上面的Gtk+版一样长,而且大部分语句都是一样的,下面这处有改动:

10 my $app = new Gnome::App $NAME, $NAME;

不象在Gtk+中我们申明创建一个窗口,在这里我们从更高一层声明创建一整个程序。把程序的名称传递给 new,而且传递了两个。第一个是作为窗口的标题,第二个则是在 GNOME 的环境中对该程序进行注册。 把文本标签添加到窗口的语句也有变化:

13 $app->set_contents($label);

为什么在这里用 set_content 方法而不用 add 方法呢?这是由 GTK+ 把图形元素(也就是控件)显示在窗口中的方法决定的,GTK+ 如何显示图形元素由包含该图形元素的上一层元素,或称容器决定。通常,一个窗口只能放一个控件。但幸运的是很多控件都能自身包含其它的控件。而我们上面的那条语句则是申明,在窗口中只能放置文本标签类控件。 到现在你可能已经注意到了一点,当你用 GTK+ 编写好程序并运行它,然后点击程序中的退出按钮你却并不能退出,而不得用 Ctrl+C 或类似的强制方法退出。GNOME 中不存在这样的问题。当 GNOME 程序从窗口管理器中收到退出的通知时,它会给自己发送一个信号,当然不是 UNIX 内核中发送的那种信号,而是纯粹的 GNOME 特色的信号。我们需要捕捉这个信号,并且给这个信号编写一个相应的信号处理程序来退出程序。下面的语句就是完成这个功能:

代码: 全选

    my $app = new Gnome::App $NAME, $NAME;
    signal_connect $app 'delete_event',
                         sub { Gtk->main_quit; return 0 };
我们指定了一个程序来处理 “delete event“ 信号,“delete event”信号意味这退出当前子程序,返回上一级程序。我们用一个匿名子程序来捕捉并处理它—-GTK+ 中的 main_quit 方法,这个方法将终止程序的主事件等待循环。

现在我们的程序就会完全的退出了。但是现在这个程序还不能做什么。
添加菜单条

像前面提到的,GNOME 优于 GTK+ 的地方在于,它提供更多我们在应用程序中需要用到的标准控件。现在来为我们的程序添加一个标准的菜单。在 signal_connect 语句后添加下面的内容

代码: 全选

 $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 传递了一系列匿名的散列,每个散列都代表菜单中的一项。每个散列中的 subtree 关键字指明当前项下面还有子项。在散列的 label 关键字的值中,我们可以在需要定义为快捷键的字母前面加一个下划线。如上,Alt+ F就将打开菜单的 File 项。subtree 关键字的值其实可以是一个包含很多匿名散列的匿名数组,但是上面的程序段中,两个 subtree 关键字的值我们都只指定了一个匿名散列。

表征子项的散列中的 type 关键字都被赋于“item”,表示该子项为一个普通项,不像它的上一级包含子菜单。菜单的每个选项都可以有自己的图标。这里通过 “pixmap_type=>stock” 语句我们调用了 GNOME 自带的图标库,我们还用了 Menu_Quit 和 Menu_About 方法来实现标准的退出的弹出窗口功能。
现在,重新运行你的程序,你就将会看到菜单条了。还想来点特别炫的东西吗?我说过 GNOME 可以为你做到一切。用下面的语句启动程序:

LANG=fr_FR perl hello.pl

一切仍然正常,仍有菜单条,但是文字却变成了法语!实现这个转变的代码在哪里呢?GNOME 为你做了一切。LANG 后根pt_PT就将变成葡萄牙语,跟 de_DE 就将变成德语,跟 el_GR (如果你有那个字库的话)就将变成希腊语。神奇吧! 这里还有一个小问题:那个菜单好像出来能看不能用。由于我们已经知道了如何退出一个 GTK+ 程序,那么让我们首先让菜单的Exit项起作用。把这个菜单项的散列内容照下面修改:

代码: 全选

                {type => 'item',
                 label => 'E_xit',
                 pixmap_type => 'stock',
                 pixmap_info => 'Menu_Quit',
                 callback => sub {Gtk->main_quit; return 0 }
                }

正如我们前面说到的一样,当我们选中这个选项之后,GNOME 会自动执行我们为那个选项编写的子程序。
添加一个弹出窗口

现在再改写其它菜单项的程序,为它们添加弹出窗口的功能。同样,GNOME 将会为我们完成这一切。我们为 About… 指定一个子程序的引用,

代码: 全选

{type => 'item',
                 label => '_About...',
                 pixmap_type => 'stock',
                 pixmap_info => 'Menu_About',
                 callback => chunk73189886chunk#38;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 方法来同样显示这个弹出窗口。当点击 “OK” 按钮的时候,这个弹出窗口就关闭。

添加更多的东西

GNOME 系统还提供另外两个很有特色的交互项:工具栏和状态栏。我们首先添加工具栏,在添加菜单的语句后添加下面的语句:

代码: 全选

$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 => chunk73189426chunk#38;about_box
    }
   );
我们同样给 creat_toolbar 函数传递了一系列的匿名散列,而且散列的很多关键值都是你熟悉的。hint 关键值的内容定义当鼠标旋停在按钮上的时候显示的内容。而每个选项绑定的处理函数和选项的图标都和前面一样。 接着添加状态栏:

代码: 全选

    my $bar = new Gnome::AppBar 0,1,"user" ;
    $bar->set_status("   Welcome   ");

    $app->set_statusbar( $bar );
首先,创建一个 AppBar 实例,它是一个程序的状态栏。然后用 set_status 定义状态栏的初始信息。状态栏到现在就已经存在了,但是还不会在屏幕上被显示出来。因为还没有指定它是属于整个 GNOME 环境中那个应用程序。所以我们用 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 => chunk73189196chunk#38;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 => chunk73189196chunk#38;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;
    }

=head1 Summary
这样,我们用 GNOME/Perl 创建了我们的第一个应用程序。它拥有标准的 GNOME 界面,有标准的菜单栏,工具栏,状态栏,弹出窗口。它无论是看起来,还是真正运行起来都像一个真正的 GNOME 应用程序。总共只有大约 70 行 Perl 代码。

下一次,我们会创建一个更有用的应用程序,一个食谱管理器。在里面我们将会用到一些稍微复杂一些的控件,比如容器,文本输入域,滚动条和列表框。
admin
论坛管理员
帖子: 8
注册时间: 2005-03-25 22:56

用 Perl 编写 GNOME 应用程序(二)

#2

帖子 admin » 2005-04-21 11:34

译者/作者:SaladJonk
出处:中国Perl协会 FPC(Foundation of Perlchina)
作者:Simon Cozens
原名:Perl_com Programming GNOME Applications with Perl-Part 2
原文:http://www.perl.com/pub/a/2000/10/gnome.html
发表:2000年11月28日
请保护作者的著作权,维护作者劳动的结晶。


上个月的文章,我们主要讨论了如何用 GTK+ 和 GNOME 创建一个简单的 “Hello World ” 程序。这次,我们将创建一个复杂一些的程序,一个能保存和提取食谱的程序。
菜谱管理程序

在我们开始写程序前,先让我们来看看怎么设计它。首先设计用户界面,然后再考虑如何用程序实现这个界面。

当设计用户界面的时候,我们要考虑怎样给用户提供一个有用又直观的浏览数据的界面,这个界面不能太拥挤,给人压抑感。当我们使用一个软件的时候,怎样才能用起来简单顺手呢?这个问题涉及两方面:软件能提供的交互手段和我们能看到的数据。

从数据显示方面考虑,我觉得最好的组织这些菜谱的方法是把他们弄成一个列表,就像真正的菜谱书上印刷出的表格一样。上移和下移滚动条可以浏览每道菜的名字,点击名字则就可以看到相应那道菜的全部内容。我们还可以在名字后面附加一些其它的信息,我觉得该条该道菜被添加的日期和烹饪它需要的时间是比较有用的内容。

然后再来看有什么交互功能,这得由工具栏上的按钮决定。我觉得程序应该实现的一个很有用的功能是:由用户输入手头上有的原料,然后程序可以告诉用户这些原料可以烹饪出哪些道菜。程序还应该能把我们输入的菜谱保存起来,以后能再次打开,所以 “Save” 和 “Open” 按钮是必要的。当然,添加新菜谱的功能是必须的,所以,还应该有 “Add” 按钮,注意 “Delete” 按钮是不太需要的,因为我们很少会删除已经输入的菜谱,即使你要添加这个功能,也很简单。最后,我们还需要一个退出的按钮。

以上说的就构成程序的主界面,它看起来应该像这样:

现在,我们再来考虑数据的存储问题。我们需要保存每道菜的名字,输入日期和烹饪它需要的时间。如果我们想通过原料来搜索可以做哪些菜,那么还应该将原料存起来。浏览所存储的原料列表要方便,还要允许用户可以自定义一些东西,实现个性化。

最初,我打算把所有的食谱都放到一个 SQL 关系型数据库中,但最后由于两个原因放弃了:首先,把每道菜的名字和需要的原料关联起来,会带来一些不必要的复杂性。其次,GNOME 应用程序通常是把数据保存在XML文档中,这样能在很容易在不同程序间共享。所以,最后我决定把用户自定义的选项和原料的清单都放在一个 XML 文档中,然后把整个菜谱放在一个单独的文件里。
主界面

现在我们已经设计好了程序的主界面,可以实现它了。像以前的程序一样,我们首先实现程序的菜单条和工具栏。

代码: 全选

 #!/usr/bin/perl -w
        use strict;
        use Gnome;

        my $NAME    = 'gCookBook';
        my $VERSION = '0.1';

        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 => '_New',
                 pixmap_type => 'stock',
                 pixmap_info => 'Menu_New'
                },
                {
                 type => 'item',
                 label => '_Open...',
                 pixmap_type => 'stock',
                 pixmap_info => 'Menu_Open'
                },
                {
                 type => 'item',
                 label => '_Save',
                 pixmap_type => 'stock',
                 pixmap_info => 'Menu_Save'
                },
                {
                 type => 'item',
                 label => 'Save _As...',
                 pixmap_type => 'stock',
                 pixmap_info => 'Menu_Save As'
                },
                {
                 type => 'separator'
                },
                {
                 type => 'item',
                 label => 'E_xit',
                 pixmap_type => 'stock',
                 pixmap_info => 'Menu_Quit',
                 callback => sub { Gtk->main_quit; return 0 }
                }
                 ]
           },
           { 
          type => 'subtree',
          label => '_Edit',
          subtree => [
                {
                 type => 'item',
                 label => 'C_ut',
                 pixmap_type => 'stock',
                 pixmap_info => 'Menu_Cut',
                },
                {
                 type => 'item',
                 label => '_Copy',
                 pixmap_type => 'stock',
                 pixmap_info => 'Menu_Copy'
                },
                {
                 type => 'item',
                 label => '_Paste',
                 pixmap_type => 'stock',
                 pixmap_info => 'Menu_Paste'
                }
                 ]
           },
           {
          type => 'subtree',
          label => '_Settings',
          subtree => [
                {
                 type => 'item',
                 label => '_Preferences...',
                 pixmap_type => 'stock',
                 pixmap_info => 'Menu_Preferences',
                 callback => chunk73965706chunk#38;show_prefs
                }
                 ]
           },
           {
          type   => 'subtree',
          label  => '_Help',
          subtree => [
                {type => 'item', 
                 label => '_About...',
                 pixmap_type => 'stock',
                 pixmap_info => 'Menu_About',
                 callback => chunk73965706chunk#38;about_box
                }
             ]
           }
          );

    $app->create_toolbar(
           {
            type     => 'item',
            label    => 'Cook',
            pixmap_type => 'stock',
            pixmap_info => 'Search',
            hint     => 'Find a recipe by ingedients'
           },
           {
            type     => 'item',
            label    => 'Add',
            pixmap_type => 'stock',
            pixmap_info => 'Add',
            hint     => 'Add a new recipe'
           },
           {
            type     => 'item',
            label    => 'Open...', 
            pixmap_type => 'stock',
            pixmap_info => 'Open',
            hint     => "Open a recipe book" 
           },
           {
            type     => 'item',
            label    => 'Save', 
            pixmap_type => 'stock',
            pixmap_info => 'Save',
            hint     => "Save this recipe book" 
           },
           { 
            type     => 'item',
            label    => 'Exit',
            pixmap_type => 'stock',
            pixmap_info => 'Quit',
            hint     => "Leave $NAME",
            callback  => sub { Gtk->main_quit;}
           }
          );

    $app->set_default_size(600,400);

    my $bar = new Gnome::AppBar 0,1,"user" ;
    $bar->set_status("");
    $app->set_statusbar( $bar );

    show_all $app;

    main Gtk;

    sub about_box {
      my $about = new Gnome::About $NAME, $VERSION,
      "(C) Simon Cozens, 2000", ["Simon Cozens"], 
      "This program is released under the 
          same terms as Perl itself";
      show $about;
      }

列表

接着,我们应该显示出每道菜的名字。通常用 CList 控件实现。然而,标准的 Gtk 库中的 CList 控件不太好用,你只能向里面输入数据,而不能读取数据。所以你不得不把数据单独放到一个数组里面。CList 控件提供其下列表的自动排序,你只需要在列表名栏中点击一下就可以了,

我觉得每次要弄列表栏都不太方便,于是我自己写了一个模块,叫 Gtk::HandyClist,它封装了上面提到的所有功能。(你如果你想试试这个模块,可以从 CPAN 上下载它,注意要下0.02版,因为我们后面用到的 hide 方法只有在 0.02 版中才有)
为了实现这个列表,首先要准备好待显示的数据!像下面这样创建一个比较抽象的数组:

代码: 全选

my @cookbook = (
                [ "Frog soup", "29/08/99", "12"],
                [ "Chicken scratchings", "12/12/99", "40"],
                [ "Pork with beansprouts in a garlic
                    butter sauce and a really really long name
                    that we have to scroll to see",
                  "1/1/99", 30],
                [ "Eggy bread", "10/10/10", 3]
               );
然后装载上面提到的模块:

use Gtk::HandyCList;

列表要可以上下滚动,所以我们把列表放到一个能处理滚动条的控件中:Gtk::ScrolledWindow。

代码: 全选

my $scrolled_window = new Gtk::ScrolledWindow( undef, undef );
  $scrolled_window->set_policy( 'automatic', 'always' );
现在开始创建列表,首先,指定列表的名字,然后指定列表每行的长宽尺寸。

代码: 全选

 my $list = new Gtk::HandyCList qw(Name Date Time);
  $list->sizes(350,150,100);
我们希望当点击列表的标题栏的时候能对列表进行排序,所以我们需要在模块中定义好排序的方法。如果你不定义,默认的方法可以对字母和数字进行排序,但是我们希望它能用我们提供的子函数排序。我们还给列表设置了阴影效果,看起来漂亮些。

代码: 全选


$list->sortfuncs("alpha", chunk73965246chunk#38;sort_date, "number");
  $list->set_shadow_type('out');
现在把抽象数组定义的数据传给列表:

代码: 全选

$list->data(@cookbook);
然后,把列表加到那个可滚动的窗口中,并告诉程序,最顶层的窗口是这个可滚动的窗口。

代码: 全选

$scrolled_window->add($list);
  $app->set_contents($scrolled_window);
最后,我们还要处理当用户点击菜名的时候发来的信号,收到信号后显示这道菜的全部内容。

代码: 全选


$list->signal_connect( "select_row", chunk73964976chunk#38;display_recipe);

当然,我们还需要实现上面用到的两个子程序,sort_data和display_recipe。后者现在暂时不考虑,先搞定前者。下面是我写的第一个子程序的实现代码,因为我是个英国人,所以程序风格有点英国化:

代码: 全选

 sub sort_date {
          my ($ad, $am, $ay) = ($_[0] =~ m|(\d+)/(\d+)/(\d+)|);
          my ($bd, $bm, $by) = ($_[1] =~ m|(\d+)/(\d+)/(\d+)|);
          return $ay <=> $by || $am <=> $bm || $ad <=> $bd;
        }


至于程序风格的本地化就留给读者了。

到现在,你手头的这个程序已经可以分列显示出菜名,该道菜输入的时间,和烹饪它需要的时间了。运行它,点击标题栏看看自动排序的功能,然后把窗口的大小调整一下,看看会发生什么。
显示每道菜的详细内容
现在让我们来处理每道菜详细内容的显示问题。这个问题就有点复杂了。首先,我们必须要把每道菜的相关内容存起来,这里把菜名,录入时间,烹饪需要的时间都存在名为 @cookbook 的数组里,所以,这个原来创建的抽象数组每项内容都要加一列:

代码: 全选

    my @cookbook = (
        [ "Frog soup", "29/08/99", "12", 
          "Put frog in water. Slowly raise water temperature 
           until frog is cooked."],
        [ "Chicken scratchings", "12/12/99", "40", 
          "Remove fat from chicken, and fry 
   under a medium grill"],
        [ "Pork with beansprouts in a garlic butter sauce 
           and a really really long name that we have to
           scroll to see",
          "1/1/99", 30, 
  "Pour boiling water into packet and stir"],
        [ "Eggy bread", "10/10/10", 3, 
  "Fry bread. Fry eggs. Combine."]
           );
由于不需要一次就在列表框中显示出所有的数据,所以传给Gtk::HandyCList模块的数据要作修改:

代码: 全选


- my $list = new Gtk::HandyCList qw(Name Date Time);
 + my $list = new Gtk::HandyCList qw(Name Date Time Recipe);
 + $list->hide("Recipe");

(如果你不记得上面的句型,这里说一下:前面带减号的行表示的内容将不被显示,而带加号的行表示的内容将被显示)。

现在,我们把每道菜的相关数据都保存在我们自己定义的数据结构中了,如果我们想要浏览这些数据怎么办呢?可以用 Gnome::Less 这个控件,它是根据 Unix 中的 less 实用程序命名的。本是一个文件浏览器,但我们也可以用它来浏览字符。
现在停一停,想一想下一步要做什么。我们需要捕捉用户双击菜名的信号,然后应该要弹出一个窗口,里面由 Gnome::Less 控件来显示用户所点击那道菜的详细内容。还要允许用户关闭这个弹出窗口。我们把鼠标点击的信号同 display_recipe 子函数绑定起来,下面是这个子函数的代码:

代码: 全选

sub display_recipe {
      my ($clist, $row, $column, $mouse_event) = @_;
      return unless $mouse_event->{type} eq "2button_press";
当用户点击时,会收到信号传给子函数的参数。参数中的第一项指明发出信号的是哪个控件,这里是 HandyCList 控件。这一点将决定后面的参数内容。假如发出信号的是HandyCList 控件,那么后面的参数就是用户点击内容所在的行和列,及一个 Gtk::Gdk::MouseEvent 实例来指明用户点击的类型,是双击还是单击。这里,我们只处理双击的情况,对应的 Gtk::Gdk::MouseEvent 的值为”2button_press”。如果用户的动作不是双击,那么返回:

代码: 全选

 my %recipe = %{($clist->data)[$row]};
如果我们知道点击处所在的行,我们就可以从 HandyCList 控件中,通过 data 方法提取出那行的内容。Data 方法是一个用来提取数据和输入数据的方法,用这个方法我们能很方便的存储数据和显示数据。我们也可以用它来显示列表中的数据,每一行都作为一个散列的引用被存储,然后我们根据引用找到数据。

代码: 全选


my $recipe_str = $recipe{Name}."\n";
      $recipe_str .= "-" x length($recipe{Name})."\n\n";
      $recipe_str .= "Cooking time : $recipe{Time}\n";
      $recipe_str .= "Date created : $recipe{Date}\n\n";
      $recipe_str .= $recipe{Recipe};
接着,产生我们要显示的字符串,利用被引用的散列的值。

代码: 全选


 my $db = new Gnome::Dialog($recipe{Name});
      my $gl = new Gnome::Less;
      my $button = new Gtk::Button( "Close" );
      $button->signal_connect( "clicked", sub { $db->destroy } );
目前为止,我们这个弹出窗口包含了三个部分:弹出的对话框(对话框的标题栏显示对应那到道菜的名称)的框架,对话框的页面,其上显示用户点击那道菜的具体内容,一个关闭按钮,当用户点击的时候,对话框会被关掉。

代码: 全选


   $db->action_area->pack_start( $button, 1, 1, 0 );
      $db->vbox->pack_start($gl, 1, 1, 0);
弹出的对话框由两部分组成:包含动作的位于底部的动作域,也就是放按钮的位置,还有就是上部的显示信息的文本域。因此,我们把设计的按钮放到动作域,而显示数据的Less 控件放到文本域。

代码: 全选

  $gl->show_string($recipe_str);
      show_all $db;
    }

最后,把要显示的字符串传给页面,然后显示对话框。菜谱的详细内容就被显示出来了

我们学到哪里了,下面的教程将讲解什么?

到现在,这个程序的完整代码,点击这里 获得。

目前,我们还只是处理一些静态的数据,把它们硬编码进程序,而实际生活中这是行不通的。后面的教程我们将要介绍如何删除和添加菜谱,同时还将把所有的数据存在磁盘上的XML文件中。一旦所以的这些完成了,一个菜谱管理程序的核心部分就完成了,在教程的最后部分,我们还将加入更多的功能,比如根据输入原料来判断能烹饪那些菜品。
最后的注意事项

自从我上个月写了第一篇教程后,有几个朋友给我写了信,说到它们的 GNOME 版程序不能运行。如果你也遇到这个问题,那么你可能需要安装最新版的 Gnome.pm 模块。在CPAN上的模块不是最新的,在这个地址去下最新的 http://projects.prosa.it/gtkperl

我支持“GNOME 是 Unix 的桌面系统”的说法,KDE 也很好的提供 UNIX 下相同的桌面环境,只是很长时间来人们因为它的 TrollTech 和 QPL 许可证而不得不放弃使用它。同时,像Sun和IBM等大公司都在投入资金开发 GNOME,力图使它成为 Unix 下的正统桌面系统,所以谁是 Unix 下桌面系统不难理解。

现在很多人都高兴的看到那些大公司也在为开发 KDE 的社团投钱。(引用 http://www.kde.org/announcements/gfresponse.html 上的话,“现在我们经常被问到 KDE 开发人员是否也会成立一个想 GNOME 基金一样的基金?答案是,不会,绝对不会。“) KDE是除了 GNOME 的另一个好选择,很明显,我更喜欢 GNOME,但是像 http://segfault.org/ 上说到:”KDE-GNOME之间的战争,双方都还没输!)
vitacy
帖子: 133
注册时间: 2007-05-11 8:47
联系:

#3

帖子 vitacy » 2007-09-30 15:43

不错,不错.顶
头像
eexpress
帖子: 58428
注册时间: 2005-08-14 21:55
来自: 长沙

#4

帖子 eexpress » 2007-09-30 15:56

要pdf地址呢。要gnome2的。
● 鸣学
x_programmer
帖子: 9
注册时间: 2007-10-25 7:20

Re: 用 Perl 进行 GNOME 编程(转中国perl协会)

#5

帖子 x_programmer » 2009-04-02 12:00

为什么我执行你的第一段GTK的代码,就会有异常呢??

*************************
saku@HP520:~/scripts$ sudo ./gnome.pl
./gnome.pl: 3: use: not found
./gnome.pl: 4: use: not found
./gnome.pl: 6: my: not found
init: 非法的运行级别: Gnome
键入’init --help'获取更多信息
./gnome.pl: 10: my: not found
./gnome.pl: 12: my: not found
./gnome.pl: 13: Syntax error: "(" unexpected

*************************

是我哪里配置的不对么??
tusooa
帖子: 6548
注册时间: 2008-10-31 22:12
系统: 践兔
联系:

Re: 用 Perl 进行 GNOME 编程(转中国perl协会)

#6

帖子 tusooa » 2009-12-12 11:50

x_programmer 写了:为什么我执行你的第一段GTK的代码,就会有异常呢??

*************************
saku@HP520:~/scripts$ sudo ./gnome.pl
./gnome.pl: 3: use: not found
./gnome.pl: 4: use: not found
./gnome.pl: 6: my: not found
init: 非法的运行级别: Gnome
键入’init --help'获取更多信息
./gnome.pl: 10: my: not found
./gnome.pl: 12: my: not found
./gnome.pl: 13: Syntax error: "(" unexpected

*************************

是我哪里配置的不对么??
perl ./gnome.pl

代码: 全选

] ls -ld //
回复