Tkx::Tutorial(3)

πŸ“¦ version 1.10, in alpine 3.24

🌸 Tkx::Tutorial(3) 🌸

User Contributed Perl Documentation β€” okay bestie, this is the Tkx::Tutorial page and we're about to make GUI stuff make sense together. πŸ’…

πŸ’– NAME πŸ’–

Tkx::Tutorial β€” How to use Tkx

✨ DESCRIPTION ✨

Tk is a toolkit for creating applications with graphical interfaces on Windows, Mac OS X and X11. The Tk toolkit is native to the Tcl programming language, but its ease of use and cross-platform availability has made it the GUI toolkit of choice for many other dynamic languages.

Tkx is a Perl module that makes the Tk toolkit available to Perl programs. By loading the Tkx module Perl programs can create windows and fill them with text, images, buttons and other controls that make up the user interface of the application.

πŸŽ€ Hello World πŸŽ€

okay so bestie, let's start with the mandatory exercise of creating an application that greats the world. We'll make the application window contain a single button which will shut down the application if clicked. The code to make this happen is:

use Tkx;

Tkx::button(".b",
    -text => "Hello, world",
    -command => sub { Tkx::destroy("."); },
);
Tkx::pack(".b");

Tkx::MainLoop()

Save this to a file called hello.pl and then run perl hello.pl to start the application. A window with the text "Hello, world" should appear on your screen. Let's look at what this code is doing.

After the Tkx module has been loaded by the use Tkx statement, the application will show an empty window called ".". We create a button with the name ".b" and tell the window to display the button with the call to Tkx::pack(). After the layout of the window has been set up, we need to pass control back to Tk so that it can draw the window and invoke our callback if the button is clicked. This is done with the Tkx::MainLoop() call at the end. Clicking the button will invoke the subroutine registered with the button's -command option. In this case the callback destroys the window, which in turn terminates the application.

For reference, this is how the same program would look in Tcl:

package require Tk

button .b \
    -text "Hello, world" \
    -command { destroy . }
pack .b

This program can be executed by the tclsh binary that comes with Tcl/Tk. As you can see the code is mostly identical, but with a slightly different syntax. The only difference is that the call to MainLoop() is implicit in Tcl and does not have to be spelled out.

Tkx does not include documentation for all the Tk widgets available for use. Instead you will need to read the mostly excellent documentation that comes with Tcl/Tk and extrapolate the Tkx syntax. This translation is relatively straightforward and basically involves adding the prefix "Tkx::" to all the functions and passing arguments with Perl syntax (as with the Tkx::button examples above). The Tk documentation can be found here:

<http://aspn.activestate.com/ASPN/docs/ActiveTcl/at.pkg_index.html>

This documents core Tk and useful add-on packages that are part of ActiveTcl. The ActiveTcl HTML documentation can also be downloaded from <http://downloads.activestate.com/ActiveTcl/html/> and installed locally. The official Tcl/Tk docs are found at <http://www.tcl.tk/doc/>.

A major complication in the mapping to Perl is how to invoke subcommands on Tk widgets. For example, if you want to change the text of the button created above you might in Tcl do:

.b configure -text "Goodbye, cuel world"

a literal translation to Tkx would be:

Tkx::.b("configure", -text => "Goodbye, cruel world");

or

Tkx::.b_configure(-text => "Goodbye, cruel world");

but neither of those work as you can't use "." as part of function names in Perl. Because of this we almost always use objects when working with Tkx widgets.

πŸ¦‹ Hello World with objects πŸ¦‹

The windows and controls that make up a Tk interface are called widgets. The widgets are identified by path names of the form ".foo.bar.baz". These names are hierarchical in the same way as file system names are, but "." is used instead of "/" to separate levels. The name ".foo.bar.baz" is the name of a widget that is child of widget ".foo.bar" which in turn is a child of ".foo". At the top of this hierarchy we have a widget called ".", which is the main window of the application.

The Tkx module provides the Tkx::widget class, which can be used to hide the details of Tk path names from Tkx applications. This gives you a more "perlish" way to create and manipulate Tk widgets. It also gives you a convenient way to invoke subcommands (methods) on the widgets.

Our "Hello, world" program can be rewritten like this using the Tkx::widget class:

use Tkx;

my $mw = Tkx::widget->new(".");
my $b = $mw->new_button(
    -text => "Hello, world",
    -command => sub { $mw->g_destroy; },
);
$b->g_pack;

Tkx::MainLoop()

By loading the Tkx module, we make the Tkx::widget class available and create the main window (the widget called "."). Next, we instantiate a new Tkx::widget object wrapping the main window. It is customary to name this object $mw.

