├── .gitignore ├── 10a-Cell-renderer-text.pl ├── 3a-A-basic-window.pl ├── 3b-A-better-window.pl ├── 4a-Adding-buttons.pl ├── 4b-Adding-button-functions.pl ├── 5a-Fun-with-labels.pl ├── 6a-Checkbox-Basics.pl ├── 6b-Radiobox.pl ├── 6c-Spinbox.pl ├── 6d-Toggle-Button.pl ├── 7b-Layout-Grid.pl ├── 8a-Display-Progress-bar.pl ├── 8b-Spinner.pl ├── 8c-Switch.pl ├── 8d-Separators.pl ├── 9a-cairo-drawing.pl ├── 9b-cairo-animation.pl ├── Perl Gtk3 Tutorial.odt ├── Perl Gtk3 Tutorial.pdf ├── README.md ├── mandelbrot-fractal.pl └── pong.pl /.gitignore: -------------------------------------------------------------------------------- 1 | blib/ 2 | .build/ 3 | _build/ 4 | cover_db/ 5 | inc/ 6 | Build 7 | Build.bat 8 | .last_cover_stats 9 | Makefile 10 | Makefile.old 11 | MANIFEST.bak 12 | META.yml 13 | MYMETA.yml 14 | nytprof.out 15 | pm_to_blib 16 | -------------------------------------------------------------------------------- /10a-Cell-renderer-text.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use feature ':5.10'; 6 | use Gtk3 '-init'; 7 | use Glib qw/TRUE FALSE/; 8 | 9 | my $window = Gtk3::Window->new('toplevel'); 10 | $window->set_title("Cell Renderer Example"); 11 | $window->set_position("mouse"); 12 | $window->set_default_size(250, 100); 13 | $window->set_border_width(5); 14 | $window->signal_connect (delete_event => sub { Gtk3->main_quit }); 15 | 16 | my $data = Gtk3::ListStore->new( 'Glib::Int', 'Glib::String', 'Glib::String' ); 17 | $data->set( $data->append, 0, 1, 1, 'First bit'); 18 | $data->set( $data->append, 0, 2, 1, 'second' ); 19 | $data->set( $data->append, 0, 3, 2, 'third' ); 20 | my $tree = Gtk3::TreeView->new($data); 21 | 22 | my $spinner = Gtk3::CellRendererSpin->new; 23 | $spinner->set_property( editable => TRUE ); 24 | my $adj = Gtk3::Adjustment->new( 0, 0, 100, 1, 10, 0 ); 25 | $spinner->set_property( adjustment => $adj ); 26 | $spinner->signal_connect( edited => \&on_amount_edited, $data ); 27 | my $col0 = Gtk3::TreeViewColumn->new_with_attributes( 'Number', $spinner, text => 0 ); 28 | $tree->append_column($col0); 29 | 30 | my $col1 = Gtk3::TreeViewColumn->new_with_attributes( 'Text', Gtk3::CellRendererText->new, text => 1 ); 31 | $tree->append_column($col1); 32 | 33 | my $ren_text = Gtk3::CellRendererText->new(); 34 | $ren_text->set_property('editable', TRUE); 35 | $ren_text->signal_connect (edited => \&cell_edited, $data); 36 | my $col2 = Gtk3::TreeViewColumn->new_with_attributes( 'Ed Text', $ren_text, text => 2 ); 37 | $tree->append_column($col2); 38 | 39 | $window->add($tree); 40 | $window->show_all; 41 | Gtk3->main; 42 | 43 | sub on_amount_edited { 44 | my ( $widget, $path, $value, $model ) = @_; 45 | my $path_str = Gtk3::TreePath->new($path); 46 | my $iter = $model->get_iter($path_str); 47 | $model->set( $iter, 0, int($value) ); 48 | } 49 | 50 | sub cell_edited { 51 | my ($cell, $path, $value, $model) = @_; 52 | my $path_str = Gtk3::TreePath->new($path); 53 | my $iter = $model->get_iter($path_str); 54 | $model->set($iter, 2, $value); 55 | } 56 | 57 | 58 | -------------------------------------------------------------------------------- /3a-A-basic-window.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use diagnostics; 6 | use feature ':5.14'; 7 | use Gtk3 '-init'; 8 | 9 | my $window = Gtk3::Window->new('toplevel'); 10 | $window->show_all; 11 | Gtk3->main; 12 | 13 | -------------------------------------------------------------------------------- /3b-A-better-window.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use diagnostics; 6 | use feature ':5.14'; 7 | use Gtk3 '-init'; 8 | 9 | my $window = Gtk3::Window->new('toplevel'); 10 | $window->set_title("My Title"); 11 | $window->set_position("mouse"); 12 | $window->set_default_size(400, 50); 13 | $window->set_border_width(20); 14 | $window->signal_connect (delete_event => sub { Gtk3->main_quit }); 15 | 16 | $window->show_all; 17 | Gtk3->main; 18 | -------------------------------------------------------------------------------- /4a-Adding-buttons.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use diagnostics; 6 | use feature ':5.14'; 7 | use Gtk3 '-init'; 8 | 9 | use Glib qw/TRUE FALSE/; 10 | 11 | sub quit_function { 12 | say "Exiting Gtk3"; 13 | Gtk3->main_quit; 14 | return FALSE; 15 | } 16 | 17 | my $window = Gtk3::Window->new('toplevel'); 18 | $window->set_title("My Title"); 19 | $window->set_position("mouse"); 20 | $window->set_default_size(400, 50); 21 | $window->set_border_width(20); 22 | $window->signal_connect (delete_event => \&quit_function); 23 | 24 | my $button1 = Gtk3::Button->new("Quit"); 25 | $button1->signal_connect (clicked => \&quit_function); 26 | 27 | my $button2 = Gtk3::Button->new("Another Quit"); 28 | $button2->signal_connect (clicked => \&quit_function); 29 | 30 | my $hbox = Gtk3::Box->new("horizontal", 5); 31 | 32 | $hbox->pack_start($button1, TRUE, TRUE, 0); 33 | $hbox->pack_start($button2, TRUE, TRUE, 0); 34 | 35 | $hbox->set_homogeneous (TRUE); 36 | 37 | $window->add($hbox); 38 | 39 | $window->show_all; 40 | Gtk3->main; 41 | -------------------------------------------------------------------------------- /4b-Adding-button-functions.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use diagnostics; 6 | use feature ':5.14'; 7 | use Gtk3 '-init'; 8 | 9 | use Glib qw/TRUE FALSE/; 10 | 11 | #### DECLARATIONS 12 | 13 | my $window = Gtk3::Window->new('toplevel'); 14 | $window->set_title("My Title"); 15 | $window->set_position("mouse"); 16 | $window->set_default_size(400, 50); 17 | $window->set_border_width(20); 18 | $window->signal_connect (delete_event => \&quit_function); 19 | 20 | my $button1 = Gtk3::Button->new("Quit"); 21 | $button1->signal_connect (clicked => \&quit_function); 22 | 23 | my $button2 = Gtk3::Button->new("Say Hello"); 24 | $button2->signal_connect (clicked => \&say_something, "Hello"); 25 | 26 | my $button3 = Gtk3::Button->new("Say Goodbye"); 27 | $button3->signal_connect (clicked => \&say_something, "Goodbye"); 28 | 29 | my $hbox = Gtk3::Box->new("horizontal", 5); 30 | $hbox->set_homogeneous (TRUE); 31 | $hbox->pack_start($button1, TRUE, TRUE, 0); 32 | $hbox->pack_start($button2, TRUE, TRUE, 0); 33 | $hbox->pack_start($button3, TRUE, TRUE, 0); 34 | 35 | my $vbox = Gtk3::Box->new("vertical", 5); 36 | $vbox->add($hbox); 37 | 38 | my $label = Gtk3::Label->new("Am I connected?"); 39 | $vbox->add($label); 40 | 41 | $window->add($vbox); 42 | $window->show_all; 43 | 44 | #### FUNCTIONS 45 | 46 | sub quit_function { 47 | say "Exiting Gtk3"; 48 | Gtk3->main_quit; 49 | return FALSE; 50 | } 51 | 52 | sub say_something { 53 | my ($button, $userdata) = @_; 54 | $label->set_label( $userdata ); 55 | return FALSE; 56 | } 57 | 58 | Gtk3->main; 59 | -------------------------------------------------------------------------------- /5a-Fun-with-labels.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use diagnostics; 6 | use feature ':5.14'; 7 | use Gtk3 '-init'; 8 | 9 | use Glib qw/TRUE FALSE/; 10 | 11 | #### DECLARATIONS 12 | 13 | my $window = Gtk3::Window->new('toplevel'); 14 | $window->set_title("My Title"); 15 | $window->set_position("mouse"); 16 | $window->set_default_size(400, 50); 17 | $window->set_border_width(20); 18 | $window->signal_connect (delete_event => \&quit_function); 19 | 20 | my $button1 = Gtk3::Button->new("Twist"); 21 | $button1->signal_connect (clicked => \&say_something, "twist"); 22 | 23 | my $button2 = Gtk3::Button->new("red"); 24 | $button2->signal_connect (clicked => \&say_something, "red"); 25 | 26 | my $button3 = Gtk3::Button->new("weird"); 27 | $button3->signal_connect (clicked => \&say_something, "weird"); 28 | 29 | my $hbox = Gtk3::Box->new("horizontal", 5); 30 | $hbox->set_homogeneous (TRUE); 31 | $hbox->pack_start($button1, TRUE, TRUE, 0); 32 | $hbox->pack_start($button2, TRUE, TRUE, 0); 33 | $hbox->pack_start($button3, TRUE, TRUE, 0); 34 | 35 | my $vbox = Gtk3::Box->new("vertical", 5); 36 | $vbox->add($hbox); 37 | 38 | my $label = Gtk3::Label->new("Am I connected?"); 39 | $vbox->add($label); 40 | 41 | $window->add($vbox); 42 | $window->show_all; 43 | 44 | #### FUNCTIONS 45 | 46 | sub quit_function { 47 | say "Exiting Gtk3"; 48 | Gtk3->main_quit; 49 | return FALSE; 50 | } 51 | 52 | sub say_something { 53 | my ($button, $userdata) = @_; 54 | if ($userdata eq "twist") { 55 | my $angle = $label->get_angle; 56 | $label->set_angle( $angle - 30 ); 57 | } elsif ($userdata eq "red") { 58 | $label->set_markup('Red and connected'); 59 | } elsif ($userdata eq "weird") { 60 | $label->set_markup('Weird'); 61 | } 62 | return FALSE; 63 | } 64 | 65 | Gtk3->main; 66 | -------------------------------------------------------------------------------- /6a-Checkbox-Basics.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl 2 | 3 | # 4 | # This example borrows very heavily from the 5 | # The Python GTK+ 3 Tutorial 6 | # http://python-gtk-3-tutorial.readthedocs.org/en/latest/index.html 7 | # 8 | 9 | use strict; 10 | use warnings; 11 | use diagnostics; 12 | use feature ':5.14'; 13 | use Gtk3 '-init'; 14 | use Glib qw/TRUE FALSE/; 15 | 16 | my $window = Gtk3::Window->new('toplevel'); 17 | $window->set_title("Basic Check Boxes"); 18 | $window->set_position("mouse"); 19 | $window->set_default_size(200, 100); 20 | $window->set_border_width(5); 21 | $window->signal_connect (delete_event => sub { Gtk3->main_quit }); 22 | 23 | my $vbox = Gtk3::Box->new("vertical", 5); 24 | $window->add($vbox); 25 | 26 | my $entry = Gtk3::Entry->new; 27 | $entry->set_text("Hello World"); 28 | $vbox->pack_start($entry, TRUE, TRUE, 0); 29 | 30 | my $hbox = Gtk3::Box->new("horizontal", 6); 31 | $vbox->pack_start($hbox, TRUE, TRUE, 0); 32 | 33 | my $check_button1 = Gtk3::CheckButton->new_with_label('Editable'); 34 | $check_button1->signal_connect (toggled => \&cb_toggled, "editable"); 35 | $check_button1->set_active(TRUE); 36 | $hbox->pack_start($check_button1, TRUE, TRUE, 0); 37 | 38 | my $check_button2 = Gtk3::CheckButton->new_with_label("Visible"); 39 | $check_button2->signal_connect (toggled => \&cb_toggled, "visible"); 40 | $check_button2->set_active(TRUE); 41 | $hbox->pack_start($check_button2, TRUE, TRUE, 0); 42 | 43 | my $check_button3 = Gtk3::CheckButton->new_with_label("Icon"); 44 | $check_button3->signal_connect (toggled => \&cb_toggled, "icon"); 45 | $check_button3->set_active(FALSE); 46 | $hbox->pack_start($check_button3, TRUE, TRUE, 0); 47 | 48 | $window->show_all; 49 | Gtk3->main; 50 | 51 | sub cb_toggled { 52 | my ($widget, $data) = @_; 53 | my $value = $widget->get_active; 54 | 55 | if ($data eq 'visible') { 56 | $entry->set_visibility($value); 57 | } elsif ($data eq 'editable') { 58 | $entry->set_editable($value); 59 | } elsif ($data eq 'icon') { 60 | my $stock_id = undef; 61 | if ($value == TRUE) { 62 | $stock_id = Gtk3::STOCK_FIND; 63 | } 64 | $entry->set_icon_from_stock('secondary', $stock_id); 65 | } 66 | return FALSE; 67 | } 68 | -------------------------------------------------------------------------------- /6b-Radiobox.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use diagnostics; 6 | use feature ':5.14'; 7 | use Gtk3 '-init'; 8 | use Glib qw/TRUE FALSE/; 9 | 10 | my $window = Gtk3::Window->new('toplevel'); 11 | $window->set_title("Basic Radio Boxes"); 12 | $window->set_position("mouse"); 13 | $window->set_default_size(200, 100); 14 | $window->set_border_width(5); 15 | $window->signal_connect (delete_event => sub { Gtk3->main_quit }); 16 | 17 | my $hbox = Gtk3::Box->new("horizontal", 5); 18 | $window->add($hbox); 19 | 20 | my $rbutton1 = Gtk3::RadioButton->new_with_label_from_widget(undef, "Button 1"); 21 | $hbox->pack_start($rbutton1, TRUE, TRUE, 0); 22 | $rbutton1->signal_connect (toggled => \&rb_toggled, "rb1"); 23 | 24 | my $rbutton2 = Gtk3::RadioButton->new_with_label_from_widget($rbutton1, "Button 2"); 25 | $hbox->pack_start($rbutton2, TRUE, TRUE, 0); 26 | $rbutton2->signal_connect (toggled => \&rb_toggled, "rb2"); 27 | 28 | my $rbutton3 = Gtk3::RadioButton->new_with_label_from_widget($rbutton1, "Button 3"); 29 | $hbox->pack_start($rbutton3, TRUE, TRUE, 0); 30 | $rbutton3->signal_connect (toggled => \&rb_toggled, "rb3"); 31 | 32 | $window->show_all; 33 | Gtk3->main; 34 | 35 | sub rb_toggled { 36 | my ($widget, $data) = @_; 37 | print "Radio button $data is now "; 38 | if ($widget->get_active == TRUE) { 39 | say " set ON"; 40 | } else { 41 | say " set OFF"; 42 | } 43 | return FALSE; 44 | } 45 | 46 | -------------------------------------------------------------------------------- /6c-Spinbox.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use diagnostics; 6 | use feature ':5.14'; 7 | use Gtk3 '-init'; 8 | use Glib qw/TRUE FALSE/; 9 | 10 | my $window = Gtk3::Window->new('toplevel'); 11 | $window->set_title("Spinbox Example"); 12 | $window->set_position("mouse"); 13 | $window->set_default_size(250, 100); 14 | $window->set_border_width(5); 15 | $window->signal_connect (delete_event => sub { Gtk3->main_quit }); 16 | 17 | my $vbox = Gtk3::Box->new("vertical", 5); 18 | $window->add($vbox); 19 | 20 | my $adjust = Gtk3::Adjustment->new(5, 0, 10, 1, 10, 0); 21 | my $spin = Gtk3::SpinButton->new($adjust, 1, 1); 22 | $vbox->pack_start($spin, FALSE, FALSE, 0); 23 | 24 | my $hbox = Gtk3::Box->new("horizontal", 5); 25 | $vbox->pack_start($hbox, FALSE, FALSE, 0); 26 | 27 | my $zero_button = Gtk3::Button->new('Zero'); 28 | $hbox->pack_start($zero_button, FALSE, FALSE, 0); 29 | $zero_button->signal_connect ( clicked => \&zero ); 30 | 31 | my $check_accuracy = Gtk3::CheckButton->new_with_label('High accuracy'); 32 | $hbox->pack_start($check_accuracy, FALSE, FALSE, 0); 33 | $check_accuracy->signal_connect ( toggled => \&toggle_accuracy ); 34 | 35 | my $hbox2 = Gtk3::Box->new("horizontal", 5); 36 | $vbox->pack_start($hbox2, FALSE, FALSE, 0); 37 | 38 | my $update_button = Gtk3::Button->new('Update'); 39 | $hbox2->pack_start($update_button, FALSE, FALSE, 0); 40 | $update_button->signal_connect ( clicked => \&update ); 41 | 42 | my $label = Gtk3::Label->new("No Value"); 43 | $hbox2->pack_start($label, FALSE, FALSE, 0); 44 | 45 | 46 | $window->show_all; 47 | Gtk3->main; 48 | 49 | sub zero { 50 | my ($widget, $data) = @_; 51 | $spin->set_value(0); 52 | return FALSE; 53 | } 54 | 55 | sub toggle_accuracy{ 56 | my ($widget, $data) = @_; 57 | my $value = $widget->get_active; 58 | if ( $value == TRUE ) { 59 | $spin->set_digits(2); 60 | } else { 61 | $spin->set_digits(1); 62 | } 63 | return FALSE; 64 | } 65 | 66 | sub update { 67 | $label->set_label( $spin->get_value ); 68 | return FALSE; 69 | } 70 | -------------------------------------------------------------------------------- /6d-Toggle-Button.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use diagnostics; 6 | use feature ':5.14'; 7 | use Gtk3 '-init'; 8 | use Glib qw/TRUE FALSE/; 9 | 10 | my $window = Gtk3::Window->new('toplevel'); 11 | $window->set_title("Spin and toggle Example"); 12 | $window->set_position("mouse"); 13 | $window->set_default_size(250, 100); 14 | $window->set_border_width(5); 15 | $window->signal_connect (delete_event => sub { Gtk3->main_quit }); 16 | 17 | my $vbox = Gtk3::Box->new("vertical", 5); 18 | $window->add($vbox); 19 | 20 | my $adjust = Gtk3::Adjustment->new(5, 0, 10, 1, 10, 0); 21 | my $spin = Gtk3::SpinButton->new($adjust, 1, 1); 22 | $vbox->pack_start($spin, FALSE, FALSE, 0); 23 | 24 | my $hbox = Gtk3::Box->new("horizontal", 5); 25 | $vbox->pack_start($hbox, FALSE, FALSE, 0); 26 | 27 | my $zero_button = Gtk3::Button->new('Zero'); 28 | $hbox->pack_start($zero_button, FALSE, FALSE, 0); 29 | $zero_button->signal_connect ( clicked => \&zero ); 30 | 31 | my $toggle_accuracy = Gtk3::ToggleButton->new_with_label('High accuracy'); 32 | $hbox->pack_start($toggle_accuracy, FALSE, FALSE, 0); 33 | $toggle_accuracy->signal_connect ( toggled => \&toggle_accuracy ); 34 | 35 | my $hbox2 = Gtk3::Box->new("horizontal", 5); 36 | $vbox->pack_start($hbox2, FALSE, FALSE, 0); 37 | 38 | my $update_button = Gtk3::Button->new('Update'); 39 | $hbox->pack_start($update_button, FALSE, FALSE, 0); 40 | $update_button->signal_connect ( clicked => \&update ); 41 | 42 | my $label = Gtk3::Label->new("No Value"); 43 | $hbox->pack_start($label, FALSE, FALSE, 0); 44 | 45 | $window->show_all; 46 | Gtk3->main; 47 | 48 | sub zero { 49 | my ($widget, $data) = @_; 50 | $spin->set_value(0); 51 | return FALSE; 52 | } 53 | 54 | sub toggle_accuracy{ 55 | my ($widget, $data) = @_; 56 | my $value = $widget->get_active; 57 | if ( $value == TRUE ) { 58 | $spin->set_digits(2); 59 | } else { 60 | $spin->set_digits(1); 61 | } 62 | return FALSE; 63 | } 64 | 65 | sub update { 66 | $label->set_label( $spin->get_value ); 67 | return FALSE; 68 | } 69 | -------------------------------------------------------------------------------- /7b-Layout-Grid.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use diagnostics; 6 | use feature ':5.14'; 7 | use Gtk3 '-init'; 8 | use Glib qw/TRUE FALSE/; 9 | 10 | my $window = Gtk3::Window->new('toplevel'); 11 | $window->set_title("Grid Example"); 12 | $window->set_position("mouse"); 13 | $window->set_default_size(250, 100); 14 | $window->set_border_width(5); 15 | $window->signal_connect (delete_event => sub { Gtk3->main_quit }); 16 | 17 | my $grid = Gtk3::Grid->new; 18 | $window->add($grid); 19 | 20 | my $toggle1 = Gtk3::ToggleButton->new_with_label('One'); 21 | my $toggle2 = Gtk3::ToggleButton->new_with_label('Two'); 22 | my $toggle3 = Gtk3::ToggleButton->new_with_label('Three'); 23 | my $toggle4 = Gtk3::ToggleButton->new_with_label('Four'); 24 | my $toggle5 = Gtk3::ToggleButton->new_with_label('Five'); 25 | my $toggle6 = Gtk3::ToggleButton->new_with_label('Six'); 26 | 27 | $grid->add($toggle1); 28 | $grid->attach($toggle2, 1, 0, 2, 1); 29 | $grid->attach_next_to($toggle3, $toggle1, 'bottom', 1, 2); 30 | $grid->attach_next_to($toggle4, $toggle3, 'right', 2, 1); 31 | $grid->attach($toggle5, 1, 2, 1, 1); 32 | $grid->attach_next_to($toggle6, $toggle5, 'GTK_POS_RIGHT', 1, 1); 33 | 34 | $window->show_all; 35 | Gtk3->main; 36 | -------------------------------------------------------------------------------- /8a-Display-Progress-bar.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use diagnostics; 6 | use feature ':5.14'; 7 | use Gtk3 '-init'; 8 | use Glib qw/TRUE FALSE/; 9 | 10 | my $window = Gtk3::Window->new('toplevel'); 11 | $window->set_title("Progress Bar"); 12 | $window->set_position("mouse"); 13 | $window->set_default_size(250, 100); 14 | $window->set_border_width(5); 15 | $window->signal_connect (delete_event => sub { Gtk3->main_quit }); 16 | 17 | my $vbox = Gtk3::Box->new("vertical", 5); 18 | $window->add($vbox); 19 | 20 | my $progress = Gtk3::ProgressBar->new; 21 | $progress->set_orientation("horizontal"); 22 | $progress->set_inverted(FALSE); 23 | $progress->set_text(undef); 24 | $progress->set_show_text(FALSE); 25 | $vbox->add($progress); 26 | 27 | my $hbox = Gtk3::Box->new("horizontal", 2); 28 | $vbox->add($hbox); 29 | 30 | my $toggle1 = Gtk3::ToggleButton->new_with_label('Text'); 31 | my $toggle2 = Gtk3::ToggleButton->new_with_label('Percent'); 32 | my $toggle3 = Gtk3::ToggleButton->new_with_label('Invert'); 33 | $hbox->pack_start($toggle1, TRUE, TRUE, 0); 34 | $hbox->pack_start($toggle2, TRUE, TRUE, 0); 35 | $hbox->pack_start($toggle3, TRUE, TRUE, 0); 36 | 37 | $toggle1->signal_connect ( toggled => \&showtext ); 38 | $toggle2->signal_connect ( toggled => \&showpercent ); 39 | $toggle3->signal_connect ( toggled => \&invert ); 40 | 41 | my $increment = 0.01; 42 | my $timer = Glib::Timeout->add (50, \&update); 43 | 44 | $window->show_all; 45 | Gtk3->main; 46 | 47 | sub showtext { 48 | my ($widget, $data) = @_; 49 | my $text = undef; 50 | if ( $widget->get_active ) { 51 | $text = "some text"; 52 | } 53 | $progress->set_text( $text ); 54 | $progress->set_show_text( $widget->get_active ); 55 | return FALSE; 56 | } 57 | 58 | sub showpercent { 59 | my ($widget, $data) = @_; 60 | my $show = $widget->get_active; 61 | $progress->set_text( undef ); 62 | $progress->set_show_text( $show ); 63 | return FALSE; 64 | } 65 | 66 | sub invert { 67 | my ($widget, $data) = @_; 68 | $progress->set_inverted( $widget->get_active ); 69 | return FALSE; 70 | } 71 | 72 | sub update { 73 | if ( $progress->get_fraction >= 1 ) { 74 | $increment = -0.01; 75 | } 76 | if ( $progress->get_fraction <= 0 ) { 77 | $increment = 0.01; 78 | } 79 | $progress->set_fraction( $progress->get_fraction + $increment ); 80 | return TRUE; 81 | } 82 | -------------------------------------------------------------------------------- /8b-Spinner.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use diagnostics; 6 | use feature ':5.14'; 7 | use Gtk3 '-init'; 8 | use Glib qw/TRUE FALSE/; 9 | 10 | my $window = Gtk3::Window->new('toplevel'); 11 | $window->set_title("Spinner Example"); 12 | $window->set_position("mouse"); 13 | $window->set_default_size(250, 100); 14 | $window->set_border_width(5); 15 | $window->signal_connect (delete_event => sub { Gtk3->main_quit }); 16 | 17 | my $vbox = Gtk3::Box->new("vertical", 5); 18 | $window->add($vbox); 19 | 20 | my $spinner = Gtk3::Spinner->new; 21 | my $toggle1 = Gtk3::ToggleButton->new_with_label('Spin'); 22 | $toggle1->set_active( FALSE ); 23 | 24 | $vbox->pack_start($spinner, TRUE, TRUE, 5 ); 25 | $vbox->pack_start($toggle1, TRUE, TRUE, 5 ); 26 | 27 | $toggle1->signal_connect ( toggled => \&toggle ); 28 | 29 | $window->show_all; 30 | Gtk3->main; 31 | 32 | sub toggle { 33 | my ($widget, $data) = @_; 34 | if ( $widget->get_active == TRUE ) { 35 | $spinner->start; 36 | $toggle1->set_label("Stop"); 37 | } else { 38 | $spinner->stop; 39 | $toggle1->set_label("Spin"); 40 | } 41 | return FALSE; 42 | } 43 | -------------------------------------------------------------------------------- /8c-Switch.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use diagnostics; 6 | use feature ':5.14'; 7 | use Gtk3 '-init'; 8 | use Glib qw/TRUE FALSE/; 9 | use Data::Dumper; 10 | 11 | my $window = Gtk3::Window->new('toplevel'); 12 | $window->set_title("Switch Example"); 13 | $window->set_position("mouse"); 14 | $window->set_default_size(250, 100); 15 | $window->set_border_width(5); 16 | $window->signal_connect (delete_event => sub { Gtk3->main_quit }); 17 | 18 | my $vbox = Gtk3::Box->new("vertical", 5); 19 | $window->add($vbox); 20 | 21 | my $spinner = Gtk3::Spinner->new; 22 | my $switch = Gtk3::Switch->new; 23 | $switch->set_active( FALSE ); 24 | 25 | $vbox->pack_start($spinner, TRUE, TRUE, 5 ); 26 | $vbox->pack_start($switch, TRUE, TRUE, 5 ); 27 | 28 | $switch->signal_connect( 'notify::active' => \&toggle ); 29 | 30 | $window->show_all; 31 | Gtk3->main; 32 | 33 | sub toggle { 34 | my ($widget, $data) = @_; 35 | say Dumper($data); 36 | if ( $widget->get_active == TRUE ) { 37 | $spinner->start; 38 | } else { 39 | $spinner->stop; 40 | } 41 | return FALSE; 42 | } 43 | -------------------------------------------------------------------------------- /8d-Separators.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use diagnostics; 6 | use feature ':5.14'; 7 | use Gtk3 '-init'; 8 | use Glib qw/TRUE FALSE/; 9 | use Data::Dumper; 10 | 11 | my $window = Gtk3::Window->new('toplevel'); 12 | $window->set_title("Switch Example"); 13 | $window->set_position("mouse"); 14 | $window->set_default_size(250, 100); 15 | $window->set_border_width(5); 16 | $window->signal_connect (delete_event => sub { Gtk3->main_quit }); 17 | 18 | my $vbox = Gtk3::Box->new("vertical", 5); 19 | $window->add($vbox); 20 | 21 | my $spinner = Gtk3::Spinner->new; 22 | my $switch = Gtk3::Switch->new; 23 | $switch->set_active( FALSE ); 24 | 25 | my $sep = Gtk3::Separator->new("horizontal"); 26 | 27 | $vbox->pack_start($spinner, TRUE, TRUE, 5 ); 28 | $vbox->pack_start($sep, FALSE, FALSE, 2 ); 29 | $vbox->pack_start($switch, TRUE, TRUE, 5 ); 30 | 31 | $switch->signal_connect( 'notify::active' => \&toggle ); 32 | 33 | $window->show_all; 34 | Gtk3->main; 35 | 36 | sub toggle { 37 | my ($widget, $data) = @_; 38 | say Dumper($data); 39 | if ( $widget->get_active == TRUE ) { 40 | $spinner->start; 41 | } else { 42 | $spinner->stop; 43 | } 44 | return FALSE; 45 | } 46 | -------------------------------------------------------------------------------- /9a-cairo-drawing.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use feature ':5.14'; 6 | use Gtk3 '-init'; 7 | use Glib qw/TRUE FALSE/; 8 | use Cairo::GObject; 9 | 10 | use constant PI => 3.1415927; 11 | 12 | my $window = Gtk3::Window->new('toplevel'); 13 | $window->set_title("Cairo Basics"); 14 | $window->set_position("mouse"); 15 | $window->set_default_size(600, 400); 16 | $window->set_border_width(5); 17 | $window->signal_connect (delete_event => sub { Gtk3->main_quit }); 18 | 19 | my $frame = Gtk3::Frame->new("Cairo Drawings"); 20 | $window->add($frame); 21 | 22 | my $drawable = Gtk3::DrawingArea->new; 23 | $drawable->signal_connect( draw => \&cairo_draw ); 24 | $frame->add($drawable); 25 | 26 | $window->show_all; 27 | 28 | Gtk3->main; 29 | 30 | sub cairo_draw { 31 | my ( $widget, $context, $ref_status ) = @_; 32 | 33 | # Background 34 | $context->set_source_rgb(0.9, 0.9, 0.7); 35 | $context->paint; 36 | 37 | # Solid box 38 | $context->set_source_rgb(0.6, 0.0, 0.0); 39 | $context->set_line_width(10); 40 | $context->rectangle( 5, 5, 160, 130); 41 | $context->stroke; 42 | 43 | # Transparent Rectangle 44 | $context->set_source_rgba(0.0, 0.7, 0.0, 0.5); 45 | $context->set_line_width(10); 46 | $context->rectangle( 85, 85, 160, 230); 47 | $context->fill; 48 | 49 | # Circle with border - transparent 50 | $context->set_source_rgba(0.0, 0.0, 0.9, 0.5); 51 | $context->arc( 220, 150, 100, 0, PI * 2 ); 52 | $context->set_line_width(10); 53 | $context->stroke_preserve; 54 | $context->set_source_rgba(0.9, 0.2, 0.2, 0.2); 55 | $context->fill; 56 | 57 | # Segment 58 | $context->set_source_rgba(0.9, 0.2, 0.2, 0.4); 59 | $context->set_line_width(2); 60 | $context->arc( 400, 200, 100, 0, 5); 61 | $context->stroke_preserve; 62 | $context->line_to(400, 200); 63 | $context->stroke_preserve; 64 | $context->close_path; 65 | $context->stroke_preserve; 66 | $context->set_source_rgba(0.1, 0.4, 0.4, 0.4); 67 | $context->fill; 68 | 69 | # Arc 70 | $context->set_source_rgba(0.1, 0.1, 0.1, 0.8); 71 | $context->set_line_width(2); 72 | $context->arc( 450, 220, 100, 0, 5); 73 | $context->stroke; 74 | 75 | # Line 76 | $context->set_source_rgba(0, 0, 0, 0.5); 77 | $context->set_line_width(30); 78 | $context->move_to(50, 50); 79 | $context->line_to(550, 350); 80 | $context->stroke; 81 | 82 | # Curve 83 | $context->set_line_width(10); 84 | $context->set_source_rgba(0.9, 0.9, 0, 0.9); 85 | $context->move_to(50, 50); 86 | $context->curve_to( 100, 250, 250, 150, 550, 350); 87 | $context->stroke; 88 | 89 | # Text 90 | $context->set_source_rgba(0.0, 0.9, 0.9, 0.7); 91 | $context->select_font_face( "Sans", "normal", "bold" ); 92 | $context->set_font_size( 50 ); 93 | $context->move_to(220, 50); 94 | $context->show_text( "Ooooooh"); 95 | $context->stroke; 96 | 97 | $context->move_to(370, 170); 98 | $context->text_path( "pretty" ); 99 | $context->set_source_rgba(0.9, 0, 0.9, 0.7); 100 | $context->fill_preserve; 101 | $context->set_source_rgba(0.2, 0.1, 0.1, 0.7); 102 | $context->set_line_width( 2 ); 103 | $context->stroke; 104 | 105 | # Linear Gradients 106 | my $pattern2 = Cairo::LinearGradient->create(520, 20, 570, 320); 107 | $pattern2->add_color_stop_rgba(0, 0, 0.7, 1, 0.5); 108 | $pattern2->add_color_stop_rgba(1, 0, 0, 0.5, 0.95); 109 | $context->set_source( $pattern2 ); 110 | $context->rectangle( 520, 20, 570, 320); 111 | $context->fill; 112 | 113 | # Radial Gradients 114 | my $pattern = Cairo::RadialGradient->create(40, 220, 25, 60, 240, 150); 115 | $pattern->add_color_stop_rgba(0, 1, 0.7, 1, 0.95); 116 | $pattern->add_color_stop_rgba(1, 0, 0, 0.5, 0.95); 117 | $context->set_source( $pattern ); 118 | $context->arc( 80, 250, 80, 0, PI * 2 ); 119 | $context->fill; 120 | 121 | return FALSE; 122 | } 123 | -------------------------------------------------------------------------------- /9b-cairo-animation.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use feature ':5.14'; 6 | use Gtk3 '-init'; 7 | use Glib qw/TRUE FALSE/; 8 | use Cairo::GObject; 9 | use Data::Dumper; 10 | 11 | use constant PI => 3.1415927; 12 | use constant WIDTH => 600; 13 | use constant HEIGHT => 400; 14 | use constant RADIUS => 20; 15 | 16 | my %status = ( 17 | vector => { 'x' => 2, 'y' => 3 }, 18 | position => { 'x' => 20, 'y' => 30 }, 19 | ); 20 | 21 | my $window = Gtk3::Window->new('toplevel'); 22 | $window->set_title("Cairo animation"); 23 | $window->set_position("mouse"); 24 | $window->set_default_size(WIDTH+10, HEIGHT+30); 25 | $window->set_border_width(5); 26 | $window->signal_connect (delete_event => sub { Gtk3->main_quit }); 27 | 28 | my $frame = Gtk3::Frame->new("Bouncing ball"); 29 | $window->add($frame); 30 | 31 | my $drawable = Gtk3::DrawingArea->new; 32 | $drawable->signal_connect( draw => \&cairo_draw, \%status ); 33 | $frame->add($drawable); 34 | 35 | $window->show_all; 36 | 37 | my $timer = Glib::Timeout->add( 20, \&update ); 38 | 39 | Gtk3->main; 40 | 41 | sub update { 42 | $status{'position'}{'x'} += $status{'vector'}{'x'}; 43 | $status{'position'}{'y'} += $status{'vector'}{'y'}; 44 | if ( $status{'position'}{'y'} >= HEIGHT - RADIUS ) { 45 | $status{'vector'}{'y'} = -$status{'vector'}{'y'}; 46 | } 47 | if ( $status{'position'}{'y'} <= RADIUS ) { 48 | $status{'vector'}{'y'} = -$status{'vector'}{'y'}; 49 | } 50 | if ( $status{'position'}{'x'} >= WIDTH - RADIUS ) { 51 | $status{'vector'}{'x'} = -$status{'vector'}{'x'}; 52 | } 53 | if ( $status{'position'}{'x'} <= RADIUS ) { 54 | $status{'vector'}{'x'} = -$status{'vector'}{'x'}; 55 | } 56 | $drawable->queue_draw; 57 | return TRUE; 58 | } 59 | 60 | sub cairo_draw { 61 | my ( $widget, $context, $ref_status ) = @_; 62 | 63 | # Background 64 | my $pattern2 = Cairo::LinearGradient->create( 0, 0, WIDTH , HEIGHT); 65 | $pattern2->add_color_stop_rgba(0, 0.2, 0.7, 0.9, 0.7); 66 | $pattern2->add_color_stop_rgba(1, 0.2, 0.7, 0.5, 0.7); 67 | $context->set_source( $pattern2 ); 68 | $context->rectangle( 0, 0, WIDTH , HEIGHT); 69 | $context->fill; 70 | 71 | # Ball 72 | my $pattern = Cairo::RadialGradient->create( 73 | $ref_status->{'position'}{'x'}, 74 | $ref_status->{'position'}{'y'}, 75 | RADIUS, 76 | $ref_status->{'position'}{'x'}-2, 77 | $ref_status->{'position'}{'y'}-2, 78 | RADIUS / 2 79 | ); 80 | $pattern->add_color_stop_rgb(0, 0.8, 0.6, 0.8); 81 | $pattern->add_color_stop_rgb(1, 1, 0.7, 1); 82 | $context->set_source( $pattern ); 83 | $context->arc( 84 | $ref_status->{'position'}{'x'}, 85 | $ref_status->{'position'}{'y'}, 86 | RADIUS, 87 | 0, 88 | PI * 2, 89 | ); 90 | $context->fill; 91 | 92 | return FALSE; 93 | } 94 | -------------------------------------------------------------------------------- /Perl Gtk3 Tutorial.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinphilp/Perl-gtk3-Tutorial/71955c37e9b30fcecf89b8d3ea4210c2693e1cd7/Perl Gtk3 Tutorial.odt -------------------------------------------------------------------------------- /Perl Gtk3 Tutorial.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinphilp/Perl-gtk3-Tutorial/71955c37e9b30fcecf89b8d3ea4210c2693e1cd7/Perl Gtk3 Tutorial.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Perl-gtk3-Tutorial 2 | ================== 3 | 4 | A basic introduction to using the Gtk3 toolkit with the Perl bindings. This tutorial was written to help my son and I learn Gtk3/Perl. The tutorial takes a series of standard widgets and presents them with a full, working example and an explanation of how it works. 5 | 6 | We are posting this on the internet in case other people find it useful. 7 | -------------------------------------------------------------------------------- /mandelbrot-fractal.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl 2 | 3 | # Based on http://warp.povusers.org/Mandelbrot/ 4 | 5 | use strict; 6 | use warnings; 7 | use feature ':5.14'; 8 | use Gtk3 '-init'; 9 | use Glib qw/TRUE FALSE/; 10 | use Cairo::GObject; 11 | use Data::Dumper; 12 | 13 | use constant LOOPS => 100; 14 | 15 | use constant MAX_X => 800; # REAL 16 | use constant MAX_Y => 800; # IMAG 17 | 18 | use constant MIN_REAL => -2.0; # -2.0 19 | use constant MIN_IMAG => -1.2; # -1.2 20 | use constant MAX_REAL => 1.0; # 1.0 21 | my $MAX_IMAG = MIN_IMAG + (MAX_REAL - MIN_REAL) * (MAX_X / MAX_Y); 22 | my $re_factor = (MAX_REAL - MIN_REAL) / (MAX_X - 1); 23 | my $im_factor = ($MAX_IMAG - MIN_IMAG) / (MAX_Y - 1); 24 | 25 | my @farray = (); 26 | 27 | my $window = Gtk3::Window->new('toplevel'); 28 | $window->set_title("Mandelbrot Set"); 29 | $window->set_position("mouse"); 30 | $window->set_default_size(MAX_X + 50, MAX_Y + 100); 31 | $window->set_border_width(5); 32 | $window->signal_connect (delete_event => sub { Gtk3->main_quit }); 33 | 34 | my $vbox = Gtk3::Box->new("vertical", 1); 35 | $window->add($vbox); 36 | 37 | my $frame = Gtk3::Frame->new("Cairo Drawings"); 38 | $vbox->pack_start($frame, TRUE, TRUE, 0); 39 | 40 | my $drawable = Gtk3::DrawingArea->new; 41 | $drawable->signal_connect( draw => \&cairo_draw ); 42 | $frame->add($drawable); 43 | 44 | my $surface = Cairo::ImageSurface->create ('argb32', MAX_X, MAX_Y); 45 | my $cr = Cairo::Context->create ($surface); 46 | 47 | fractal(); 48 | drawbuf(); 49 | 50 | $window->show_all; 51 | 52 | Gtk3->main; 53 | 54 | sub fractal { 55 | for (my $y = 0; $y <= MAX_Y; $y++ ) { 56 | my $c_im = $MAX_IMAG - $y * $im_factor; 57 | for (my $x = 0; $x <= MAX_X; $x++ ) { 58 | $farray[$x][$y]{'real'} = MIN_REAL + $x * $re_factor; 59 | $farray[$x][$y]{'imag'} = $c_im; 60 | my $z = calcz( $farray[$x][$y]{'real'}, $farray[$x][$y]{'imag'} ); 61 | $farray[$x][$y]{'z'} = $z; 62 | } 63 | } 64 | } 65 | 66 | sub calcz { 67 | my ($c_re, $c_im) = @_; 68 | my $z_re = $c_re; 69 | my $z_im = $c_im; 70 | my $inside = TRUE; 71 | for (my $n=0; $n <= LOOPS; $n++) { 72 | my $z_re2 = ($z_re * $z_re); 73 | my $z_im2 = ($z_im * $z_im); 74 | if ( ($z_re2 + $z_im2) > 4 ) { 75 | return $n; 76 | } 77 | $z_im = 2 * $z_re * $z_im + $c_im; 78 | $z_re = $z_re2 - $z_im2 + $c_re; 79 | } 80 | return 0; 81 | } 82 | 83 | sub drawbuf { 84 | $cr->set_line_width(1); 85 | for (my $y = 0; $y <= MAX_Y; $y++ ) { 86 | for (my $x = 0; $x <= MAX_X; $x++ ) { 87 | $cr->set_source_rgb( $farray[$x][$y]{'z'} / LOOPS, 0, 0 ); 88 | $cr->rectangle( $x, $y, 1, 1); 89 | $cr->stroke; 90 | } 91 | } 92 | } 93 | 94 | sub cairo_draw { 95 | my ( $widget, $context, $ref_status ) = @_; 96 | $context->set_source_surface($surface, 0, 0); 97 | $context->paint; 98 | return FALSE; 99 | } 100 | -------------------------------------------------------------------------------- /pong.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use feature ':5.14'; 6 | use Gtk3 '-init'; 7 | use Glib qw/TRUE FALSE/; 8 | use Cairo::GObject; 9 | use Data::Dumper; 10 | 11 | use constant PI => 3.1415927; 12 | use constant WIDTH => 600; 13 | use constant HEIGHT => 400; 14 | use constant RADIUS => 12; 15 | 16 | my %status = ( 17 | vector => { 'x' => 2, 'y' => 3 }, 18 | position => { 'x' => 20, 'y' => 30 }, 19 | playerA => { 'y' => 50 }, 20 | playerB => { 'y' => 50 } 21 | ); 22 | 23 | my $window = Gtk3::Window->new('toplevel'); 24 | $window->set_title("Grid Example"); 25 | $window->set_position("mouse"); 26 | $window->set_default_size(WIDTH +50, HEIGHT +100); 27 | $window->set_border_width(5); 28 | $window->signal_connect (delete_event => sub { Gtk3->main_quit }); 29 | $window->signal_connect( key_press_event => \&keypress ); 30 | 31 | my $vbox = Gtk3::Box->new("vertical", 5); 32 | $window->add($vbox); 33 | 34 | my $hbox = Gtk3::Box->new("horizontal", 5); 35 | $vbox->add($hbox); 36 | 37 | my $toggle1 = Gtk3::ToggleButton->new_with_label('Text'); 38 | my $toggle2 = Gtk3::ToggleButton->new_with_label('Percent'); 39 | my $toggle3 = Gtk3::ToggleButton->new_with_label('Invert'); 40 | $hbox->pack_start($toggle1, TRUE, TRUE, 0); 41 | $hbox->pack_start($toggle2, TRUE, TRUE, 0); 42 | $hbox->pack_start($toggle3, TRUE, TRUE, 0); 43 | 44 | my $frame = Gtk3::Frame->new("Pong"); 45 | $vbox->pack_start($frame, TRUE, TRUE, 5); 46 | 47 | my $drawable = Gtk3::DrawingArea->new; 48 | $drawable->signal_connect( draw => \&cairo_draw, \%status ); 49 | $frame->add($drawable); 50 | 51 | $window->show_all; 52 | 53 | my $timer = Glib::Timeout->add( 20, \&update ); 54 | 55 | Gtk3->main; 56 | 57 | sub keypress { 58 | my ( $widget, $event ) = @_; 59 | 60 | given($event->key->{'string'}) { 61 | when ('q') { $status{'playerA'}{'y'} -= 3; } 62 | when ('a') { $status{'playerA'}{'y'} += 3; } 63 | when ('p') { $status{'playerB'}{'y'} -= 3; } 64 | when ('l') { $status{'playerB'}{'y'} += 3; } 65 | } 66 | return FALSE; 67 | } 68 | 69 | sub update { 70 | $status{'position'}{'x'} += $status{'vector'}{'x'}; 71 | $status{'position'}{'y'} += $status{'vector'}{'y'}; 72 | if ( $status{'position'}{'y'} >= HEIGHT - RADIUS ) { 73 | $status{'vector'}{'y'} = -$status{'vector'}{'y'}; 74 | } 75 | if ( $status{'position'}{'y'} <= RADIUS ) { 76 | $status{'vector'}{'y'} = -$status{'vector'}{'y'}; 77 | } 78 | if ( $status{'position'}{'x'} >= WIDTH - RADIUS ) { 79 | $status{'vector'}{'x'} = -$status{'vector'}{'x'}; 80 | } 81 | if ( $status{'position'}{'x'} <= RADIUS ) { 82 | $status{'vector'}{'x'} = -$status{'vector'}{'x'}; 83 | } 84 | $drawable->queue_draw; 85 | return TRUE; 86 | } 87 | 88 | sub cairo_draw { 89 | my ( $widget, $context, $ref_status ) = @_; 90 | 91 | # Background 92 | $context->set_source_rgb(0.19, 0.69, 0); 93 | $context->rectangle(0, 0, WIDTH, HEIGHT); 94 | $context->fill; 95 | 96 | # Ball 97 | $context->set_source_rgb(0.69, 0.19, 0); 98 | $context->arc( 99 | $ref_status->{'position'}{'x'}, 100 | $ref_status->{'position'}{'y'}, 101 | RADIUS, 102 | 0, 103 | PI * 2 104 | ); 105 | $context->stroke_preserve; 106 | $context->set_source_rgb(0.6, 0.4, 0.2); 107 | $context->fill; 108 | 109 | # Paddle A 110 | $context->set_source_rgb(0.0, 0.0, 0.7); 111 | $context->rectangle( 15, $ref_status->{'playerA'}{'y'}, 10, 30); 112 | $context->fill; 113 | 114 | # Paddle B 115 | $context->set_source_rgb(0.0, 0.0, 0.7); 116 | $context->rectangle( WIDTH - 15, $ref_status->{'playerB'}{'y'}, 10, 30); 117 | $context->fill; 118 | 119 | return FALSE; 120 | } 121 | --------------------------------------------------------------------------------