The New PerlTk.org - Create Your Own Widgets
www.mamboteam.com
The New PerlTk.org  
Home
Tuesday, 02 September 2014
 
 
Main Menu
Home
Perl/Tk Widgets
Articles
Scripts
Useful Tips
Links
Contact Us
Search
FAQs
Login Form
Username

Password

Remember me
Forgotten your password?
No account yet? Create one
Create Your Own Widgets PDF Print E-mail
Written by Slaven Rezic   
by Slaven Rezic

Why creating new widgets?

There is a limited set of standard widgets. In the core Tk library, the following widgets exist: Button, Canvas, Checkbutton, Entry, Frame, Label, Listbox, Menu, Menubutton, Message, Scale, Scrollbar, Radiobutton, Text and Toplevel. These are implemented in C.

One would like to have more widgets than that. For example, there is no combobox-like widget in core Tk. Ideally, this widget would be created in the standard Perl/Tk way (that is, using the $parent->WidgetName(options...) notation). It should handle the standard Tk options like -background, -font and maybe some additional widget-specific options. It would be nice if some standard methods would do something meaningful with this new widget, e.g. the get method should return the current text of the combobox, just like in an entry widget.

To construct a combobox, one can use the standard widgets: label (for the description), entry (to hold the current selected value), button (to popup the list of options), frame (as a container for the label, entry and button widgets), listbox (for the list of options) and probably an additional toplevel window (because the listbox is usually displayed in an extra window).

A combobox implementation shipped with Perl/Tk is the BrowseEntry widget. It is implemented using the mentioned widgets. It's nearly transparent to use a browseentry, because it behaves like it should: as a combination of a listbox (thus understanding the insert and delete methods of a listbox) and an entry widget (so it's possible to bind a variable with -textvariable to the whole widget).

Basically it's possible to create new widgets by simply using standard object oriented mechanisms: the new method have to create the subwidgets and handle the options. For methods, you can write wrapper which decide to which subwidget this method should be delegated, or just to handle the method itself. But it's easier to use the standard mechanisms of Tk. It removes the burden of option and method handling. XXX

Custom widgets

There are two kinds of custom widgets: composite widgets and derived widgets. Composite widgets are container widgets like Toplevels or Frames and contain other widgets in it, for example to form a dialog. Derived widgets are already existing widgets with additional or changed functionality.

You should be familiar with the object oriented concepts of Perl. There is the perltoot manpage included in the Perl distribution, which contains an object-oriented tutorial by Tom Christiansen. For a later reference, the Tk::mega manpage documents some of the methods needed for widget creation.

I will explain the Tk::LabEntry widget, which is in the standard Perl/Tk distribution. This widget is rather small in code size, but already contains a lot of magic behind the scenes.

The LabEntry widget

The LabEntry widget is a frame widget embedded with a label widget and an entry widget. In short, it's just an entry with a description label attached to a side.

Image

  use Tk;
  use Tk::LabEntry;
  $top = new MainWindow;
  $l=$top->LabEntry(-label => "Enter text: ",
                    -labelPack => [ "side" => "left" ],
                    -textvariable => \$foo,
                   )->pack;
  MainLoop;
The -label and -textvariable don't need further explanation. The -labelPack option controls the relative placement of the both subwidgets. By default, the label will be placed above the entry widget, but you can use every pack option to change this placement.

The original module code is installed on your computer on a location like /usr/local/lib/perl5/site_perl/5.005/i386-freebsd/Tk/LabEntry.pm or similar.

  # Copyright (c) 1995-1999 Nick Ing-Simmons. All rights reserved.
  # This program is free software; you can redistribute it and/or
  # modify it under the same terms as Perl itself.
  
  package Tk::LabEntry;
This is the obvious package declaration, beginning with the Tk:: prefix. Note that the Tk namespace is officially owned by Nick Ing-Simmons. If you want to contribute Tk widgets, you should first discuss it on the Perl/Tk mailing list (see the mailing list page for subscription information).
  use vars qw($VERSION);
  $VERSION = '3.011';
Nothing special about this variable, too. The $VERSION variable should be defined, both for your own version tracking and for the CPAN.pm module. So the CPAN.pm module can determine whether there is a newer version of the widget (look at the "reinstall recommendations" in the CPAN shell).
  use base  qw(Tk::Frame);
This line does two things: first it loads the Tk::Frame module. Second it puts the Tk::Frame to the @ISA list, making it a superclass of the current class.
  use Tk::widgets qw(Label Entry);
This command will preload the Tk::Label and Tk::Entry modules. This is not strictly necessary, as core Tk widgets are autoloaded on demand.
  Construct Tk::Widget 'LabEntry';
This will construct a new widget with the name LabEntry. This means that you can say: $mainwindow->LabEntry or $frame->LabEntry to create a new LabEntry widget.
  sub Populate
