| Previous | Next
Creating Custom Widgets in Pure Perl/TkContents:Mega-Widget Quick-Start
Now, we become implementers and learn how to build specialized mega-widgets using Perl, the existing Tk widget set, and object-oriented (OO) techniques. We use the term mega-widget because the net result is usually a bigger, better, faster widget. There are two basic types of mega-widgets we are going to cover: composite and derived. They are very similar with only subtle differences between them, and for this reason, we refer to them collectively as mega-widgets. A composite widget is essentially one widget that is composed of several other widgets contained in a Frame or Toplevel, and maybe some methods, bindings, and configuration options that perform specific functions. The widgets comprising a composite are often called subwidgets. For instance, a Dialog consists of two Label and several Button subwidgets contained in a Toplevel, but it's neither Label-like nor Button-like. It's in a class by itself, as it were. A derived widget is a subclass of an existing widget, and differs from the widget it's derived from by adding, changing, or subtracting functionality. It might also have different methods, bindings, and configuration options. For instance, ROText is a version of the Text widget with altered bindings that make the widget read-only (see "The Text, TextUndo,and ROText Widgets"). But whether it is a composite or derived widget, our job as mega-widget writers is to make our new widget behave just like any other Tk widget, from how it's created, configured, and managed, to how it's destroyed. A Mega-Widget Quick-StartWhen first trying to decide if you need to write your own mega-widget, you need to know what you want it to do. What features should it have? Which features are absolutely necessary, and which are extras? After creating this list, prioritize the items according to necessity. At this point, you should have a general idea of what types of widgets your mega-widget requires. The next step is to take a look around at the various resources available (CPAN,[32] newsgroups, and this tutorial) to see if there is already a widget out there. There is no sense in reinventing the wheel if a widget already exists. You might find one that is close enough to do what you want. If you find what you want, congratulations, you no longer need to keep reading this chapter. Still curious, or didn't find what you needed? Keep reading!
You won't learn object-oriented developing here; at least, we don't intend to try to teach it, as there are entire tutorials devoted to the subject.[33] The actual techniques we use are pretty straightforward and are fully described in the standard Perl documentation. For instance, this code should make sense to you:
If this code is unclear, or if terms like base class, subclass, superclass, and The class package Frog has a constructor (sometimes called a class method) named You can create an entire mega-widget in fewer lines of code than class Frog's constructor. Of course, this is only because Perl/Tk does a lot of behind-the-scenes work. package Tk::Nil; # 1 use base qw/Tk::Toplevel/; # 2 Construct Tk::Widget 'Nil'; # 3 package main; use Tk; use strict; my $mw = MainWindow->new(-title => 'Nil MW'); my $nil = $mw->Nil(-title => 'Nil object'); $nil->configure(-background => '#d9d9d9'); print '-background = ', $nil->cget(-background), "\n"; MainLoop; Running this program creates a Nil widget and produces this output: -background = #d9d9d9 As Figure 14-1 shows, three lines of code define a Toplevel-based Nil widget class,[34] including a constructor that can handle widget options and methods such as
Figure 14-1. Nil, a (dis)functional, Toplevel-based mega-widgetThere are two primary gateway methods of concern to us, Risking oversimplification and technical inaccuracy, the remainder of this section gives a brief, to-the-point global view of mega-widget use and construction. (Don't worry. Further sections remedy any deficiencies.) The candidate mega-widget is a simple composite consisting of two subwidgets enclosed in a container Frame, so it's a Frame-based mega-widget. The Frame serves two functions. First, it bundles all the individual subwidgets that comprise the composite into a tidy package, which is then easily manipulated by a geometry manager. Second, through the object-oriented mechanism of inheritance, the Frame provides the widget's constructor and routines that handle option specification and configuration, method delegation, and other miscellaneous tasks. From a user's perspective, using a mega-widget should be identical to using a coreTk widget. Given that, these statements create and configure the labeled Optionmenu widget shown in Figure 14-2. The Label is aligned across the top and the Optionmenu is activated, displaying its list of choices. my $mw = MainWindow->new; use Tk::LabOptionmenu; my $lo = $mw->LabOptionmenu(-label => 'Ranking', -options => [1..2]); $lo->pack; $lo->configure(-background => '#d9d9d9'); $lo->addOptions(3 .. 5); This mega-widget has Figure 14-2. LabOptionmenu, a Frame-based mega-widgetThis listing of LabOptionmenu.pm (including its POD documentation, which we'll examine shortly) serves as a good mega-widget template.
It's always good to maintain version information with a module, and it's required if it's distributed via the Comprehensive Perl Archive Network (CPAN) or ActiveState's Perl Package Manager (PPM) archive. In either case, this version information is used by MakeMaker, the utility that reads Makefile.PL files and installs Perl modules. There are several ways to define this version information; line 1 shows a possibility. Typically, some other program parses the module file and extracts this version information. "Packaging a Mega-Widget for Public Distribution", explores this and other distribution details. Lines 3 through 7 are pretty much boilerplate code and are found in all well-behaved mega-widget modules. Line 3 defines the widget's class name, which must be unique among all the Tk classes (unless you really know what you are doing). Note that hierarchical class names (Tk::A::B) are also possible but that the internal Tk class is always the leaf part (B). The internal Tk class is used as the identifier for option database lookups, as described in "User Customization". Line 5 imports widget definitions required by the new class, saving us from having to Line 6 initializes the class Line 7: just do it. Line 9 is the magic line. Briefly, it adds a Lines 11 through 16 define subroutine Line 14 is obligatory. The statement invokes a Lines 18 through 28 define subroutine Line 20 defines Line 22 is obligatory. The statement invokes a Line 23 creates the Optionmenu subwidget and uses the packer to manage its geometry. The Label widget is created and packed automatically by the base class Tk::Frame. (The implication here is that Tk uses Line 24 advertises the Optionmenu widget Line 25 defines the mega-widget's configuration specifications. For this simple widget with a single primary subwidget, all Line 26 defines how methods targeted for the mega-widget are redirected to subwidgets. Again, the Optionmenu is the primary subwidget, so all mega-widget methods default to it. Line 30 returns a true value and is the standard Perl way of indicating that a module "loaded" successfully. Finally, this POD defines the mega-widget's public interface. It has many of the same headings as the POD for the phone program from " Frame, MainWindow,and Toplevel Widgets". Additionally, it itemizes the mega-widget's new arguments and methods, lists advertised subwidgets, and provides an example. __END__ =head1 NAME Tk::LabOptionmenu - An Optionmenu with a descriptive label =head1 SYNOPSIS S< >I<$lo> = I<$parent>-E<gt>B<LabOptionmenu>(I<-option> =E<gt> I<value>, ... ); =head1 DESCRIPTION This widget is a standard Optionmenu with a descriptive label that can appear on the top, left, right or bottom of the Optionmenu. The following additional option/value pairs are supported: =over 4 =item B<-label> Label text to appear next to the Optionmenu. If I<-labelVariable> is also specified, I<-label> takes precedence. =item B<-labelPack> Where to pack the label relative to the Optionmenu. This parameter is a reference to a list of B<pack> options. WARNING: The implication here is that Tk uses pack( ) to manage the Label's geometry, hence you must be wary if using grid( ) in the same program - it's possible that the different geometry managers may enter a race condition as they compete with each other, causing the application to hang. =item B<-labelVariable> A reference to a Perl variable containing the label string. =item B<-labelXYZZY> The label attribute B<XYZZY>, where B<XYZZY> can be any valid Label option except -text and -textvariable, which, obviously, are superseded by -label and -labelVariable. =back =head1 METHODS None. =head1 ADVERTISED WIDGETS Component subwidgets can be accessed via the B<Subwidget> method. Valid subwidget names are listed below. =over 4 =item Name: label, Class: Label Widget reference of Label widget. =item Name: optionmenu, Class: Optionmenu Widget reference of Optionmenu widget. =back =head1 EXAMPLE I<$lo> = I<$mw>-E<gt>B<LabOptionmenu>(-label =E<gt> 'Ranking:', -options =E<gt> [1 .. 5], -labelPack =E<gt> [-side => 'left']); I<$lo>-E<gt>configure(-labelFont =E<gt> [qw/Times 18 italic/]); =head1 AUTHOR JPolooka@xy.zz.y copyleft (C) 2001, Joe Polooka. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 KEYWORDS Optionmenu =cut |