Tuesday, December 22, 2009

Reentrant perldoc help

Just want to elaborate on a tip that I gave back in March.
I have a somehow standard way of displaying help for my scripts:
I display the POD data using the $0 perl internal variable.
$0 (or $PROGRAM_NAME if you are using the use English; pragma) "contains the name of the file containing the Perl script being executed", to quote the Camel book.

The help section is printed on screen if a script requires a parameter but none has been provided. I used to write it this way for a command line accepting only one parameter:
sub get_help {
system("perldoc $0");
exit;
}
my $arg = $ARGV[0] || &get_help;

However, I recently ran into a slight difficulty.
On Windows, $0 contains the disk letter followed by the complete path and finally the file name. For example:
C:\Users\Damien\Desktop\test coverage\Perl Scripts

Notice the white spaces in the directory names.
When trying to perldoc this particular file name, I would get an error stating that there is no documentation for "C:\Users\Damien\Desktop\test", "coverage\Perl" nor "Scripts".

My first reaction was simply to surround the $0 file name with "" as shown below:
sub get_help {
system("perldoc \"$0\"");
exit;
}
my $arg = $ARGV[0] || &get_help;

[Added Dec. 29th]
However, an enlightened reader pointed me to the best solution:
sub get_help {
system("perldoc", $0);
exit;
}
my $arg = $ARGV[0] || &get_help;

Using the list argument form of the system function avoids relying on the shell to run your command.

Reentrant perldoc help

Just want to elaborate on a tip that I gave back in March.
I have a somehow standard way of displaying help for my scripts:
I display the POD data using the $0 perl internal variable.
$0 (or $PROGRAM_NAME if you are using the use English; pragma) "contains the name of the file containing the Perl script being executed", to quote the Camel book.

The help section is printed on screen if a script requires a parameter but none has been provided. I used to write it this way for a command line accepting only one parameter:
sub get_help {
system("perldoc $0");
exit;
}
my $arg = $ARGV[0] || &get_help;

However, I recently ran into a slight difficulty.
On Windows, $0 contains the disk letter followed by the complete path and finally the file name. For example:
C:\Users\Damien\Desktop\test coverage\Perl Scripts

Notice the white spaces in the directory names.
When trying to perldoc this particular file name, I would get an error stating that there is no documentation for "C:\Users\Damien\Desktop\test", "coverage\Perl" nor "Scripts".

My first reaction was simply to surround the $0 file name with "" as shown below:
sub get_help {
system("perldoc \"$0\"");
exit;
}
my $arg = $ARGV[0] || &get_help;

[Added Dec. 29th]
However, an enlightened reader pointed me to the best solution:
sub get_help {
system("perldoc", $0);
exit;
}
my $arg = $ARGV[0] || &get_help;

Using the list argument form of the system function avoids relying on the shell to run your command.

Monday, September 14, 2009

Installing Padre on Windows Vista

Last week, I (re-)installed Padre on my Windows Vista machine. Padre is a development environment geared towards Perl programmers, with a focus on Perl beginners.
I use the Strawberry Perl distribution (5.10.0.6) and read that it was a breeze to install Padre.
Well, it almost is.
I opened the cpan shell (typing cpan in a DOS shell is all it takes if C:\strawberry\perl\bin is in your PATH).
The regular install (install Padre) failed because of a dependency on Wx::Perl::ProcessStream not building. Just force install that module (force install Wx::Perl::ProcessStream) then install Padre (a ticket has already been written for the issue).
It works like a charm, no need to force install Padre anymore!
So if you are a Perl beginner and want to use an editor written in Perl + suited for Perl tasks, you have no excuse anymore.

If you already have Padre and want to update it to version 0.46 released today (September 14th 2009), type upgrade Padre in your cpan shell. Better yet, you can update directly from the Padre Plugins menu: Plugins>Module Tools>Install CPAN Module and type Padre.

Installing Padre on Windows Vista

Last week, I (re-)installed Padre on my Windows Vista machine. Padre is a development environment geared towards Perl programmers, with a focus on Perl beginners.
I use the Strawberry Perl distribution (5.10.0.6) and read that it was a breeze to install Padre.
Well, it almost is.
I opened the cpan shell (typing cpan in a DOS shell is all it takes if C:\strawberry\perl\bin is in your PATH).
The regular install (install Padre) failed because of a dependency on Wx::Perl::ProcessStream not building. Just force install that module (force install Wx::Perl::ProcessStream) then install Padre (a ticket has already been written for the issue).
It works like a charm, no need to force install Padre anymore!
So if you are a Perl beginner and want to use an editor written in Perl + suited for Perl tasks, you have no excuse anymore.

If you already have Padre and want to update it to version 0.46 released today (September 14th 2009), type upgrade Padre in your cpan shell. Better yet, you can update directly from the Padre Plugins menu: Plugins>Module Tools>Install CPAN Module and type Padre.

Saturday, September 5, 2009

Learning Perl for cheap!

Brian d foy announced on his blog a price drop on O'Reilly Perl books, including his own "Learning Perl" book that he co-wrote with Randal Schwartz and Tom Phoenix.
With the discount code found in the comment section, the grand total is... $6.49 for a must have in every programmer's library!
Credits to RAT for announcing the news first.

Learning Perl for cheap!

Brian d foy announced on his blog a price drop on O'Reilly Perl books, including his own "Learning Perl" book that he co-wrote with Randal Schwartz and Tom Phoenix.
With the discount code found in the comment section, the grand total is... $6.49 for a must have in every programmer's library!
Credits to RAT for announcing the news first.

Tuesday, July 14, 2009

Perl modules of online MMORPG