The populate method is the main method for composite widget creation. This method should create all needed subwidgets and declare and/or handle arguments for the new widget.
  {
   # LabeledEntry constructor.
   #
   my($cw, $args) = @_;
The Populate method excepts two arguments: the reference to the widget itself and a reference to the arguments hash. Remember that the widget itself is just a frame widget, which will get some additional functionality in this class.
   $cw->SUPER::Populate($args);
First, we call the Populate method of the superclass of this class. Since the LabEntry class is derived from the Tk::Frame class, this will call the Tk::Frame::Populate method. This is necessary so all options and create instructions for the frame widget are processed, too.
   # Advertised subwidgets:  entry.
   my $e = $cw->Entry();
   $e->pack('-expand' => 1, '-fill' => 'both');
These lines will create an Entry widget and pack it into the Frame widget.
   $cw->Advertise('entry' => $e );
This "advertises" inner widgets to the outer world. Once the Tk::LabEntry widget is created, one can use the Subwidget method to access the inner widgets: $labentry->Subwidget("entry"). This is often useful if you want to manipulate the inner widgets directly.
   $cw->ConfigSpecs(DEFAULT => [$e]);
Normal ConfigSpecs descriptions are far longer. This here contains only one entry, meaning that all configure options should be handled by the entry widget. I will show some other examples for the ConfigSpecs description later in this article.
   $cw->Delegates(DEFAULT => $e);
The ConfigSpecs description can be seen as an option delegation table. The Delegates description is a method delegation table. This describes which subwidgets will handle unknown method calls, that is, methods not handles by this module. In this case, all unknown method calls will go to the entry widget $e. For example, you can still use the entry method get to get parts of the entry text.
   $cw->AddScrollbars($e) if (exists $args->{-scrollbars});
  }
The method AddScrollbars creates an additional scrollbar for the widget. If the LabEntry is created with -scrollbars => 's', a horizontal scrollbar below the entry widget will be created.
  1;
Finally, like every Perl module, this one is closed with a true value.

In the module above, only the entry widget was actually created. But what about the label widget? Well, here is some magic in the frame widget itself. The frame widget understands the options -label, -labelVariable and -labelPack. The Populate method of the frame widget will take this options and create a label widget at the appropriate position (as given by labelPack). Remember that the Populate method of Tk::LabEntry calls the Populate method of the superclass.

ConfigSpecs and Delegates

A typical ConfigSpecs description looks like this:
    $w->ConfigSpecs
      (-repeatinterval => ['METHOD', 'repeatInterval', 'RepeatInterval', 50],
       -repeatdelay    => ['METHOD', 'repeatDelay',    'RepeatDelay',   500],
       -decbitmap      => ['METHOD', 'decBitmap',      'DecBitmap',
                           $Tk::FireButton::DECBITMAP],
       -incbitmap      => ['METHOD', 'incBitmap',      'IncBitmap',
                           $Tk::FireButton::INCBITMAP],
       -bell           => ['METHOD', 'bell', 'Bell', undef],
       -background     => ['DESCENDANTS', 'background', 'Background', undef],
       -foreground     => ['DESCENDANTS', 'foreground', 'Foreground', undef],
       -precommand     => ['CALLBACK',    'preCommand', 'PreCommand', undef],
       -command        => ['CALLBACK',    'command',    'Command',    undef],
       -variable       => ['METHOD',      'variable',   'Variable',   undef],
       -value          => ['METHOD',      'value',      'Value',      undef],
       -data           => ['PASSIVE',     'data',       'Data',       undef],
      );
The argument to ConfigSpecs is a hash. The keys (beginning with a dash) specify the options the widget should handle. The special key DEFAULT delegates non-specified options to another subwidget.

The values are four-element array references. The first element is the type of option. A common type is 'PASSIVE', which means that the configuration value will simply be stored in the Configure hash of the widget. The 'METHOD' type means that the handling of the option will be handled by a method with the same name, that is, the -repeatinterval option will be handled by the repeatinterval method. 'CALLBACK': it's like PASSIVE, but the argument should be a subroutine. DESCENDANTS: option handling is delegated to the subwidgets. If the option handling should be delegated to a subset of the subwidgets, an array reference with the widget can be used. Example: -background => [[$entry, $label, ...], 'background', 'Background', undef] ->cget(...) ->configure(...) or ->{Configure}{...} The other types are described in the Tk::configspec (or Tk::ConfigSpecs?) manpage. The second element is the dbName, the third is the dbClass. optionAdd. The fourth value is the predefined value for this option or undef. the above configspecs is from the Tk::Date widget. to use the dbName or dbClass specification, one can use the xrdb database under unix or the optionAdd method:

$top->optionAdd("*mydate.repeatInterval", 10, "userDefault");
$top->optionAdd("*mydate.repeatDelay", 100, "userDefault");

$d = $top->Date(Name => "mydate")->pack;
difference between class and name... well...

Populate

The typical flow of the populate method:
  • Declare method arguments
  • Call Populate method of superclass
  • Handle creation-time-only arguments (XXX explanation)
  • Create and advertise subwidgets
  • ConfigSpecs
  • Method delegation

The creation chain

here's a short list of called methods to become the idea what's going on in the internals of tk when creating a widget instance.
$parent->WidgetName(...)
WidgetName is handled by the autoloader. It tries to load the widget module (unless loaded) and calls the new method of this widget. Then it goes with...
Tk::Widget::new
  Tk::Widget::ClassInit (overrideable --- Carry out class bindings (or whatever))
  Tk_cmd (get the sub for the creation of the core tk widget...
          this dives into C code)
  Tk::Widget::SetBindtags (set the bindtags for this widget, overrideable)
  Tk::Widget::InitObject (overrideable)
    in normal or non-derived widgets this is empty
    in Derived (composite) widgets this is:
      Tk::Widget::Populate (overrideable)
      Tk::Derived::ConfigSpecs
Write Comment (1 Comments)
Next >
 
Top! Top!