跳到內容

使用 Gtk2-Perl 程式設計/入門

來自華夏公益教科書,開放的世界,開放的書籍

在本章中,我們將逐步介紹兩個簡單的 Gtk2-Perl 應用程式。我們將介紹一些術語,並演示使用 Gtk2-Perl 進行 GUI 程式設計的一些基本概念。


Hello World!

[編輯 | 編輯原始碼]
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 程式設計的一些基本概念。

  • 訊號
  • 回撥
  • 視窗部件
  • 打包

本章的目的只是讓您熟悉這些概念。我們將在接下來的幾章中更詳細地介紹它們。

華夏公益教科書