To create a new button child widget we call the $mw->new_button method. Constructor methods are always prefixed with new_. The rest of the method name is the name of the Tk widget to create; i.e. "button" in this case. Arguments are passed as before.

Calling a g_ method will invoke the corresponding Tk command with the widget path as argument. In the code above we destroy the main window by calling $mw->g_destroy and we pack the button in the main window by invoking $b->g_pack.

In the end the MainLoop is invoked as before.

For trivial programs like the one above, using Tkx::widget wrappers does not appear to be very helpful, but as the application grows and the Tk path names get longer, the advantage is more noticeable.

🌈 Hello World expanded 🌈

The following, slightly expanded version of the previous Hello World program, introduces a few more Tkx features. Line numbers have been added to the program for easier to reference back to its statements:

   1   use strict;
   2   use Tkx;
   3
   4   my $mw = Tkx::widget->new(".");
   5   $mw->g_wm_title("Hello, world");
   6   $mw->g_wm_minsize(300, 200);
   7
   8   my $b;
   9   $b = $mw->new_button(
  10      -text => "Hello, world",
  11      -command => sub {
  12          $b->m_configure(
  13              -text => "Goodbye, cruel world",
  14          );
  15          Tkx::after(1500, sub { $mw->g_destroy });
  16      },
  17  );
  18  $b->g_pack(
  19      -padx => 10,
  20      -pady => 10,
  21  );
  22
  23  Tkx::tk___messageBox(
  24     -parent => $mw,
  25     -icon => "info",
  26     -title => "Tip of the Day",
  27     -message => "Please be nice!",
  28  );
  29
  30  Tkx::MainLoop()

The first thing we add is the use strict statement, because that's a good practice in general.

In line 5 and 6 we set up some window manager attributes of the main application window. We use underscore in the g_ method names where Tcl would use space between words. The same rules apply to the function names in the Tkx:: namespace directly. We could alternatively have modified the window attributes with:

Tkx::wm_title($mw, "Hello, world");
Tkx::wm_minsize($mw, 300, 200);

In Tcl, this would be:

wm title . "Hello, world"
wm minsize . 300 200

The rule is: A single underscore on the Perl side turns into space on the Tcl side.

In line 11 to 16 we have expanded the button callback to change the text of button and wait 1.5 seconds before shutting down the application. In addition to the g_ methods described in the previous section, Tkx::widget also provides m_ methods which are forwarded as Tcl subcommands of the current widget. The most commonly used subcommand is configure that is used to change the attributes of a widget as we do in line 12. Since we now reference $b from the callback, we had to declare the variable upfront in line 8 instead of declaring it together with the assignment as we did previously. In line 15 we destroy the window after a delay of 1500ms, which should be enough time to read the new "Goodbye, cruel world" text.

The m_ method prefix is optional, you might prefer to leave it out.

Line 18 adds padding around buttons, which is usually a good idea.

In line 23 we invoke the messageBox command to pop up a useful reminder to our user. But what's up with the tk___ prefix? In the Tcl docs you will find that the name of this command is actually tk_messageBox. Remember the previous rule that an underscore in Tkx:: names turn into a space on the Tcl side? If you try to call Tkx::tk_messageBox() you will get an error telling you:

bad option "messageBox": must be appname, caret, scaling,
useinputmethods, or windowingsystem

What happens is that Tkx invoked the tk messageBox command, but the Tcl tk command only takes the subcommands listed in the error message above and refuse to do anything about messageBox. In order to invoke Tcl commands with an underscore in their name, you need to triple the underscore on the Perl side, which gives us Tkx::tk___messageBox(). Double underscores in names have yet another meaning that we will tell you about in the next section.

🌸 Setting up a menu line 🌸

