使用 Gtk2-Perl 编程/入门
在本章中,我们将逐步介绍两个简单的 Gtk2-Perl 应用程序。我们将介绍一些术语,并演示使用 Gtk2-Perl 进行 GUI 编程的一些基本概念。
use strict;
use warnings;
use Gtk2;
Gtk2->init;
my $window = Gtk2::Window->new;
my $label = Gtk2::Label->new('Hello World!');
$window->signal_connect('delete-event' => sub { Gtk2->main_quit });
$window->add($label);
$label ->show;
$window->show;
Gtk2->main;
use strict;
use warnings;
因为如果还没有人告诉你把这些包含在每个你编写的 Perl 程序的顶部,我现在告诉你。
use Gtk2;
Gtk+ 库包含在 Gtk2 命名空间下。
Gtk2->init;
此方法需要从每个 Gtk2 应用程序调用。它初始化库以供使用,设置诸如颜色映射之类的东西,并连接默认的信号处理程序。此方法还会检查传递给应用程序的命令行参数。以下参数被所有 Gtk 应用程序接受。它们将从参数列表中移除,将其余参数留给您的应用程序处理。
* --gtk-module * --g-fatal-warnings * --gtk-debug * --gtk-no-debug * --gdk-debug * --gdk-no-debug * --display * --sync * --name * --class
您可以像这样隐式调用它,而不是显式调用 init 方法。
use Gtk2 '-init';
my $window = Gtk2::Window->new('toplevel');
my $label = Gtk2::Label->new('Hello World!');
这两行创建新的 **小部件**。小部件是 GUI 的一个元素。窗口、标签、按钮、菜单、图像都是小部件的例子。在这个例子中,我们可以看到窗口小部件的构造函数接受一个参数,告诉它创建一个顶层窗口,标签小部件可以接受一些文本来显示。不同的窗口部件接受不同的构造函数参数。查看 Gtk 文档以了解更多关于特定窗口部件的信息。
$window->add($label);
这行将标签打包到窗口中。窗口小部件是一个容器的例子。容器小部件用于控制其子元素在屏幕上的显示方式。容器小部件具有各种方法,可用于添加子元素。这里我们使用一个非常简单的例子来在窗口中显示一个标签。
$label ->show;
$window->show;
你能猜到这几行做了什么吗?
$window->show_all;
我们可以这样说。
Gtk2->main;
每个 Gtk 应用程序都必须有一个对 main 方法的调用。这将控制权转交给 Gtk 主循环,该循环等待事件发生,然后采取适当的操作。
$window->signal_connect('delete-event' => sub { Gtk2->main_quit });
这行并没有被遗忘。在这里,我们将一个回调附加到窗口的 'delete-event' 上。回调是对函数的引用。在这个例子中,我们使用了一个匿名子例程,它中断 Gtk 的主循环,并将控制权返回给程序。在调用 Gtk2->main 之后的任何代码都将被执行,在我们的例子中,程序只是关闭。如果我们没有将此回调连接到 'delete-event',窗口将关闭,但我们的程序仍然会在主循环中运行,用户将无法轻松退出应用程序。(您可以使用 Ctrl+c 来终止它。)
在运行此脚本或阅读下面的解释之前,请通读该程序并看看您是否能说出它做了什么。如果您认为自己已经弄清楚了,请尝试运行它。当您按下 Alt+s 或 Alt+x 时会发生什么?当您更改窗口大小后,您会注意到什么?
use warnings;
use strict;
use Glib qw(TRUE FALSE);
use Gtk2 '-init';
my $window = Gtk2::Window->new;
$window->set_title('Quick Note');
$window->signal_connect('delete-event' => sub { $_[0]->destroy });
$window->signal_connect('destroy' => sub { Gtk2->main_quit });
my $hbox = Gtk2::HBox->new;
my $label = Gtk2::Label->new('Note');
my $entry = Gtk2::Entry->new();
my $save_btn = Gtk2::Button->new_with_mnemonic('_Save');
$save_btn->signal_connect('clicked' => \&save_note, $entry);
my $exit_btn = Gtk2::Button->new_with_mnemonic('E_xit');
$exit_btn->signal_connect('clicked' => sub { $window->destroy });
$hbox->pack_start($label , FALSE, FALSE, 0);
$hbox->pack_start($entry , TRUE , TRUE , 0);
$hbox->pack_start($save_btn, FALSE, FALSE, 0);
$hbox->pack_start($exit_btn, FALSE, FALSE, 0);
$window->add($hbox);
$window->show_all;
Gtk2->main;
sub save_note {
my $save = shift;
my $entry = shift;
my $text = $entry->get_text;
$entry->set_text('');
open my $OUTFILE, '>>notes.txt'
or die "could not open notes.txt for appending";
flock $OUTFILE, 2;
print $OUTFILE join '|', time, $text . "\n";
close $OUTFILE;
}
在这个脚本中,我们介绍了一些新的窗口部件。HBox 是一个容器窗口部件,我们用它来控制窗口中其他窗口部件的布局。另外两个是 Entry 和 Button 窗口部件,它们允许用户与程序交互。
use Glib qw(TRUE FALSE);
Glib 是 Gtk 的依赖项。我们在程序中包含这行代码来访问常量 TRUE 和 FALSE。您只需使用布尔值 1 和 0,但为了清晰起见,我们将使用 TRUE 和 FALSE。
$window->signal_connect('delete-event' => sub { $_[0]->destroy });
$window->signal_connect('destroy' => sub { Gtk2->main_quit });
您可以看到,我们在绑定到信号/事件时采用了稍微不同的方法。在这里,我们在 'delete-event' 发生时调用窗口的 destroy 方法(它发出 'destroy' 信号)。连接到 'destroy' 信号,我们有指示退出 Gtk 主循环的运行。使用这种方法是因为我们希望程序在应用程序窗口以任何方式销毁时退出,而不仅仅是当用户按下窗口标题栏中的关闭按钮时。稍后,我们将连接到 $exit_btn 的 'clicked' 信号以销毁窗口,从而退出应用程序。
尽管使用相同的方法连接,但事件和信号之间 **确实** 存在区别(这些信号与 Unix 系统信号不同,也不是使用它们实现的,但术语非常相似)。稍后我们将对此进行更多介绍。
my $save_btn = Gtk2::Button->new_with_mnemonic('_Save');
这将创建一个新的 Button 窗口部件,它带有标签 'Save'。请注意,我们在这里使用了方法 'new_with_mnemonic'。这将设置键盘快捷键 Alt+s 以触发按钮上的 'clicked' 事件。'Save' 中的 'S' 也将被下划线,以提醒最终用户存在键盘快捷键。
因为我们使用的是 Perl 绑定,所以我们可以说
my $save_btn = Gtk2::Button->new('_Save');
这是因为 Perl 绑定意识到我们实际上想要使用底层 C 库中提供的 'new_with_mnemonic' 构造函数。其他也具有 'new_with_mnemonic' 或 'new_with_label' 构造函数的窗口部件通常也允许您这样做。许多人仍然更喜欢使用 'new_with_label' 或 'new_with_mnemonic' 构造函数,无论如何。
$save_btn->signal_connect('clicked' => \&save_note, $entry)
这里我们使用对子例程的引用,而不是匿名子例程。当回调被执行时,$entry 将作为参数传递。
my $exit_btn = Gtk2::Button->new_with_mnemonic('E_xit');
$exit_btn->signal_connect('clicked' => sub { $window->destroy });
现在应该清楚地了解这两行代码的作用。
$hbox->pack_start($label , FALSE, FALSE, 0);
$hbox->pack_start($entry , TRUE , TRUE , 0);
$hbox->pack_start($save_btn, FALSE, FALSE, 0);
$hbox->pack_start($exit_btn, FALSE, FALSE, 0);
这几行将我们的界面窗口部件打包到 HBox 窗口部件中。语法如下
$container->pack_start($widget, $expand, $fill, $padding)
如果 $expand 参数为 FALSE,则容器将围绕窗口部件缩小。如果 $expand 参数为 TRUE,则容器将围绕窗口部件展开以填充其父级分配的空间。当 $fill 参数为 TRUE 时,容器内的额外空间将分配给子窗口部件。当设置为 FALSE 时,额外空间将作为容器窗口部件的填充。$fill 参数只有在 $expand 参数也为 TRUE 时才起作用。
在我们的例子中,$entry 对象被打包,$expand 和 $fill 参数设置为 TRUE。当您更改窗口大小后,围绕 $entry 的框将展开,允许 $entry 填充剩余区域。其他对象被打包,$expand 和 $fill 值为 FALSE,它们将保持其自然大小。
sub save_note {
my $save = shift;
my $entry = shift;
my $text = $entry->get_text;
$entry->set_text('');
open my $OUTFILE, '>>notes.txt'
or die "could not open notes.txt for appending";
flock $OUTFILE, 2;
print $OUTFILE join '|', time, $text . "\n";
close $OUTFILE;
}
如果您不清楚这里发生了什么,建议您浏览 Perl 编程.
通过两个应用程序,我们介绍了使用 Gtk2-Perl 进行 GUI 编程的一些基本概念。
- 信号
- 回调
- 窗口部件
- 打包
本章的目的只是让您熟悉这些概念。我们将在接下来的几章中更详细地介绍它们。