Category: | Gui Programming |
Author/Contact Info | zentara of perlmonks |
Description: | UPDATE Oct 29, 2008 I just found out that the separate module TrayIcon is not crossplatform ( not work on win32), and I should have used StatusIcon( a part of Gtk2 ) instead. I will try to update this as soon as I can to using StatusIcon. Sorry for any inconvenience.
This is a little demo to show some tricks to make a nice Gtk2::TrayIcon based app. It does nothing but tail itself, but includes a generic outline for including multi-colored icons, flashing the icon, and a configuration setup which you can easily modify. A left click on the icon will popup a window tailling $0. This can be useful if you want to tail /var/log/messages( with correct permissions), etc, or if you want to setup an IO::Notify type watch on a directory. A directory and file selector option are in the configuration window. I may put the output of Sys::Info into a table as a real use. A middle click will start the icon blinking, a second middle-click stops it. This just demos how to make your trayicon flash efficiently. A right-click brings up the menu. It is full of easily modifiable code, to allow you to do what you want. All the checkbuttons do is change icon color, but you can make them do whatever you want. I originally started to write a TrayIcon app, similar to Gufw a PyGtk2 TrayIcon app to monitor a firewall, but decided to write a generic one first. After configuration, if you hit the "accept" button, it will save the options to __DATA__ for the next startup. |
#!/usr/bin/perl use warnings; use strict; use Gtk2 '-init'; use Gtk2::TrayIcon; use MIME::Base64; my %icon; build_icons(); # r,g,b,y my $icon_cur = $icon{ 'g' }; my $flash = 0; #flash control my %val; while(<DATA>){ chomp; my($key,$value) = split(/=/,$_); if( defined $key){ $val{$key} = $value; } } # check data reloaded properly #foreach my $key(sort keys %val){print $key.'='.$val{$key}."\n"} # default global variables if not saved to __DATA__ $val{'dir'} ||= './'; $val{'file'} ||= $0; $val{'check1'} ||= 1; $val{'check2'} ||= 0; $val{'check3'} ||= 0; $val{'var1'} ||= 'VAR1'; $val{'var2'} ||= 'VAR2'; $val{'var3'} ||= 'VAR3'; $val{'var4'} ||= 'VAR4'; $val{'var5'} ||= 'VAR5'; $val{'var6'} ||= 'VAR6'; $val{'var7'} ||= 'VAR7'; $val{'var8'} ||= 'VAR8'; $val{'var9'} ||= 'VAR9'; $val{'var10'} ||= 'VAR10'; #icon goes in box, box goes in tray my $icon = Gtk2::Image->new_from_pixbuf( $icon_cur ); my $eventbox = Gtk2::EventBox->new; $eventbox->add( $icon ); my $trayicon = Gtk2::TrayIcon->new( 'Info' ); $trayicon->add( $eventbox ); #tooltip my $tooltip = Gtk2::Tooltips->new; $tooltip->set_tip( $trayicon, "Info v1.0" ); $eventbox->signal_connect( 'button_press_event', \&click ); #show tray $trayicon->show_all; #end event loop Gtk2->main; #handles tray clicks sub click { #left mouse button if ( $_[ 1 ]->button == 1 ) { &pop_it; } elsif ( $_[ 1 ]->button == 2 ) { if($flash == 1){ $flash = 0; }else{ $flash = 1; &start_flash; } } #right mouse button elsif ( $_[ 1 ]->button == 3 ) { &config_it; } return 1; } sub start_flash{ $tooltip->set_tip( $trayicon, "Danger Will Robinson!!" ); my $toggle = -1; Glib::Timeout->add( 500, sub { $toggle *= -1; if($toggle > 0){ $icon->set_from_pixbuf ($icon{'y'}) }else{ $icon->set_from_pixbuf (undef); } if($flash > 0){return 1} else{ $icon->set_from_pixbuf ($icon_cur); $tooltip->set_tip( $trayicon, "Info v1.0" ); return 0 }; } ); } sub pop_it{ my $popup = Gtk2::Window->new( 'toplevel' ); $popup->set_position( 'center' ); my $vbox = Gtk2::VBox->new( 0, 6 ); $popup->add($vbox); $vbox->set_border_width(2); my $hbox= Gtk2::HBox->new( 0, 6 ); $vbox->pack_end($hbox,0,0,2); my $ebutton = Gtk2::Button->new_from_stock('gtk-close'); $hbox->pack_end( $ebutton, 0, 0, 0 ); $ebutton->signal_connect( clicked => sub{ $popup->hide_all } ); # Create a textbuffer to contain that string my $textbuffer = Gtk2::TextBuffer->new(); my $tail = `tail -n 20 $val{'file'}`; $textbuffer->set_text($tail); # Create a textview using that textbuffer my $textview = Gtk2::TextView->new_with_buffer($textbuffer); # Add the textview to a scrolledwindow my $scrolledwindow = Gtk2::ScrolledWindow->new( undef, undef ); $scrolledwindow->add($textview); $scrolledwindow->set_size_request (300, 300); $vbox->pack_start($scrolledwindow, 1, 1, 0 ); $popup->show_all; } #right click menu sub config_it { # change Mode or whatever.... a menu entry my $menu = Gtk2::Menu->new(); my $menu_Z1 = Gtk2::ImageMenuItem->new_with_label( "Mode Z1" ); $menu_Z1->signal_connect( activate => sub { #change the icon when Z1 is clicked in the menu $icon->set_from_pixbuf ($icon{'b'}); $tooltip->set_tip( $trayicon, "Mode Z1" ); } ); $menu_Z1->set_image( Gtk2::Image->new_from_stock( 'gtk-refresh', 'm +enu' ) ); $menu->add( $menu_Z1); # launch configure window my $menu_pref = Gtk2::ImageMenuItem->new_with_label( "Configure" ); $menu_pref->signal_connect( activate => \&configure ); $menu_pref->set_image( Gtk2::Image->new_from_stock( 'gtk-preferences', 'menu' ) ); $menu->add( $menu_pref ); #separator my $menu_sep = Gtk2::SeparatorMenuItem->new(); $menu->add( $menu_sep ); #Quit my $menu_quit = Gtk2::ImageMenuItem->new_with_label( "Quit" ); $menu_quit->signal_connect( activate => \&exit_it ); $menu_quit->set_image( Gtk2::Image->new_from_stock( 'gtk-quit', 'me +nu' ) ); $menu->add( $menu_quit ); $menu->show_all; $menu->popup( undef, undef, undef, undef, 0, 0 ); return 1; } #configuration dialog window sub configure { #Create the new window my $config_window = Gtk2::Window->new( 'toplevel' ); $config_window->set_title( "Configuration" ); $config_window->set_position( 'center' ); #VBox container my $table_config = Gtk2::Table->new( 3, 5, 0 ); #Create Notebook my $config_notebook = Gtk2::Notebook->new; $config_notebook->set_tab_pos( 'top' ); #the First Page; Config file select my $vbox_p1 = Gtk2::VBox->new( 0, 1 ); my $hbox_1_p1 = Gtk2::HBox->new( 0, 1 ); my $label_1_p1 = Gtk2::Label->new( 'Directory' ); my $entry_1_p1 = Gtk2::Entry->new; $entry_1_p1->set_width_chars (60); $entry_1_p1->set_position(60); my $button_1_p1 = Gtk2::Button->new_with_mnemonic( "_Browse" ); $button_1_p1->set_size_request( 80, 32 ); my $align_button_1_p1 = Gtk2::Alignment->new( 0.5, 0.5, 0, 0 ); $align_button_1_p1->add( $button_1_p1 ); $entry_1_p1->set_text( $val{'dir'} ); $hbox_1_p1->pack_start( $label_1_p1, 0, 0, 1 ); $hbox_1_p1->pack_start( $entry_1_p1, 1, 1, 1 ); $hbox_1_p1->pack_start( $align_button_1_p1, 0, 0, 1 ); my $hbox_2_p1 = Gtk2::HBox->new( 0, 1 ); my $label_2_p1 = Gtk2::Label->new( 'Tail File' ); my $entry_2_p1 = Gtk2::Entry->new; $entry_2_p1->set_width_chars (60); $entry_2_p1->set_position(60); my $button_2_p1 = Gtk2::Button->new_with_mnemonic( "_Browse" ); $button_2_p1->set_size_request( 80, 32 ); my $align_button_2_p1 = Gtk2::Alignment->new( 0.5, 0.5, 0, 0 ); $align_button_2_p1->add( $button_2_p1 ); $entry_2_p1->set_text( $val{'file'} ); $hbox_2_p1->pack_start( $label_2_p1, 0, 0, 1 ); $hbox_2_p1->pack_start( $entry_2_p1, 1, 1, 1 ); $hbox_2_p1->pack_start( $align_button_2_p1, 0, 0, 1 ); $vbox_p1->pack_start( $hbox_1_p1, 0, 0, 1 ); $vbox_p1->pack_start( $hbox_2_p1, 0, 0, 1 ); $button_1_p1->signal_connect( 'clicked' => sub { my $fs = Gtk2::FileChooserDialog->new( 'Choose a Directory', $config_window, 'select-folder', 'gtk-cancel' => 'cancel', 'gtk-ok' => 'accept' ); my $response = $fs->run(); if ( "accept" eq $response ) { my $dir = $fs->get_filename(); $entry_1_p1->set_text( $dir ); } $fs->destroy; } ); $button_2_p1->signal_connect( 'clicked' => sub { my $fs = Gtk2::FileChooserDialog->new( "FS", $config_window, 'open', "Cancel" => "cancel", "OK" => "accept", ); my $response = $fs->run(); if ( "accept" eq $response ) { my $file = $fs->get_filename(); $entry_2_p1->set_text( $file ); } $fs->destroy; } ); my $checkbutton1 = Gtk2::CheckButton->new('Some Feature1'); $checkbutton1->set_active( $val{'check1'} ); $vbox_p1->pack_start( $checkbutton1, 0, 0, 1 ); $checkbutton1->signal_connect( clicked => \&check_button_callback1 ); my $checkbutton2 = Gtk2::CheckButton->new_with_label('Some Feature2'); $checkbutton2->set_active( $val{'check2'} ); $vbox_p1->pack_start( $checkbutton2, 0, 0, 1 ); $checkbutton2->signal_connect( clicked => \&check_button_callback2 ); my $checkbutton3 = Gtk2::CheckButton->new_with_mnemonic('Some Feature3 +'); $checkbutton3->set_active( $val{'check3'} ); $vbox_p1->pack_start( $checkbutton3, 0, 0, 1 ); $checkbutton3->signal_connect( clicked => \&check_button_callback3 ); #the Second Page; my $vbox_p2 = Gtk2::VBox->new( 0, 1 ); my $label_1_p2 = Gtk2::Label->new( 'Label 1' ); my $entry_1_p2 = Gtk2::Entry->new; my $label_2_p2 = Gtk2::Label->new( 'Label2' ); my $entry_2_p2 = Gtk2::Entry->new; $entry_1_p2->set_text( $val{'var1'} ); $entry_2_p2->set_text( $val{'var2'} ); $vbox_p2->pack_start( $label_1_p2, 0, 0, 1 ); $vbox_p2->pack_start( $entry_1_p2, 0, 0, 1 ); $vbox_p2->pack_start( $label_2_p2, 0, 0, 1 ); $vbox_p2->pack_start( $entry_2_p2, 0, 0, 1 ); #the Third Page; my $table_p3 = Gtk2::Table->new( 4, 2, 0 ); my $label_1_p3 = Gtk2::Label->new( 'First Label' ); my $entry_1_p3 = Gtk2::Entry->new; my $label_2_p3 = Gtk2::Label->new( 'Second label' ); my $entry_2_p3 = Gtk2::Entry->new; my $label_3_p3 = Gtk2::Label->new( 'Third Label' ); my $entry_3_p3 = Gtk2::Entry->new; my $label_4_p3 = Gtk2::Label->new( 'Fourth Label' ); my $entry_4_p3 = Gtk2::Entry->new; $entry_1_p3->set_text( $val{'var3'} ); $entry_2_p3->set_text( $val{'var4'} ); $entry_3_p3->set_text( $val{'var5'} ); $entry_4_p3->set_text( $val{'var6'} ); # $entry_3_p3->set_editable( 0 ); # if no edit desired $table_p3->attach_defaults( $label_1_p3, 0, 1, 0, 1 ); $table_p3->attach_defaults( $entry_1_p3, 1, 2, 0, 1 ); $table_p3->attach_defaults( $label_2_p3, 0, 1, 1, 2 ); $table_p3->attach_defaults( $entry_2_p3, 1, 2, 1, 2 ); $table_p3->attach_defaults( $label_3_p3, 0, 1, 2, 3 ); $table_p3->attach_defaults( $entry_3_p3, 1, 2, 2, 3 ); $table_p3->attach_defaults( $label_4_p3, 0, 1, 3, 4 ); $table_p3->attach_defaults( $entry_4_p3, 1, 2, 3, 4 ); #the Fourth Page; my $vbox_p4 = Gtk2::VBox->new( 0, 1 ); my $table_1_p4 = Gtk2::Table->new( 4, 2, 0 ); my $frame_1_p4 = Gtk2::Frame->new( 'Some Cool Title' ); my $label_1_p4 = Gtk2::Label->new( 'First Label' ); my $entry_1_p4 = Gtk2::Entry->new; my $label_2_p4 = Gtk2::Label->new( 'Second Label' ); my $entry_2_p4 = Gtk2::Entry->new; $entry_1_p4->set_text( $val{'var7'} ); $entry_2_p4->set_text( $val{'var8'} ); $table_1_p4->attach_defaults( $label_1_p4, 0, 1, 0, 1 ); $table_1_p4->attach_defaults( $entry_1_p4, 1, 2, 0, 1 ); $table_1_p4->attach_defaults( $label_2_p4, 0, 1, 1, 2 ); $table_1_p4->attach_defaults( $entry_2_p4, 1, 2, 1, 2 ); $frame_1_p4->add( $table_1_p4 ); my $table_2_p4 = Gtk2::Table->new( 4, 2, 0 ); my $frame_2_p4 = Gtk2::Frame->new( 'Another Cool Title' ); my $label_3_p4 = Gtk2::Label->new( 'Third Label' ); my $entry_3_p4 = Gtk2::Entry->new; my $label_4_p4 = Gtk2::Label->new( 'Fourth Label' ); my $entry_4_p4 = Gtk2::Entry->new; $entry_3_p4->set_text( $val{'var9'} ); $entry_4_p4->set_text( $val{'var10'}); $table_2_p4->attach_defaults( $label_3_p4, 0, 1, 0, 1 ); $table_2_p4->attach_defaults( $entry_3_p4, 1, 2, 0, 1 ); $table_2_p4->attach_defaults( $label_4_p4, 0, 1, 1, 2 ); $table_2_p4->attach_defaults( $entry_4_p4, 1, 2, 1, 2 ); $frame_2_p4->add( $table_2_p4 ); $vbox_p4->pack_start( $frame_1_p4, 0, 0, 1 ); $vbox_p4->pack_start( $frame_2_p4, 0, 0, 1 ); #append pages $config_notebook->append_page( $vbox_p1, "File Stuff" ); $config_notebook->append_page( $vbox_p2, "More Items" ); $config_notebook->append_page( $table_p3, "More in a Table" ); $config_notebook->append_page( $vbox_p4, "Another Table Set" ); #add button to the main window my $button_accept = Gtk2::Button->new_with_mnemonic( "_Accept" ); my $button_cancel = Gtk2::Button->new_with_mnemonic( "_Cancel" ); #pack them into the dialog window $table_config->attach_defaults( $config_notebook, 0, 5, 0, 1 ); $table_config->attach_defaults( $button_accept, 1, 2, 2, 3 ); $table_config->attach_defaults( $button_cancel, 3, 4, 2, 3 ); $config_window->add( $table_config ); $config_window->show_all; #Button Functions $button_cancel->signal_connect( 'clicked' => sub { $config_window-> +destroy } ); $button_accept->signal_connect( 'clicked' => sub { my @settings = ( ( $val{'dir'} = $entry_1_p1->get_text ), ( $val{'file'} = $entry_2_p1->get_text ), ( $val{'check1'} = $checkbutton1->get_active() || 0 ), ( $val{'check2'} = $checkbutton2->get_active() || 0 ), ( $val{'check3'} = $checkbutton3->get_active() || 0 ), ( $val{'var1'} = $entry_1_p2->get_text ), ( $val{'var2'} = $entry_2_p2->get_text ), ( $val{'var3'} = $entry_1_p3->get_text ), ( $val{'var4'} = $entry_2_p3->get_text ), ( $val{'var5'} = $entry_3_p3->get_text ), ( $val{'var6'} = $entry_4_p3->get_text ), ( $val{'var7'} = $entry_1_p4->get_text ), ( $val{'var8'} = $entry_2_p4->get_text ), ( $val{'var9'} = $entry_3_p4->get_text ), ( $val{'var10'} = $entry_4_p4->get_text ), ); save_default(); $config_window->destroy; } ); return 1; } #Exit sub exit_it { Gtk2->main_quit; return 0; } #save to DATA when apply is clicked sub save_default { open(SELF,"+<$0")||die $!; while(<SELF>){last if /^__DATA__/} truncate(SELF,tell SELF); foreach my $key(sort keys %val ) { print SELF $key.'='.$val{$key}."\n"; #default line ending } truncate(SELF,tell SELF); close SELF; } sub check_button_callback1 { my ($button) = @_; if ($button->get_active) { # if control reaches here, the check button is on $icon->set_from_pixbuf ($icon{'r'}); $tooltip->set_tip( $trayicon, "Mode RED" ); } else { # if control reaches here, the check button is off $icon->set_from_pixbuf ($icon_cur); $tooltip->set_tip( $trayicon, "Mode NORMAL" ); } } sub check_button_callback2 { my ($button) = @_; if ($button->get_active) { # if control reaches here, the check button is on $icon->set_from_pixbuf ($icon{'y'}); $tooltip->set_tip( $trayicon, "Mode YELLOW" ); } else { # if control reaches here, the check button is off $icon->set_from_pixbuf ($icon_cur); $tooltip->set_tip( $trayicon, "Mode NORMAL" ); } } sub check_button_callback3 { my ($button) = @_; if ($button->get_active) { # if control reaches here, the check button is on $icon->set_from_pixbuf ($icon{'b'}); $tooltip->set_tip( $trayicon, "Mode BLUE" ); } else { # if control reaches here, the check button is off $icon->set_from_pixbuf ($icon_cur); $tooltip->set_tip( $trayicon, "Mode NORMAL" ); } } sub build_icons { my %data; $data{ 'r' } = decode_base64( 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+ +gvaeTAAAACXBI WXMAAAsTAAALEwEAmpwYAAAB00lEQVRYw+3WP2gUURAG8N++iwoRxRSilSiiHN4iWJhe7M +TCQiJW IqiFtUIQQWyENNpZxkYxjVgKNnb24hUGQe0s0gj+PcztWtw7WC63d2TJbQrvwbK8x2Pmm2 +++mXlJ nue2cwXbvKYApgBmqsFON55l7f8rBQv4gm/xv1AVQLLpRtSjfx2NwmkXM1XSUJWBxpj9tA +wnDuDj mP2E+wDH+yKun4Ge0pPCB3ndjegXslh+GfK6NfBjYF8jgJDux+7trIIz2DXYUesB0It+ea +jDkC7X wcBpzJbk/KKQvhbSxiQBPB8hug5O4s5kAIT0K/ZE54NRZtiLOdwU0qtbByCkTSH9hANDor ++BD9FO wM4IYinqZUsYeIHDBbX3o7+NFVwvtPXv8c4+fBbSZrUHSQ/9I5yLxgZVfx6vYjkmuILHQ5 +pTNw6q a7L223IAPYfHcBZHo+MyCluR9h2R8k68O4unmB82QbCKl3iHN3GurCV50rqEyziCZjQ6bD +2JrKwi kbU7Bcbm8BMHcRcXSgLo072O93iQ5ElrXB/v4iEWo+PuiLdiP1UnYsOaH2P7zygAv7Eka9 ++vMC8S HMJf3IvVUiLCjQDWcAvPSqPdPKAGTsXULBYqKf8H7mhziLP34moAAAAASUVORK5CYII=' ); $data{ 'g' } = decode_base64( 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+ +gvaeTAAAACXBI WXMAAAsTAAALEwEAmpwYAAABwUlEQVRYw+2VvS8EURTFf8/4CKJQTKOgEIlEqFCIRnSUhE +6iUOhV Go3Kf0ChkiipNWqFQuErEtFJSEREfO7O0dyNid2Z3X3sbmHfy82bdzPv3TPnnnvHSaKWo4 +EajzqA OoBGn0MOl+cT+l8pmANugEdb53wBuHIbkdGfAYKYOws0+qTBl4GgyL5ehhUHcFVkX3ERfm +9jraAq IrQgLmbewX+TghcgsvKLAFVbA895xFQLgMOFQHstq2ACaMnD5TsklWyIEPGMiFDe3C7nrp +yVy8AI 0JaQ81mHO3C4oJIp2E0R3TswBKxWJAWIW6M+W4D+LOLd7BaxWPq9xQP3I64teITIxAIvIc +7t+dPW N8QdIvwrAKex4PG5guhAjMd8T8bCh4m13wuAqX0H8ZCg+GlEA6IV0YZYLvBOZKycIcaSAD +hJuebS B0wCvcAUECbIZgC4AJqAZhNfaNWxA4wWOBMBl8AecAIc2n/lHsQ8Yh9xYvQlzW3EIKIZ0f +KDsU7z dyO2TANKYCWyFB0jZkgJmJsZxIZRHqRoBYQzG0AclXD3axqAF8SaT3czED2ILsRmKoQCrj +vEQtrX egAKEMOI9R9lHH0BkYPO/OE8jeIAAAAASUVORK5CYII=' ); $data{ 'b' } = decode_base64( 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTA +AAACXBI WXMAAAsTAAALEwEAmpwYAAAB3UlEQVRYw+3WPYhTQRQF4G+SuIKiuEXAShRRFhcEC7cXO7 +GwkBUr EdTCUhQWyUMkINhoZ7k2ijZip2BjZy82LhbaWaQR/xc3MzYTeMT8sGGTFGbg8ZjhzZwz59 +577gsp JdMcFVMeMwIzArVRNhUp/rPWDJX/KwTL+IQv+b08KoGwWSPK8m+gWlpuozZKGEZVoDpkPi +vDsRP4 MGQ+Xh/A4U4ST1yBnOmh9ECatBH9RMzlF5EmnQPfu+aTI1CkWMfOaVbBCWzvdtSJEMi3X+ +0FWKS4 OgkFjmNHn5ifLVJ8VaRYHSeBJwOSbh1HcXMsBIoUP2NXBu++ZcRuzONqkeLFLWvHRYoLeI +H9JbAO gSu4hoXcomtZia9YbIZKaysUeFYCDyXwG3iKyyVb/5a/2YOPmfzmFcjZfh+n8mHdWX8aL3 +M5BlzA gx7m1M6N6lIzVN70JZABD+EkDmbgeh/Si3iPbZjLktdzdTzCUo89EWt4jrd4nftKKzRi+x +zO40CO 5Vwf4IdZlTWEZqislxSbxw/sRQNn+lygI/cG3uFOaMT2MB9v4x5WMnB7wL9iJ1RHsmEtDT +n79yAC v3C3GSq3R+gXAfvwB7dytfROwh4EWriOx/1uOwKhKo7l0KyUKin9BYeXmN9loYmUAAAAAE +lFTkSu QmCC'); $data{ 'y' } = decode_base64( 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+ +gvaeTAAAACXBI WXMAAAsTAAALEwEAmpwYAAAB2UlEQVRYw+3WP2gUQRQG8N/mokJEMRyi2UIUUQ4FwcL0Yi +cWFhKx EkEtrBWCCGIjpNHOMjaKNmIp2NjZi41BUKuzSBYE/x7mbi12DpbL7R1ZcnuFN7AsM8y897 +3vvffN RGmaGueYMuYxATABMF3qVBJvXKs3/68ULOALvoX/QlkA0aaFKKN/HbXcahvTZdJQloHakP +mkDUcO 4OOQ+Yh1gKPdIq6egazSo9wHadVC9Aud0H4dpFXXwI+eeYUAkngvdo6zC05jR6+iVgMgi3 +65r8Mk Xq6CgVOYKcj5BUn8WhLXRgng2YCia+EEbo8GQBJ/xa7gvDfKDnZjFjck8ZWtA5DEDUn8Cf +v6RH8d H4KdKWwPIJZCvWwJAy9wMFft3ehv4Tmu5WT9e9izB58lcaPcgyRD/xBng7Heqj+HV6EdI1 +zGoz7i 1A4X1VX15ttiAJnDIziDw8FxEYXHA+3bAuWtsHcGTzDf50wHK3iJd3gT7pXVKF2bu4hLOI +RGMNpv PA6srCBSb7ZyjM3iJ/bjDs4XBNClex3vcT9K1+aG6XgbD7AYHLcHvBW7qToWBGt+iO0/gw +D8xpJ6 816J+yLCAfzF3dAtBUW4EcAqbuJpYbSbB1TDyZCaxVwnpf8AkwJ+jVuvoFMAAAAASUVORK +5CYII=' ); # this properly renders them to pixbuf foreach my $key ( keys %data ) { $icon{ $key } = do { my $loader = Gtk2::Gdk::PixbufLoader->new(); $loader->write( $data{ $key } ); $loader->close(); $loader->get_pixbuf(); }; } } __DATA__ |
|
---|