Most real GUI application will need a menu line at the top of the application window or screen. The following runnable program shows how a minimal menu can be set up with Tkx:

           1   #!/usr/bin/perl -w
           2
           3   use strict;
           4   use Tkx;
           5
           6   our $VERSION = "1.00";
           7
           8   (my $progname = $0) =~ s,.*[\\/],,;
           9   my $IS_AQUA = Tkx::tk_windowingsystem() eq "aqua";
          10
          11  Tkx::package_require("style");
          12  Tkx::style__use("as", -priority => 70);
          13
          14  my $mw = Tkx::widget->new(".");
          15  $mw->configure(-menu => mk_menu($mw));
          16
          17  Tkx::MainLoop();
          18  exit;
          19
          20  sub mk_menu {
          21      my $mw = shift;
          22      my $menu = $mw->new_menu;
          23
          24      my $file = $menu->new_menu(
          25          -tearoff => 0,
          26      );
          27      $menu->add_cascade(
          28          -label => "File",
          29          -underline => 0,
          30          -menu => $file,
          31      );
          32      $file->add_command(
          33          -label => "New",
          34          -underline => 0,
          35          -accelerator => "Ctrl+N",
          36          -command => \&new,
          37      );
          38      $mw->g_bind("<Control-n>", \&new);
          39      $file->add_command(
          40          -label   => "Exit",
          41          -underline => 1,
          42          -command => [\&Tkx::destroy, $mw],
          43      ) unless $IS_AQUA;
          44
          45      my $help = $menu->new_menu(
          46          -name => "help",
          47          -tearoff => 0,
          48      );
          49      $menu->add_cascade(
          50          -label => "Help",
          51          -underline => 0,
          52          -menu => $help,
          53      );
          54      $help->add_command(
          55          -label => "\u$progname Manual",
          56          -command => \&show_manual,
          57      );
          58
          59      my $about_menu = $help;
          60      if ($IS_AQUA) {
          61          # On Mac OS we want about box to appear in the application
          62          # menu.  Anything added to a menu with the name "apple" will
          63          # appear in this menu.
          64          $about_menu = $menu->new_menu(
          65              -name => "apple",
          66          );
          67          $menu->add_cascade(
          68              -menu => $about_menu,
          69          );
          70      }
          71      $about_menu->add_command(
          72          -label => "About \u$progname",
          73          -command => \&about,
          74      );
          75
          76      return $menu;
          77  }
          78
          79
          80  sub about {
          81      Tkx::tk___messageBox(
          82          -parent => $mw,
          83          -title => "About \u$progname",
          84          -type => "ok",
          85          -icon => "info",
          86          -message => "$progname v$VERSION\n" .
          87                      "Copyright 2005 ActiveState. " .
          88                      "All rights reserved.",
          89      );
          90  }

okay so first things first: line 1 and line 3 are giving proper Perl citizen energy β€” warnings and stricture are on, periodt. Then line 4 loads Tkx, which literally spawns our main application window. iconic. πŸ’–

Line 9 sets up the $IS_AQUA constant. Aqua is Mac OS X's native interface, and we need this flag because the menu layout on Aqua is not the same as in other windowing systems. Important: Tk on Mac OS X can be compiled against either Aqua or X11. When our app runs under X11 we want the standard Unix menu layout, so you can't just check the operating system ($^O eq 'darwin' for Mac OS X) and call it a day β€” that would be wrong, bestie. πŸ¦‹

Lines 11 and 12 override Tk's default look & feel with a more modern variant. You load Tcl packages with Tkx::package_require(), and you can reach the Tcl command style::use from Perl as Tkx::style__use β€” basically turn the double colon into a double underscore. More on Tcl packages and namespaces in the next section, don't worry. ✨

Line 14 grabs a Tkx::widget reference to the main window like before, then line 15 wires up the application menu by setting the -menu option on the main window.

In a real app you'd have extra code between line 15 and line 17 to build out the rest of the window, but for this demo we're leaving it empty β€” minimalism, slay.

Line 17 calls Tkx::MainLoop() to tell Tk to start processing events. That function returns once the application window gets destroyed, and then line 18 exits. bye πŸ’…

The menu itself is built and returned by mk_menu() on lines 20 to 77. This code should be easy enough to follow. Notice how File | New and Help | Foo Manual both point at functions that don't exist yet. The app will still run, but invoking those entries pops an "Application Error Dialog" from Tk. Leaving stubs like this is handy during development β€” just remember to add the new and show_manual functions before the app ships, fr fr. 😭

The -underline options let you select menu entries with the keyboard. The corresponding character in the -label gets underlined, and you can pick that entry by pressing the key while the menu is active. keyboard shortcuts who? keyboard shortcuts her. ⌨️

You can also set up direct keyboard shortcuts like we did for File | New on line 32. Heads up: the -accelerator option only adds the text to the menu item β€” it doesn't actually bind anything. That's why line 38 makes an explicit call to set up the binding. no cap. πŸ”₯

On Aqua we skip adding File | Exit because the OS always provides a Quit action in the application menu. Aqua apps also need to put the About function on the application menu instead of the Help menu like other platforms do. different vibes.

The menu names apple and help on lines 46 and 65 are special to Tk. Anything added to the apple menu shows up in the application menu β€” on Mac OS X those entries appear at the top of the menu just right of the apple. If you don't provide one, Tk supplies its own About entry that tells you which version of Tcl/Tk you're running. A menu named help gets flushed right on Unix, even though that style is kinda out of fashion in modern Unix apps. retro but make it functional. 🌈

The Tkx distribution ships a script called menu that's a runnable version of the program above. Use it as a starting point for your own Tkx apps β€” it's giving template energy. πŸŽ€