What happened to MTG_Studio?
I was distraught to see that my favorite tool to manage a Magic: The Gathering card collection has been suspended (probably by Wizard of the Coast). Hopefully it is sadly temporary (that's what Beaker keeps telling me anyway) but I will not bet much on that. If anyone has news, please let me know.

That event prompted me to wonder if there was any module on CPAN dealing with MTG.
I was surprised not to find any, so I may just have to do something about that.

MMORPG love from Perl?
On the other hand, I found a few modules related to massively multi player role playing games.
  • World Of Warcraft
- Games::WoW::Armory - Access to the WoW Armory
- Bot::BasicBot::Pluggable::Module::WoWPVP - Fetch information about pvp grades for World Of Warcraft
- WWW::Wow::RealmStatus - The great new WWW::Wow::RealmStatus!
- WoW::Wiki - Perl extension to parse WoW wikki markup
  • Lord of the Rings Online
- There is no Perl module yet but a LOTRO Web API exists!
  • EverQuest
- Games::EverQuest::LogLineParser - Perl extension for parsing lines from the EverQuest log file.
- Other Perl scripts exist to complement the LogLineParser module
  • Eve Online
- Games::EveOnline::API - A simple Perl wrapper around the EveOnline XML API.
- WebService::EveOnline -- a wrapper intended to (eventually) provide a useful interface to the MMORPG game, "Eve Online"
  • Neverwinter Nights (not really an MMORPG)
- Games::NeverwinterNights::Query - Query Perl class to query a Neverwint Nights Server
  • F.E.A.R. (not really an MMORPG)
- Games::FEAR::Log - Log analysis tool for F.E.A.R. dedicated servers

Does anyone out there have written or used an online PC game-related module?

Perl modules of online MMORPG

What happened to MTG_Studio?
I was distraught to see that my favorite tool to manage a Magic: The Gathering card collection has been suspended (probably by Wizard of the Coast). Hopefully it is sadly temporary (that's what Beaker keeps telling me anyway) but I will not bet much on that. If anyone has news, please let me know.

That event prompted me to wonder if there was any module on CPAN dealing with MTG.
I was surprised not to find any, so I may just have to do something about that.

MMORPG love from Perl?
On the other hand, I found a few modules related to massively multi player role playing games.
  • World Of Warcraft
- Games::WoW::Armory - Access to the WoW Armory
- Bot::BasicBot::Pluggable::Module::WoWPVP - Fetch information about pvp grades for World Of Warcraft
- WWW::Wow::RealmStatus - The great new WWW::Wow::RealmStatus!
- WoW::Wiki - Perl extension to parse WoW wikki markup
  • Lord of the Rings Online
- There is no Perl module yet but a LOTRO Web API exists!
  • EverQuest
- Games::EverQuest::LogLineParser - Perl extension for parsing lines from the EverQuest log file.
- Other Perl scripts exist to complement the LogLineParser module
  • Eve Online
- Games::EveOnline::API - A simple Perl wrapper around the EveOnline XML API.
- WebService::EveOnline -- a wrapper intended to (eventually) provide a useful interface to the MMORPG game, "Eve Online"
  • Neverwinter Nights (not really an MMORPG)
- Games::NeverwinterNights::Query - Query Perl class to query a Neverwint Nights Server
  • F.E.A.R. (not really an MMORPG)
- Games::FEAR::Log - Log analysis tool for F.E.A.R. dedicated servers

Does anyone out there have written or used an online PC game-related module?

Wednesday, June 10, 2009

Adding a menu bar to the twitter GUI: part 5

Updating to the new Net::Twitter::Lite API
Marc Mims updated the API of his twitter's interface module for Perl.
There are two versions:

In order to use the Lite version (largely sufficient for my modest needs), only trivial changes are needed in the twit_GUI.pl script:
  • replace use Net::Twitter by use Net::Twitter::Lite (duh!)
  • replace calls to Net::Twitter->new() by calls to Net::Twitter::Lite->new()
This ensures that I am now using a module that is actively supported.

Adding a menu bar
A menu bar can only be added to a Frame. According to the wxPerl docs,

A frame is a window whose size and position can (usually) be changed by the user. It usually has thick borders and a title bar, and can optionally contain a menu bar, toolbar and status bar. A frame can contain any window that is not a frame or dialog.
So, let's replace the Dialog occurences with Frame in the script.
The MyApp modules becomes:

##############################################################################
#
package MyApp;
#
##############################################################################
use vars qw(@ISA);
@ISA=qw(Wx::App); # this tells Perl to look for unknown methods in the Wx::App module

use Wx qw(wxDefaultSize wxDefaultPosition);

sub OnInit {
my($this) = @_;
my $frame = MyFrame->new('Twit', wxDefaultPosition);
# set it as top window (so the app will automatically close when
# the last top window is closed)
$this->SetTopWindow($frame);
$frame->Show(1);
1;
}

package MyDialog becomes package MyFrame:
##############################################################################
#
package MyFrame;
#
##############################################################################
use vars qw(@ISA);

@ISA=qw(Wx::Frame);

use Wx::Event qw(EVT_BUTTON EVT_CLOSE EVT_TEXT EVT_TEXT_MAXLEN);
use Wx qw(:sizer
:statictext
wxDefaultPosition
wxDefaultSize
wxDefaultValidator
wxDEFAULT_DIALOG_STYLE
wxID_OK
wxOK
wxRESIZE_BORDER
wxTE_MULTILINE
);

use constant MAX_POST_LENGTH => 140;

sub new {
my $class = shift;
my $password_file;

# Main window
my $form_width = 480;
my $form_height = 195;
# Array of line numbers used on the dialog window
my @ylines = (0, 10, 20, 40, 80, 100);

my $this = $class->SUPER::new(
undef, # parent
-1, # id
$_[0], # title
$_[1], # position [x, y]
[$form_width, $form_height] # size [width, height]
);

# Display the Wx icon on the application window
$this->SetIcon(Wx::GetWxPerlIcon());

#--------------------------------------------------------------------------
# Menu Bar
#--------------------------------------------------------------------------
my $file_menu = Wx::Menu->new('');
my $menu_bar = Wx::MenuBar->new();
$menu_bar->Append($file_menu, '&File');
$this->SetMenuBar($menu_bar);
...
Increase the form height by 10 pixels (up to 195) to make room for the menu bar.
And... voilĂ !

First version of twit script using wxperlEr... not too pretty, let's go back to the doc. Oh wait, what is that about panels?
A panel is a window on which controls are placed. It is usually placed within a frame. It contains minimal extra functionality over and above its parent class wxWindow; its main purpose is to be similar in appearance and functionality to a dialog, but with the flexibility of having any window as a parent.
Ah ah! The trick is to place a wxPanel object inside the frame and then relate every existing GUI element to this panel as the parent window. Previously, all the elements (button, text dialog, etc.) had the wxFrame as a parent window, hence the dark grey background. Just add this line before the Menu Bar code:
    # Create a panel where to place GUI elements
my $panel = Wx::Panel->new($this, -1);
and replace the $this argument by $panel everywhere you need to set the parent ID.

Now we've got a menu bar element. You can fill it by appending menu elements to it as is shown in the code below. Each menu element can be appended in turn with menu options. The wxFrame::SetMenuBar() method displays a given menu bar.
    #--------------------------------------------------------------------------
# Menu Bar
#--------------------------------------------------------------------------
my $menu_bar = Wx::MenuBar->new();

my $file_menu = Wx::Menu->new();
$file_menu->Append(11, 'E&xit');
$menu_bar->Append($file_menu, '&File');

my $help_menu = Wx::Menu->new();
$help_menu->Append(21, '&About');
$menu_bar->Append($help_menu, '&Help');

# wxFrame method to show a given menu bar
$this->SetMenuBar($menu_bar);
The & in the string (eg: 'E&xit') shows what shortcut will be used in combination with the Alt key (eg: Alt + x for 'E&xit').
To link an event with a menu selection, use the EVT_MENU event:
    EVT_MENU($this, 11, \&OnQuit);
EVT_MENU($this, 21, \&OnAbout)
The callback methods can be implemented as such:
sub OnQuit {
my $this = shift;
$this->Close( 1 );
}

sub OnAbout {
my $this = shift;
use Wx qw(wxOK wxCENTRE);
Wx::MessageBox("twit_GUI $VERSION\n$version_date\n(c)DamienLearnsPerl", # text
"About", # title bar
wxOK|wxCENTRE, # buttons to display on form
$this # parent
);
}
End result under Vista? Tadaaa!

Improvement layout using panelsNext step:
- Load/save password files

Find the whole new source code here.

Adding a menu bar to the twitter GUI: part 5

Updating to the new Net::Twitter::Lite API
Marc Mims updated the API of his twitter's interface module for Perl.
There are two versions:

In order to use the Lite version (largely sufficient for my modest needs), only trivial changes are needed in the twit_GUI.pl script:
  • replace use Net::Twitter by use Net::Twitter::Lite (duh!)
  • replace calls to Net::Twitter->new() by calls to Net::Twitter::Lite->new()
This ensures that I am now using a module that is actively supported.

Adding a menu bar
A menu bar can only be added to a Frame. According to the wxPerl docs,

A frame is a window whose size and position can (usually) be changed by the user. It usually has thick borders and a title bar, and can optionally contain a menu bar, toolbar and status bar. A frame can contain any window that is not a frame or dialog.
So, let's replace the Dialog occurences with Frame in the script.
The MyApp modules becomes:

##############################################################################
#
package MyApp;
#
##############################################################################
use vars qw(@ISA);
@ISA=qw(Wx::App);   # this tells Perl to look for unknown methods in the Wx::App module

use Wx qw(wxDefaultSize wxDefaultPosition);

sub OnInit {
    my($this) = @_;
    my $frame = MyFrame->new('Twit', wxDefaultPosition);
    # set it as top window (so the app will automatically close when 
    # the last top window is closed)
    $this->SetTopWindow($frame);
    $frame->Show(1);
    1;
}

package MyDialog becomes package MyFrame:
##############################################################################
#
package MyFrame;
#
##############################################################################
use vars qw(@ISA);

@ISA=qw(Wx::Frame);

use Wx::Event qw(EVT_BUTTON EVT_CLOSE EVT_TEXT EVT_TEXT_MAXLEN);
use Wx qw(:sizer
          :statictext
          wxDefaultPosition
          wxDefaultSize
          wxDefaultValidator
          wxDEFAULT_DIALOG_STYLE
          wxID_OK
          wxOK
          wxRESIZE_BORDER
          wxTE_MULTILINE
          );

use constant MAX_POST_LENGTH => 140;

sub new {
    my $class = shift;
    my $password_file;

    # Main window
    my $form_width  = 480;
    my $form_height = 195;
    # Array of line numbers used on the dialog window
    my @ylines = (0, 10, 20, 40, 80, 100);

    my $this = $class->SUPER::new(
                            undef,  # parent
                            -1,     # id
                            $_[0],  # title
                            $_[1],  # position [x, y]
                           [$form_width, $form_height] # size [width, height]
                           );

    # Display the Wx icon on the application window
    $this->SetIcon(Wx::GetWxPerlIcon());

    #--------------------------------------------------------------------------
    # Menu Bar
    #--------------------------------------------------------------------------     
    my $file_menu = Wx::Menu->new('');
    my $menu_bar = Wx::MenuBar->new();
    $menu_bar->Append($file_menu, '&File');
    $this->SetMenuBar($menu_bar);
    ...
Increase the form height by 10 pixels (up to 195) to make room for the menu bar.
And... voilĂ !

First version of twit script using wxperlEr... not too pretty, let's go back to the doc. Oh wait, what is that about panels?
A panel is a window on which controls are placed. It is usually placed within a frame. It contains minimal extra functionality over and above its parent class wxWindow; its main purpose is to be similar in appearance and functionality to a dialog, but with the flexibility of having any window as a parent.
Ah ah! The trick is to place a wxPanel object inside the frame and then relate every existing GUI element to this panel as the parent window. Previously, all the elements (button, text dialog, etc.) had the wxFrame as a parent window, hence the dark grey background. Just add this line before the Menu Bar code:
    # Create a panel where to place GUI elements
    my $panel = Wx::Panel->new($this, -1);
and replace the $this argument by $panel everywhere you need to set the parent ID.

Now we've got a menu bar element. You can fill it by appending menu elements to it as is shown in the code below. Each menu element can be appended in turn with menu options. The wxFrame::SetMenuBar() method displays a given menu bar.
    #--------------------------------------------------------------------------
    # Menu Bar
    #-------------------------------------------------------------------------- 
    my $menu_bar = Wx::MenuBar->new();

    my $file_menu = Wx::Menu->new(); 
    $file_menu->Append(11, 'E&xit');
    $menu_bar->Append($file_menu, '&File');
 
    my $help_menu = Wx::Menu->new();
    $help_menu->Append(21, '&About');
    $menu_bar->Append($help_menu, '&Help');

    # wxFrame method to show a given menu bar
    $this->SetMenuBar($menu_bar);
The & in the string (eg: 'E&xit') shows what shortcut will be used in combination with the Alt key (eg: Alt + x for 'E&xit').
To link an event with a menu selection, use the EVT_MENU event:
    EVT_MENU($this, 11, \&OnQuit);
    EVT_MENU($this, 21, \&OnAbout)
The callback methods can be implemented as such:
sub OnQuit {
    my $this = shift;
    $this->Close( 1 );
}

sub OnAbout {
    my $this = shift;
    use Wx qw(wxOK wxCENTRE);
    Wx::MessageBox("twit_GUI $VERSION\n$version_date\n(c)DamienLearnsPerl",  # text
                   "About",                   # title bar
                   wxOK|wxCENTRE,             # buttons to display on form
                   $this                      # parent
                   );          
}
End result under Vista? Tadaaa!

Improvement layout using panelsNext step:
- Load/save password files

Find the whole new source code here.

Monday, June 1, 2009

Perl Iron Man

Ok, I've failed in the Perl Iron Man contest: it has been more than 10 days since last post :(
But after the first thrills due to the project's novelty, there are not a lot of incentives coming from the Perl Iron Man organization (unless I've somehow missed it?):
I was expecting leaderboards, post of the month, constructive critiques of blogs by writing professional, cheering pom pom girls, etc.
I'm feeling a little bit let down right now...

Perl Iron Man

Ok, I've failed in the Perl Iron Man contest: it has been more than 10 days since last post :(
But after the first thrills due to the project's novelty, there are not a lot of incentives coming from the Perl Iron Man organization (unless I've somehow missed it?):
I was expecting leaderboards, post of the month, constructive critiques of blogs by writing professional, cheering pom pom girls, etc.
I'm feeling a little bit let down right now...

Sunday, May 17, 2009

Adding a wxPerl GUI to the twitter script: part 4 - Putting it all together

Note: this article is part of a series.

Today I've finished version 1.0.0 of twit_GUI.pl. Nothing fancy, I just added the Net:Twitter part from the console version of twit.pl to the existing User Interface. I can now post to twitter.com or identi.ca from a graphical interface Perl program. That's how cool I am.

New since version 0.0.2
I removed the event handling on EVT_TEXT_MAXLEN because I realized that I didn't need to display a message when I reached 140 characters. The fact that you cannot type anything after the limit is enough. Keeping the message would have been annoying in fact...

A "gentle" analysis from Perl::Critic made me change the way I was calling the open function.
Before:
open(LOGINFILE, $password_file)
After:
open(my $logfile_handle, '<', $password_file)

Explanation:
- The LOGINFILE file handle is dangerous because as such, LOGINFILE is declared as a global. It could already be used as another file's handle.
- The '<' second argument indicates that the file is open in read-only mode. It is easier to see that than in the previous writing and it avoids bugs for file names starting with '<' or '>' (see the full explanation).

I also replaced the double quotes (") by single quotes (') everywhere variable interpolation was not needed.
Speaking about quotes, I removed those surrounding the identifiers of the hash elements.
Speaking about hashes, I really enjoy how flexible they can be used for structuring your data. Take for example the %login hash in $this->{login}. It lets me organize data as such:

%login = {
user_name => { twitter => "Twitter.com user name",
identica => "Identica user name"
},
password => { twitter => "Twitter.com password",
identica => "Identica password"
}
}
And I can add to the hash construct as I need it. Of course, if not documented, I can see that hashes contructed in this manner could get pretty tough to maintain.

What's next?
There are improvements to be made to the twit_GUI script, especially concerning the password handling. Today I have a hard-coded path inside the script which is very ugly. I am not proud of myself but the Perl Iron Man's deadline is arriving soon and I want to stay in the contest ;)
I will apply the finishing touch in a next post.

I am not sure if I should keep the POD comments. I was thinking about throwing it all inside an "About" menu item. Maybe I can display the POD info directly in a MessageBox, in a reentrant fashion.

I will not plaster the walls of this blog with yet another listing but as always you can find all script versions on the DamienLearnsPerl's companion site.


Adding a wxPerl GUI to the twitter script: part 4 - Putting it all together

Note: this article is part of a series.

Today I've finished version 1.0.0 of twit_GUI.pl. Nothing fancy, I just added the Net:Twitter part from the console version of twit.pl to the existing User Interface. I can now post to twitter.com or identi.ca from a graphical interface Perl program. That's how cool I am.

New since version 0.0.2
I removed the event handling on EVT_TEXT_MAXLEN because I realized that I didn't need to display a message when I reached 140 characters. The fact that you cannot type anything after the limit is enough. Keeping the message would have been annoying in fact...

A "gentle" analysis from Perl::Critic made me change the way I was calling the open function.
Before:
open(LOGINFILE, $password_file)
After:
open(my $logfile_handle, '<', $password_file)

Explanation:
- The LOGINFILE file handle is dangerous because as such, LOGINFILE is declared as a global. It could already be used as another file's handle.
- The '<' second argument indicates that the file is open in read-only mode. It is easier to see that than in the previous writing and it avoids bugs for file names starting with '<' or '>' (see the full explanation).

I also replaced the double quotes (") by single quotes (') everywhere variable interpolation was not needed.
Speaking about quotes, I removed those surrounding the identifiers of the hash elements.
Speaking about hashes, I really enjoy how flexible they can be used for structuring your data. Take for example the %login hash in $this->{login}. It lets me organize data as such:

%login = {
user_name => { twitter => "Twitter.com user name",
identica => "Identica user name"
},
password => { twitter => "Twitter.com password",
identica => "Identica password"
}
}
And I can add to the hash construct as I need it. Of course, if not documented, I can see that hashes contructed in this manner could get pretty tough to maintain.

What's next?
There are improvements to be made to the twit_GUI script, especially concerning the password handling. Today I have a hard-coded path inside the script which is very ugly. I am not proud of myself but the Perl Iron Man's deadline is arriving soon and I want to stay in the contest ;)
I will apply the finishing touch in a next post.

I am not sure if I should keep the POD comments. I was thinking about throwing it all inside an "About" menu item. Maybe I can display the POD info directly in a MessageBox, in a reentrant fashion.

I will not plaster the walls of this blog with yet another listing but as always you can find all script versions on the DamienLearnsPerl's companion site.


Saturday, May 9, 2009

Adding a wxPerl GUI to the twitter script: part 3 - Polishing up the UI

Last time I wrote about the WxPerl twitter GUI, I made a nice little drawing about what I wanted to achieve.
The first try wasn't too far off but a few functionalities are still missing. One of them is the real-time updating of the remaining text characters (from a limit of 140). Obviously, what I need is an event on a key stroke to update the number of characters.

WxEvents documentation
Command events can be handled by macros listed in the WxWidget/WxPerl's manual under the wxCommandEvent page.
Of interest to the twitter GUI script are:

EVT_TEXT(id, func) Process a wxEVT_COMMAND_TEXT_UPDATED command, which is generated by a wxTextCtrl control.
EVT_TEXT_MAXLEN(id, func) Process a wxEVT_COMMAND_TEXT_MAXLEN command, which is generated by a wxTextCtrl control when the user tries to enter more characters into it than the limit previously set with SetMaxLength.

The first macro (EVT_TEXT) can be linked to the TextControl box where the message is being typed. Whenever a character is added or removed to the update message, the event handler will compute and display the remaining characters left before hitting the 140 limit.
The EVT_TEXT_MAXLEN macro will link the update message's TextControl to a subroutine that will be called when 140 characters have been inputted.

Code
When using WxPerl constants, the first thing to remember is to declare them at the beginning of the package.

use Wx::Event qw(EVT_BUTTON EVT_CLOSE EVT_TEXT EVT_TEXT_MAXLEN);

To find out the complete list of symbols, you can check the Wx source code in the lib/Wx/Wx_Exp.pm file located in your Perl directory. In this file, you will also be able to see all existing tags created to group related constants. For example, 'statictext' is defined as:

$EXPORT_TAGS{'statictext'} = [ qw(wxALIGN_LEFT
wxALIGN_CENTRE
wxALIGN_CENTER
wxALIGN_RIGHT
wxST_NO_AUTORESIZE
wxST_ELLIPSIZE_START
wxST_ELLIPSIZE_MIDDLE
wxST_ELLIPSIZE_END
wxST_MARKUP) ];

Writing the following line in your program:

use Wx qw(:statictext);

will import all nine constants listed above.

Coming back to our problem at hand, if we create a TextControl and StaticText objects like this

#
# Text control to enter message to twit
#
$this->{update_text} = Wx::TextCtrl->new(
$this, # parent window
-1, # control identifier
"Type your message here", # default text value
[20, $ylines[3]], # text control position [x, y]
[435, 35], # text control size [width, height]
wxTE_MULTILINE # style: wxTE_MULTILINE=The text control allows multiple lines
);

# A EVT_TEXT_MAXLEN event is generated when the number of characters in the update_text control
# reaches the maximum passed value.
$this->{update_text}->SetMaxLength(MAX_POST_LENGTH);

#
# Static text placed at the top-right position of the text control
#
Wx::StaticText->new(
$this, # parent
-1, # id
'characters left', # label
[$form_width - 95, $ylines[2]] # position [x, y]
);

#
# Static text displaying number of characters left before reaching the max amount
#
$this->{static_text}{'CharsLeft'} = Wx::StaticText->new(
$this, # parent
-1, # id
MAX_POST_LENGTH - $this->{update_text}->GetLastPosition, # label
[$form_width - 115, $ylines[2]], # position [x, y]
wxDefaultSize, # size
Wx::wxALIGN_RIGHT # style. Not implemented yet?
);

then calling the following event handlers

# Events associated to the update_text Control
# EVT_TEXT_MAXLEN is generated when the length of the Text control
# becomes larger than the value set by SetMaxLength
EVT_TEXT_MAXLEN($this, $this->{update_text}, \&MaxTextReached);

# EVT_TEXT is generated when a character is typed inside the Text control
# We use it to update the display of characters left for message
EVT_TEXT($this, $this->{update_text}, \&CountCharsLeft);

with the subroutines definition being:

sub CountCharsLeft {
my($this, $event) = @_;

# Update number of remaining characters = MAX - current text size
my $chars_left = MAX_POST_LENGTH - $this->{update_text}->GetLastPosition;
$this->{static_text}{'CharsLeft'}->SetLabel($chars_left);
}

sub MaxTextReached {
my($this, $event) = @_;

Wx::MessageBox(
"You have reached the maximum number of characters", # text
"Update too big", # title bar
wxOK, # buttons to display on form
$this # parent
);
}
then every time that {update_text} is edited, CountCharsLeft() will be called and will reset the label for {static_text}{'CharsLeft'} with the new value computed from {update_text}->GetLastPosition.
I couldn't achieve right-aligning the number of characters. From reading the Wx::Perl::Dialog source, I do not think that the style argument is supported yet.

The result?

Now one step further to accomplish my goal of being able to understand Padre's code once I put my nose in it...


Adding a wxPerl GUI to the twitter script: part 3 - Polishing up the UI

Last time I wrote about the WxPerl twitter GUI, I made a nice little drawing about what I wanted to achieve.
The first try wasn't too far off but a few functionalities are still missing. One of them is the real-time updating of the remaining text characters (from a limit of 140). Obviously, what I need is an event on a key stroke to update the number of characters.

WxEvents documentation
Command events can be handled by macros listed in the WxWidget/WxPerl's manual under the wxCommandEvent page.
Of interest to the twitter GUI script are:

EVT_TEXT(id, func) Process a wxEVT_COMMAND_TEXT_UPDATED command, which is generated by a wxTextCtrl control.
EVT_TEXT_MAXLEN(id, func) Process a wxEVT_COMMAND_TEXT_MAXLEN command, which is generated by a wxTextCtrl control when the user tries to enter more characters into it than the limit previously set with SetMaxLength.

The first macro (EVT_TEXT) can be linked to the TextControl box where the message is being typed. Whenever a character is added or removed to the update message, the event handler will compute and display the remaining characters left before hitting the 140 limit.
The EVT_TEXT_MAXLEN macro will link the update message's TextControl to a subroutine that will be called when 140 characters have been inputted.

Code
When using WxPerl constants, the first thing to remember is to declare them at the beginning of the package.

use Wx::Event qw(EVT_BUTTON EVT_CLOSE EVT_TEXT EVT_TEXT_MAXLEN);

To find out the complete list of symbols, you can check the Wx source code in the lib/Wx/Wx_Exp.pm file located in your Perl directory. In this file, you will also be able to see all existing tags created to group related constants. For example, 'statictext' is defined as:

$EXPORT_TAGS{'statictext'} = [ qw(wxALIGN_LEFT
wxALIGN_CENTRE
wxALIGN_CENTER
wxALIGN_RIGHT
wxST_NO_AUTORESIZE
wxST_ELLIPSIZE_START
wxST_ELLIPSIZE_MIDDLE
wxST_ELLIPSIZE_END
wxST_MARKUP) ];

Writing the following line in your program:

use Wx qw(:statictext);

will import all nine constants listed above.

Coming back to our problem at hand, if we create a TextControl and StaticText objects like this

#
# Text control to enter message to twit
#
$this->{update_text} = Wx::TextCtrl->new(
$this, # parent window
-1, # control identifier
"Type your message here", # default text value
[20, $ylines[3]], # text control position [x, y]
[435, 35], # text control size [width, height]
wxTE_MULTILINE # style: wxTE_MULTILINE=The text control allows multiple lines
);

# A EVT_TEXT_MAXLEN event is generated when the number of characters in the update_text control
# reaches the maximum passed value.
$this->{update_text}->SetMaxLength(MAX_POST_LENGTH);

#
# Static text placed at the top-right position of the text control
#
Wx::StaticText->new(
$this, # parent
-1, # id
'characters left', # label
[$form_width - 95, $ylines[2]] # position [x, y]
);

#
# Static text displaying number of characters left before reaching the max amount
#
$this->{static_text}{'CharsLeft'} = Wx::StaticText->new(
$this, # parent
-1, # id
MAX_POST_LENGTH - $this->{update_text}->GetLastPosition, # label
[$form_width - 115, $ylines[2]], # position [x, y]
wxDefaultSize, # size
Wx::wxALIGN_RIGHT # style. Not implemented yet?
);

then calling the following event handlers

# Events associated to the update_text Control
# EVT_TEXT_MAXLEN is generated when the length of the Text control
# becomes larger than the value set by SetMaxLength
EVT_TEXT_MAXLEN($this, $this->{update_text}, \&MaxTextReached);

# EVT_TEXT is generated when a character is typed inside the Text control
# We use it to update the display of characters left for message
EVT_TEXT($this, $this->{update_text}, \&CountCharsLeft);

with the subroutines definition being:

sub CountCharsLeft {
my($this, $event) = @_;

# Update number of remaining characters = MAX - current text size
my $chars_left = MAX_POST_LENGTH - $this->{update_text}->GetLastPosition;
$this->{static_text}{'CharsLeft'}->SetLabel($chars_left);
}

sub MaxTextReached {
my($this, $event) = @_;

Wx::MessageBox(
"You have reached the maximum number of characters", # text
"Update too big", # title bar
wxOK, # buttons to display on form
$this # parent
);
}
then every time that {update_text} is edited, CountCharsLeft() will be called and will reset the label for {static_text}{'CharsLeft'} with the new value computed from {update_text}->GetLastPosition.
I couldn't achieve right-aligning the number of characters. From reading the Wx::Perl::Dialog source, I do not think that the style argument is supported yet.

The result?

Now one step further to accomplish my goal of being able to understand Padre's code once I put my nose in it...


Sunday, May 3, 2009

AdSense integration to Google analytics and other news

AdSense
If you are a blogger with AdSense revenues, you can now integrate the AdSense stats to your Google Analytics account.
To do so, just follow those very simple instructions.

DamienLearnsPerl's April Analytics stats
  • Thanks to the Perl::Critic post appearing on Hacker News, there was a peak at 661 visits on the 28th of April!
  • The United States (728), France (98) and the UK (93) are the leading trio of visiting countries in April.
  • 2/3 of visitors use Firefox, IE (8.8%) is in third place behind Safari (10%)
  • 56% of visits originate from Windows, 21.5% from Linux and 20% from a Mac
Strawberry Perl
New versions are freshly available for both Perl 5.8.9 and 5.10.0.
Check out what's new!

The Perl Review

The last issue is already 2-week old. Get it there!

Perl IronMan
I entered the IronMan challenge, the brainchild of Matt Trout from Enlightened Perl. Check out all participating blogs in Planet Perl Iron Man!

Padre v0.34
Version 0.34 has been released. See the changelog here.

AdSense integration to Google analytics and other news

AdSense
If you are a blogger with AdSense revenues, you can now integrate the AdSense stats to your Google Analytics account.
To do so, just follow those very simple instructions.

DamienLearnsPerl's April Analytics stats
  • Thanks to the Perl::Critic post appearing on Hacker News, there was a peak at 661 visits on the 28th of April!
  • The United States (728), France (98) and the UK (93) are the leading trio of visiting countries in April.
  • 2/3 of visitors use Firefox, IE (8.8%) is in third place behind Safari (10%)
  • 56% of visits originate from Windows, 21.5% from Linux and 20% from a Mac
Strawberry Perl
New versions are freshly available for both Perl 5.8.9 and 5.10.0.
Check out what's new!

The Perl Review

The last issue is already 2-week old. Get it there!

Perl IronMan
I entered the IronMan challenge, the brainchild of Matt Trout from Enlightened Perl. Check out all participating blogs in Planet Perl Iron Man!

Padre v0.34
Version 0.34 has been released. See the changelog here.

Wednesday, April 29, 2009

Adding a wxPerl GUI to the twitter script: part 2 - Drawing the controls

Part 2 of this series will describe how to draw the GUI elements thanks to some wxPerl magic.

wxPerl Documentation
The documentation on wxPerl is rather sparse.
The best starting points are:
  • An excellent tutorial from Jouke Visser over at Perl.com where you learn that wx stands for Windows - X to show off the portability of the library.
  • Another series of tutorials from boo_radley at perlmonks.org
  • Those tutorial and a few more are already listed on the wxPerl site
  • By clicking on this link, you will start downloading example files from the wxPerl sourceforge repository. This is a very useful resource for the first-hand demo of a lot of controls.
  • The Wx module for Perl is of course downloadable from CPAN
  • When looking at the wxWidget doc, you can transpose to the wxPerl world by knowing that "in wxPerl all classes are named Wx::Something, so wxFrame is really Wx::Frame. Static methods are called Wx::ClassName::Method(). Global function named wxFunction() are accessible as Wx::Function()." [from the wxPerl manual]
Oooh the pretty drawings...
The code listing that I present here doesn't do anything useful. It just initializes some controls and places them on an application window.
However, I'll try to explain how to find the information so we can use any control supported by wxPerl.
If you go back to my previous post on the subject, you'll be reminded that the goal is to have a window that looks like that:
GUI drawing
The first step is to actually get the main window displayed.
This is not straightforward, here's one way to do it.

#!/usr/bin/perl
use Modern::Perl;
use Wx;

...

##############################################################################
#
package MyApp;
#
##############################################################################
use vars qw(@ISA);
@ISA=qw(Wx::App); # this tells Perl to look for inkown methods in the Wx::App module

use Wx qw(wxDefaultSize wxDefaultPosition);

sub OnInit {
my($this) = @_;
my $dialog = MyDialog->new("Twit", wxDefaultPosition);
$this->SetTopWindow($dialog);
$dialog->Show(1);
1;
}
...

##############################################################################
#
package main;
#
##############################################################################

my($app) = MyApp->new();
$app->MainLoop();
There's obviously a lot of code missing but I will talk a bit about packages as it is the first time that I am confronted to them (remember, I am learning Perl). There is a perldoc page on packages. To summarize greatly, the package keyword defines the beginning of a namespace (when used as package NAMESPACE).
By the way, I didn't invent the code construct above. I found it in the wxPerl demo from the "wxPerl-0.26-samples.zip" file that you can download from the document section of the official wxPerl's download page.
In the same section, you will find a link to download the html version of the wxWidget documentation.

Line 222, we create an instance of MyApp and enter the hot loop. We will exit the loop when the MyApp object is destroyed (as a result of a click on the exit button for example).

In Package MyApp starting at line 36, the @ISA=qw(Wx::App); line tells Perl to look for unknown methods in the Wx::App module.
The Wx::App::OnInit() routine is called at the creation of a Wx::App object. It will create the application's main window.
In this function, we call the new() method of the MyDialog package/namespace that is given below:
##############################################################################
#
package MyDialog;
#
##############################################################################

use vars qw(@ISA);

@ISA=qw(Wx::Dialog);

use Wx::Event qw(EVT_BUTTON EVT_CLOSE);
use Wx qw(:sizer
wxDefaultPosition
wxDefaultSize
wxDefaultValidator
wxDEFAULT_DIALOG_STYLE
wxID_OK
wxOK
wxRESIZE_BORDER
wxTE_MULTILINE
);

use constant MAX_POST_LENGTH => 140;

sub new {
my $class = shift;

# Main window
my $form_width = 480;
my $form_height = 175;
my $yline1 = 10;
my $yline2 = 30;
my $yline3 = 70;
my $yline4 = 90;

my $this = $class->SUPER::new(
undef, # parent
-1, # id
$_[0], # title
$_[1], # position [x, y]
[$form_width, $form_height] # size [width, height]
);

# Display the Wx icon on the application window
$this->SetIcon(Wx::GetWxPerlIcon());

#--------------------------------------------------------------------------
# Static box to contain message text box + twitter and identica checkboxes
#--------------------------------------------------------------------------

# A static box is a rectangle drawn around other panel items to denote a logical grouping of items.
Wx::StaticBox->new(
$this, # parent window
-1, # window identifier (-1: default)
'Status', # text to be displayed in the static box
[10, $yline1], # window position [x, y]
[$form_width-25, 120] # checkbox size [width, height]
);

# Text control to enter message to twit
$this->{text} = Wx::TextCtrl->new(
$this, # parent window
-1, # control identifier
"Type your message here", # default text value
[20, $yline2], # text control position [x, y]
[435, 35], # text control size [width, height]
wxTE_MULTILINE # style: wxTE_MULTILINE=The text control allows multiple lines
);

# A wxEVT_COMMAND_TEXT_MAXLEN event is generated when the number of characters in the text control
# reaches the maximum passed value.
$this->{text}->SetMaxLength(MAX_POST_LENGTH);

# Static text placed in front of the twitter and identica check boxes
Wx::StaticText->new(
$this, # parent
-1, # id
'Send to:', # label
[20, $yline3] # position [x, y]
);
# twitter check box: enabled by default
$this->{checkbox}{"twitter"} = Wx::CheckBox->new(
$this, # parent
-1, # id
'Twitter.com', # label
[80, $yline3] # position [x, y]
);
# Checked by default
$this->{checkbox}{"twitter"}->SetValue(1);

# identica check box: enabled by default
$this->{checkbox}{"identica"} = Wx::CheckBox->new(
$this, # parent
-1, # id
'Identi.ca', # label
[200, $yline3] # position [x, y]
);
# Checked by default
$this->{checkbox}{"identica"}->SetValue(1);

#--------------------------------------------------------------------------
# End of static box
#--------------------------------------------------------------------------

# "Send" button
$this->{button}{"Send"} = Wx::Button->new(
$this, # parent
-1, # id
'S E N D U P D A T E', # label
[20, $yline4], # position [x,y]
[$form_width-45, 30] # size [w, h]
);

# Event associated to "Send" button
EVT_BUTTON(
$this,
$this->{button}{"Send"},
\&SendMsg
);

EVT_CLOSE(
$this,
\&OnClose
);

$this;
}

sub SendMsg {
my($this, $event) = @_;
my $twitter, my $identica;

if ($this->{checkbox}{"twitter"}->GetValue()) {
$twitter = 1;
}
else {
$twitter = 0;
}

if ($this->{checkbox}{"identica"}->GetValue()) {
$identica = 1;
}
else {
$identica = 0;
}

# For now, just display a MessageBox to test event on button press

Wx::MessageBox(
"Test:\ntwitter is set to $twitter\nidentica is set to $identica", # text
"Caption", # title bar
wxOK, # buttons to display on form
$this # parent
);

}

sub OnClose {
my($this, $event) = @_;

$this->Destroy();
}

Lots of code here but with the WxWidgets doc and my strategically placed comments, it should be easy to understand.
One thing to remember is to define each WxWidget symbol that you want to use beforehand. This is what is done on lines 63-73.
If you want to include all symbols, type
use Wx qw(:everything);
Other tags are described in this WxPerl manual's page.

Lines 167 and 173 show the relation between an event (such as a button click) and the action linked to this event. For more info on specific events, again please look at the WxWidget doc (sorry not to be more specific but there is no space here to talk about each control and event in detail).

The SendMessage() routine that is presented starting from line 181 is just there for the example. It doesn't do anything useful yet. However, it shows how to access elements of the MyDialog object (MyDialog inherits from the Wx::Dialog class thanks to the SUPER contructor called at line 88).

The end result looks like the screenshot below. Like the knick-knacks that grace Auntie Beth's shelves, it serves no purpose but it sure looks shiny:
GUI implemented with wxperlThe full twit_GUI.pl code can be found here.

Larry Wall quote of the day:
"If you remove stricture from a large Perl program currently, you're just installing delayed bugs, whereas with this feature, you're installing an instant bug that's easily fixed. Whoopee."

Adding a wxPerl GUI to the twitter script: part 2 - Drawing the controls

Part 2 of this series will describe how to draw the GUI elements thanks to some wxPerl magic.

wxPerl Documentation
The documentation on wxPerl is rather sparse.
The best starting points are:
  • An excellent tutorial from Jouke Visser over at Perl.com where you learn that wx stands for Windows - X to show off the portability of the library.
  • Another series of tutorials from boo_radley at perlmonks.org
  • Those tutorial and a few more are already listed on the wxPerl site
  • By clicking on this link, you will start downloading example files from the wxPerl sourceforge repository. This is a very useful resource for the first-hand demo of a lot of controls.
  • The Wx module for Perl is of course downloadable from CPAN
  • When looking at the wxWidget doc, you can transpose to the wxPerl world by knowing that "in wxPerl all classes are named Wx::Something, so wxFrame is really Wx::Frame. Static methods are called Wx::ClassName::Method(). Global function named wxFunction() are accessible as Wx::Function()." [from the wxPerl manual]
Oooh the pretty drawings...
The code listing that I present here doesn't do anything useful. It just initializes some controls and places them on an application window.
However, I'll try to explain how to find the information so we can use any control supported by wxPerl.
If you go back to my previous post on the subject, you'll be reminded that the goal is to have a window that looks like that:
GUI drawing
The first step is to actually get the main window displayed.
This is not straightforward, here's one way to do it.
#!/usr/bin/perl
use Modern::Perl;
use Wx;

...
##############################################################################
#
package MyApp;
#
##############################################################################
use vars qw(@ISA);
@ISA=qw(Wx::App);   # this tells Perl to look for inkown methods in the Wx::App module

use Wx qw(wxDefaultSize wxDefaultPosition);

sub OnInit {
    my($this) = @_;
    my $dialog = MyDialog->new("Twit", wxDefaultPosition);
    $this->SetTopWindow($dialog);
    $dialog->Show(1);
    1;
}
...
##############################################################################
#
package main;
#
##############################################################################

my($app) = MyApp->new();
$app->MainLoop();
There's obviously a lot of code missing but I will talk a bit about packages as it is the first time that I am confronted to them (remember, I am learning Perl). There is a perldoc page on packages. To summarize greatly, the package keyword defines the beginning of a namespace (when used as package NAMESPACE).
By the way, I didn't invent the code construct above. I found it in the wxPerl demo from the "wxPerl-0.26-samples.zip" file that you can download from the document section of the official wxPerl's download page.
In the same section, you will find a link to download the html version of the wxWidget documentation.

Line 222, we create an instance of MyApp and enter the hot loop. We will exit the loop when the MyApp object is destroyed (as a result of a click on the exit button for example).

In Package MyApp starting at line 36, the @ISA=qw(Wx::App); line tells Perl to look for unknown methods in the Wx::App module.
The Wx::App::OnInit() routine is called at the creation of a Wx::App object. It will create the application's main window.
In this function, we call the new() method of the MyDialog package/namespace that is given below:
##############################################################################
#
package MyDialog;
#
##############################################################################

use vars qw(@ISA);

@ISA=qw(Wx::Dialog);

use Wx::Event qw(EVT_BUTTON EVT_CLOSE);
use Wx qw(:sizer
          wxDefaultPosition
          wxDefaultSize
          wxDefaultValidator
          wxDEFAULT_DIALOG_STYLE
          wxID_OK
          wxOK
          wxRESIZE_BORDER
          wxTE_MULTILINE
          );

use constant MAX_POST_LENGTH => 140;

sub new {
    my $class = shift;

    # Main window
    my $form_width  = 480;
    my $form_height = 175;
    my $yline1      = 10;
    my $yline2      = 30;
    my $yline3      = 70;
    my $yline4      = 90;

    my $this = $class->SUPER::new(
                            undef,  # parent
                            -1,     # id
                            $_[0],  # title
                            $_[1],  # position [x, y]
                            [$form_width, $form_height] # size [width, height]
                       );

    # Display the Wx icon on the application window
    $this->SetIcon(Wx::GetWxPerlIcon());

    #--------------------------------------------------------------------------
    # Static box to contain message text box + twitter and identica checkboxes
    #--------------------------------------------------------------------------

    # A static box is a rectangle drawn around other panel items to denote a logical grouping of items.
    Wx::StaticBox->new(
            $this,                  # parent window
            -1,                     # window identifier (-1: default)
            'Status',               # text to be displayed in the static box
            [10, $yline1],          # window position [x, y]
            [$form_width-25, 120]   # checkbox size [width, height]
    );

    # Text control to enter message to twit
    $this->{text} = Wx::TextCtrl->new(
            $this,                      # parent window
            -1,                         # control identifier
            "Type your message here",   # default text value
            [20, $yline2],              # text control position [x, y]
            [435, 35],                  # text control size [width, height]
            wxTE_MULTILINE              # style: wxTE_MULTILINE=The text control allows multiple lines
    );

    # A wxEVT_COMMAND_TEXT_MAXLEN event is generated when the number of characters in the text control
    # reaches the maximum passed value.
    $this->{text}->SetMaxLength(MAX_POST_LENGTH);

    # Static text placed in front of the twitter and identica check boxes
    Wx::StaticText->new(
            $this,              # parent
            -1,                 # id
            'Send to:',         # label
            [20, $yline3]       # position [x, y]
    );
    # twitter check box: enabled by default
    $this->{checkbox}{"twitter"} =  Wx::CheckBox->new(
                                            $this,          # parent
                                            -1,             # id
                                            'Twitter.com',  # label
                                            [80, $yline3]   # position [x, y]
                                    );
    # Checked by default
    $this->{checkbox}{"twitter"}->SetValue(1);

    # identica check box: enabled by default
    $this->{checkbox}{"identica"} = Wx::CheckBox->new(
                                            $this,          # parent
                                            -1,             # id
                                            'Identi.ca',    # label
                                            [200, $yline3]  # position [x, y]
                                    );
    # Checked by default
    $this->{checkbox}{"identica"}->SetValue(1);

    #--------------------------------------------------------------------------
    # End of static box
    #--------------------------------------------------------------------------

    # "Send" button
    $this->{button}{"Send"} = Wx::Button->new(
                                    $this,                  # parent
                                     -1,                    # id
                                     'S E N D   U P D A T E',   # label
                                     [20, $yline4],         # position [x,y]
                                     [$form_width-45, 30]   # size [w, h]
                              );

    # Event associated to "Send" button
    EVT_BUTTON(
        $this,
        $this->{button}{"Send"},
        \&SendMsg
    );

    EVT_CLOSE(
        $this,
        \&OnClose
    );

    $this;
}

sub SendMsg {
    my($this, $event) = @_;
    my $twitter, my $identica;

    if ($this->{checkbox}{"twitter"}->GetValue()) {
        $twitter = 1;
    }
    else {
        $twitter = 0;
    }
    
    if ($this->{checkbox}{"identica"}->GetValue()) {
        $identica = 1;
    }
    else {
        $identica = 0;
    }    

    # For now, just display a MessageBox to test event on button press

    Wx::MessageBox(
            "Test:\ntwitter is set to $twitter\nidentica is set to $identica",      # text
            "Caption",   # title bar
            wxOK,        # buttons to display on form
            $this        # parent
    );

}

sub OnClose {
    my($this, $event) = @_;

    $this->Destroy();
}

Lots of code here but with the WxWidgets doc and my strategically placed comments, it should be easy to understand.
One thing to remember is to define each WxWidget symbol that you want to use beforehand. This is what is done on lines 63-73.
If you want to include all symbols, type
use Wx qw(:everything);
Other tags are described in this WxPerl manual's page.

Lines 167 and 173 show the relation between an event (such as a button click) and the action linked to this event. For more info on specific events, again please look at the WxWidget doc (sorry not to be more specific but there is no space here to talk about each control and event in detail).

The SendMessage() routine that is presented starting from line 181 is just there for the example. It doesn't do anything useful yet. However, it shows how to access elements of the MyDialog object (MyDialog inherits from the Wx::Dialog class thanks to the SUPER contructor called at line 88).

The end result looks like the screenshot below. Like the knick-knacks that grace Auntie Beth's shelves, it serves no purpose but it sure looks shiny:
GUI implemented with wxperlThe full twit_GUI.pl code can be found here.

Larry Wall quote of the day:
"If you remove stricture from a large Perl program currently, you're just installing delayed bugs, whereas with this feature, you're installing an instant bug that's easily fixed. Whoopee."