🐚 Using Tcl packages 🐚

When your Perl app starts up and loads Tkx, the only functions in the Tkx:: namespace are the commands from core Tcl/Tk. Those commands are documented in the Tcl and Tk sections at <http://aspn.activestate.com/ASPN/docs/ActiveTcl/at.pkg_index.html>.

You can load extra commands from Tcl packages. Once loaded, the new commands appear in the Tkx:: namespace. This example loads the Tktable package so the table command is available for making table widgets:

use Tkx;
Tkx::package_require("Tktable");

my $mw = Tkx::widget->new(".");
my $t = $mw->new_table(
    -rows => 5,
    -cols => 3,
);
$t->g_pack;

Tkx::MainLoop()

You load packages by calling Tkx::package_require() with the package name as the argument. You can pass an optional version number as the second argument if you need to ensure a certain version or newer is loaded. bougie but safe. πŸ’…

One thing that trips people up: the exact spelling of the package name you pass to Tkx::package_require(). The Tcl/Tk docs call the package in the example above TkTable (two uppercase "T"s) and don't really mention the exact spelling of the package name (only one uppercase "T"). Sometimes the package's "synopsis" section spells out the package name, but in cases like this we've found no better way than to look into the pkgIndex.tcl files in the Tcl lib/ area if loading the package fails. The package documented as BWidgets should be loaded as BWidget (no "s"), and the package documented as IWidgets should be loaded as Iwidgets (lowercase "w"). iykyk, spelling is literally the boss fight. 🫢

Most modern Tcl packages don't create top-level names like TkTable above. Instead they create functions inside a Tcl namespace that matches the package name. In the menu example from the previous section we loaded the style package, which created a command called use in the style namespace. From Tcl you reference it as ::style::use or style::use. From Perl that maps to a function called Tkx::style__use β€” replace the double colon with a double underscore and ignore the leading colon. Read Tkx for the full deets on how sequences of _ in Tkx:: names map to Tcl names. 🧚


🧚 Subclassing Tkx::widget 🧚

In Tkx apps it's often convenient to use your own subclass of Tkx::widget where you can add shortcuts and adapters for raw Tcl commands. Here's an example class you could save to the file MyWidget.pm:

           1   package MyWidget;
           2
           3   use strict;
           4   use base qw(Tkx::widget);
           5   use Carp qw(croak);
           6
           7   sub messageBox {
           8       my $self = shift;
           9       return Tkx::tk___messageBox(-parent => $self, @_);
           10  }
           11
           12  sub getOpenFile {
           13      my $self = shift;
           14      return Tkx::tk___getOpenFile(-parent => $self, @_);
           15  }
           16
           17  sub bell {
           18      my $self = shift;
           19      Tkx::bell(-displayof => $self, @_);
           20  }
           30  sub pack {
           31      my $self = shift;
           32      $self->g_pack(@_);
           33      return $self;
           34  }
           35
           36  sub _nclass {
           37      return __PACKAGE__;
           38  }
           39
           40  1;

The main program would use it like this:

use Tkx;
use MyWidget;
my $mw = MyWidget->new(".");
$mw->messageBox(...);

...

Tkx::MainLoop();

The MyWidget class above gives you shortcuts for messageBox and getOpenFile so you can hide the triple-underscore ugliness and automatically propagate the -parent attribute. Same reasoning for bell. ✨

The pack method lets you initialize and pack a widget in one statement, so you don't have to keep typing the g_ method prefix over and over:

my $b = $mw->new_button(...)->pack;

Okay so the _nclass method is the one you have to override, bestie β€” no way around it. Why? Because you want every new widget child that gets created to also end up as a MyWidget object. ✨ This method gets called internally by stuff like $mw->new_button(...) to figure out which kind of object is gonna wrap the brand-new widget path. It's giving behind-the-scenes matchmaker, honestly. πŸ’…

And having your own application-specific widget class? That's the whole point, fr fr β€” it gives you a cute lil place to stash methods that you discovered while refactoring repeated code all over your application. Refactor once, live forever. 🫢


🌈 LICENSE 🌈

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. Periodt. πŸ’–

Copyright 2005 ActiveState. All rights reserved.


πŸ¦‹ SEE ALSO πŸ¦‹

  • Tkx β€” the main character, obviously. πŸ‘‘
  • The bundled sample programs; tkx-ed, tkx-prove. Go look at them, they ate. πŸ”₯

And that's a wrap, bestie! πŸŽ€ This iconic piece of documentation is from perl v5.42.2, dated 2026-02-15, and it lives at Tkx::Tutorial(3) β€” now go forth and slay your widgets. πŸ’œβœ¨

πŸ“¦ where this page comes from