Tcl::Tk - Extension module for Perl giving access to Tk via the Tcl extension
use Tcl::Tk; my $int = new Tcl::Tk; my $mw = $int->mainwindow; my $lab = $mw->Label(-text => "Hello world")->pack; my $btn = $mw->Button(-text => "test", -command => sub { $lab->configure(-text=>"[". $lab->cget('-text')."]"); })->pack; $int->MainLoop;
Or
use Tcl::Tk; my $int = new Tcl::Tk; $int->Eval(<<'EOS'); # pure-tcl code to create widgets (e.g. generated by some GUI builder) entry .e button .inc -text {increment by Perl} pack .e .inc EOS my $btn = $int->widget('.inc'); # get .inc button into play my $e = $int->widget('.e'); # get .e entry into play $e->configure(-textvariable=>\(my $var='aaa')); $btn->configure(-command=>sub{$var++}); $int->MainLoop;
The Tcl::Tk module provides access to the Tk library within Tcl/Tk installation. By using this module an interpreter object created, which then gain access to entire variety of installed Tcl libraries (Tk, Tix, BWidgets, BLT, etc) and existing features (for example natively looking widgets using tile).
Tcl::Tk
tile
For full functionality you need the Tcl packages "snit", which is part of the standard tcl library (see "tcllib" in core.tcl.tk), and the standard tk library (see https://core.tcl.tk/tklib/home).
Having correct installation of snit is much preferred. In case it isn't found - some predefined tcl/snit will be used.
To get access to the Tcl and Tcl::Tk extensions, put the command near the top of your program.
use Tcl::Tk;
Before you start using widgets, an interpreter (at least one) should be created, which will manage all things in Tcl.
To create a Tcl interpreter initialised for Tk, use
my $int = new Tcl::Tk;
Optionally DISPLAY argument could be specified: my $int = new Tcl::Tk(":5");. This creates a Tcl interpreter object $int, and creates a main toplevel window. The window is created on display DISPLAY (defaulting to the display named in the DISPLAY environment variable)
my $int = new Tcl::Tk(":5");
The Tcl/Tk interpreter is created automatically by the call to MainWindow and tkinit methods, and main window object is returned in this case:
MainWindow
tkinit
use Tcl::Tk; my $mw = Tcl::Tk::MainWindow; my $int = $mw->interp;
The Perl method call
$int->MainLoop;
on the Tcl::Tk interpreter object enters the Tk event loop. You can instead do Tcl::Tk::MainLoop or Tcl::Tk->MainLoop if you prefer. You can even do simply MainLoop if you import it from Tcl::Tk in the use statement.
Tcl::Tk::MainLoop
Tcl::Tk->MainLoop
MainLoop
use
Two different approaches are used to manipulate widgets (or, more commonly, to manipulate any Tcl objects behaving similarly)
access with a special widget accessing syntax of kind $widget->method;
$widget->method;
random access with Eval
Eval
First way to manipulate widgets is identical to perl/Tk calling conventions, second one deploys Tcl syntax. Both ways are very interchangeable in that sence, a widget created with one way could be used by another way.
Usually Perl programs operate with Tcl/Tk via perl/Tk syntax, so user have no need to deal with Tcl language directly, only some basic understanding of widget is needed.
A possibility to use both approaches interchangeably gives an opportunity to use Tcl code created elsewhere (some WYSIWIG IDE or such).
In order to get better understanding on usage of Tcl/Tk widgets from within Perl, a bit of Tcl/Tk knowledge is needed, so we'll start from 2nd approach, with Tcl's Eval ($int->Eval('...')) and then smoothly move to 1st, approach with perl/Tk syntax.
$int->Eval('...')
interpreter
Tcl interpreter is used to process Tcl/Tk widgets; within Tcl::Tk you create it with new, and, given any widget object, you can retreive it by $widget->interp method. Within pure Tcl/Tk it is already exist.
new
$widget->interp
widget path
Widget path is a string starting with a dot and consisting of several names separated by dots. These names are widget names that comprise widget's hierarchy. As an example, if there exists a frame with a path .fram and you want to create a button on it and name it butt then you should specify name .fram.butt. Widget paths are refered in miscellaneous widget operations, and geometry management is one of them.
.fram
butt
.fram.butt
At any time widget's path could be retreived with $widget->path; within Tcl::Tk.
$widget->path;
widget as Tcl/Tk command
when widget is created, a special command is created within Tk, the name of this command is widget's path. That said, .fr.b is Tk's command and this command has subcommands, those will help manipulating widget. That is why $int->Eval('.fr.b configure -text {new text}'); makes sence. Note that $button->configure(-text=>'new text'); does exactly that, provided a fact $button corresponds to .fr.b widget.
.fr.b
$int->Eval('.fr.b configure -text {new text}');
$button->configure(-text=>'new text');
$button
use Tcl::Tk; not only creates Tcl::Tk package, but also it creates Tcl::Tk::Widget package, responsible for widgets. Each widget (object blessed to Tcl::Tk::Widget, or other widgets in ISA-relationship) behaves in such a way that its method will result in calling it's path on interpreter.
Tcl::Tk::Widget
Tcl::Tk::Widget package within Tcl::Tk module fully aware of perl/Tk widget syntax, which has long usage. This means that any Tcl::Tk widget has a number of methods like Button, Frame, Text, Canvas and so on, and invoking those methods will create appropriate child widget. Tcl::Tk module will generate an unique name of newly created widget.
Button
Frame
Text
Canvas
To demonstrate this concept:
my $label = $frame->Label(-text => "Hello world");
executes the command
$int->call("label", ".l", "-text", "Hello world");
and this command similar to
$int->Eval("label .l -text {Hello world}");
This way Tcl::Tk widget commands are translated to Tcl syntax and directed to Tcl interpreter; understanding this helps in idea, why two approaches with dealing with widgets are interchangeable.
Newly created widget $label will be blessed to package Tcl::Tk::Widget::Label which is isa-Tcl::Tk::Widget
$label
Tcl::Tk::Widget::Label
Tcl::Tk widgets use object-oriented approach, which means a quite concrete object hierarchy presents. Interesting point about this object system - it is very dynamic. Initially no widgets objects and no widget classes present, but they immediately appear at the time when they needed.
So they virtually exist, but come into actual existence dynamically. This dynamic approach allows same usage of widget library without any mention from within Tcl::Tk module at all.
Let us look into following few lines of code:
my $text = $mw->Text->pack; $text->insert('end', -text=>'text'); $text->windowCreate('end', -window=>$text->Label(-text=>'text of label'));
Internally, following mechanics comes into play. Text method creates Text widget (known as text in Tcl/Tk environment). When this creation method invoked first time, a package Tcl::Tk::Widget::Text is created, which will be OO presentation of all further Text-s widgets. All such widgets will be blessed to that package and will be in ISA-relationship with Tcl::Tk::Widget.
text
Tcl::Tk::Widget::Text
Second line calls method insert of $text object of type Tcl::Tk::Widget::Text. When invoked first time, a method insert is created in package Tcl::Tk::Widget::Text, with destiny to call invoke method of our widget in Tcl/Tk world.
insert
$text
invoke
At first time when insert is called, this method does not exist, so AUTOLOAD comes to play and creates such a method. Second time insert called already existing subroutine will be invoked, thus saving execution time.
As long as widgets of different type 'live' in different packages, they do not intermix, so insert method of Tcl::Tk::Widget::Listbox will mean completely different behaviour.
Tcl::Tk::Widget::Listbox
Suppose $widget isa-Tcl::Tk::Widget, its path is .path and method method invoked on it with a list of parameters, @parameters:
$widget
.path
method
@parameters
$widget->method(@parameters);
In this case as a first step all @parameters will be preprocessed, during this preprocessing following actions are performed:
for each variable reference its Tcl variable will be created and tied to it
for each code reference its Tcl command will be created and tied to it
each array reference considered as callback, and proper actions will be taken
After adoptation of @parameters Tcl/Tk interpreter will be requested to perform following operation:
$method
m/^[a-z]$/
.path method parameter1 parameter2 ....
.path method parameter1 parameter2
m/^[a-z]+[A-Z][a-z]+$/
.path method submethod parameter1 parameter2 ....
.path method submethod parameter1 parameter2
methodSubmethSubsubmeth
.path method submeth subsubmeth parameter1 parameter2 ....
.path method submeth subsubmeth parameter1 parameter2
In case it is guaranteed that preprocessing of @parameters are not required (in case no parameters are Perl references to scalar, subroutine or array), then preprocessing step described above could be skipped.
To achieve that, prepend method name with underscore, _. Mnemonically it means you are using some internal method that executes faster, but normally you use "public" method, which includes all preprocessing.
_
Example:
# at following line faster method is incorrect, as \$var must be # preprocessed for Tcl/Tk: $button->configure(-textvariable=>\$var); # faster version of insert method of "Text" widget is perfectly possible $text->_insert('end','text to insert','tag'); # following line does exactly same thing as previous line: $text->_insertEnd('text to insert','tag');
When doing many inserts to text widget, faster version will make execution faster.
Tcl::Tk module allows using any widget from Tcl/Tk widget library with either Tcl syntax (via Eval), or with regular Perl syntax.
In order to provide perlTk syntax to any Tcl/Tk widget, only single call should be made, namely 'Declare' method. This is a method of any widget in Tcl::Tk::Widget package, and also exactly the same method of Tcl::Tk interpreter object
Syntax is
$widget->Declare('perlTk_widget_method_name','tcl/tk-widget_method_name', @options);
or, exactly the same,
$interp->Declare('perlTk_widget_method_name','tcl/tk-widget_method_name', @options);
Options are:
-require => 'tcl-package-name' -prefix => 'some-prefix'
'-require' option specifies that said widget requires a Tcl package with a name of 'tcl-package-name'; '-prefix' option used to specify a part of autogenerated widget name, usually used when Tcl widget name contain non-alphabet characters (e.g. ':') so to keep autogenerated names syntaxically correct.
A typical example of such invocation is:
$mw->Declare('BLTNoteBook','blt::tabnotebook',-require=>'BLT',-prefix=>'bltnbook');
After such a call Tcl::Tk module will take a knowledge about tabnotebook widget from within BLT package and create proper widget creation method for it with a name BLTNoteBook. This means following statement:
my $tab = $mw->BLTNoteBook;
will create blt::tabnotebook widget. Effectively, this is similar to following Tcl/Tk code:
package require BLT # but invoked only once blt::tabnotebook .bltnbook1
Also, Perl variable $tab will contain ordinary Tcl/Tk widget that behaves in usual way, for example:
$tab->insert('end', -text=>'text'); $tab->tabConfigure(0, -window=>$tab->Label(-text=>'text of label'));
These two lines are Tcl/Tk equivalent of:
.bltnbook1 insert end -text {text} .bltnbook1 tab configure 0 -window [label .bltnbook1.lab1 -text {text of label}]
Given all previously said, you can also write intermixing both approaches:
$interp->Eval('package require BLT;blt::tabnotebook .bltnbook1'); $tab = $interp->widget('.bltnbook1'); $tab->tabConfigure(0, -window=>$tab->Label(-text=>'text of label'));
As a general rule, you need to consult TCL man pages to realize how to use a widget, and after that invoke perl command that creates it properly. When reading Tcl/Tk documentation about widgets, quite simple transformation is needed to apply to Tcl::Tk module.
Suppose it says:
pathName method-name optional-parameters (some description)
you should understand, that widget in question has method method-name and you could invoke it as
method-name
$widget->method-name(optional-parameters);
$widget is that widget with pathName, created with perl/Tk syntax, or fetched by $int->widget method.
$int->widget
Sometimes in Tcl/Tk method-name consist of two words (verb1 verb2), in this case there are two ways to invoke it, $widget->verb1('verb2',...); or it $widget->verb1Verb2(...); - those are identical.
$widget->verb1('verb2',...);
$widget->verb1Verb2(...);
Widget options are same within Tcl::Tk and Tcl/Tk.
$int->widget( path, widget-type )
When widgets are created they are stored internally and could be retreived by widget(), which takes widget path as first parameter, and optionally widget type (such as Button, or Text etc.). Example:
widget()
# this will retrieve widget, and then call configure on it widget(".fram.butt")->configure(-text=>"new text"); # this will retrieve widget as Button (Tcl::Tk::Widget::Button object) my $button = widget(".fram.butt", 'Button'); # same but retrieved widget considered as general widget, without # concrete specifying its type (Tcl::Tk::Widget object) my $button = widget(".fram.butt");
Please note that this method will return to you a widget object even if it was not created within this module, and check will not be performed whether a widget with given path exists, despite of fact that checking for existence of a widget is an easy task (invoking $interp->Eval("info commands $path"); will do this). Instead, you will receive perl object that will try to operate with widget that has given path even if such path do not exists. In case it do not actually exist, you will receive an error from Tcl/Tk.
$interp->Eval("info commands $path");
To check if a widget with a given path exists use Tcl::Tk::Exists($widget) subroutine. It queries Tcl/Tk for existence of said widget.
Tcl::Tk::Exists($widget)
widget_data
If you need to associate any data with particular widget, you can do this with widget_data method of either interpreter or widget object itself. This method returns same anonymous hash and it should be used to hold any keys/values pairs.
Examples:
$interp->widget_data('.fram1.label2')->{var} = 'value'; $label->widget_data()->{var} = 'value';
Many non-widget Tk commands are also available within Tcl::Tk module, such as focus, wm, winfo and so on. If some of them not present directly, you can always use $int->Eval('...') approach.
focus
wm
winfo
$widget->tooltip("text")
Any widget accepts tooltip method, accepting any text as parameter, which will be used as floating help text explaining the widget. The widget itself is returned, so to provide convenient way of chaining:
tooltip
$mw->Button(-text=>"button 1")->tooltip("This is a button, m-kay")->pack; $mw->Entry(-textvariable=>\my $e)->tooltip("enter the text here, m-kay")->pack;
tooltip method uses tooltip package, which is a part of tklib within Tcl/Tk, so be sure you have it installed.
tklib
$int->create_rotext()
This method creates "rotext" type of widget within Tcl/Tk, which then could be used for example as
$int->Eval('rotext .ro1');
$int->create_scrolled_widget("widgetname")
This method creates "scrolled" type of widget for a given widget type within Tcl/Tk. For example:
$int->create_scrolled_widget("canvas"); $int->Eval('scrolled_canvas .scanv');
This way you can even create a perl/Tk-style widget to be initially scrollable:
$int->create_scrolled_widget("text"); # introduce scrolled_text in Tcl/Tk $int->Declare('SText','scrolled_text'); # bind scrolled_text to Tcl::Tk as SText # now use SText instead of Scrolled('Text',...) everywhere in program $int->mainwindow->SText->pack(-fill=>'both');
The scrolling is taken from snit (scrodgets), and the resulting widget have both scrolled options/methods and widget's options/methods.
When widget method returns some result, this result becomes transformed according to the context, either list or scalar context. Sometimes this transformation is right, but sometimes its not. Unfortunately there are many cases, when Tcl/Tk returns a string, and this string become broken into words, because the function call is placed in list context.
In such cases concatenate such call with empty string to force right behaviour:
use Tcl::Tk; my $mw = Tcl::Tk::tkinit; my $int = $mw->interp; my $but = $mw->Button(-text=>'1 2 3')->pack; print "[", $but->cget('-text'), "] wrong - widget method returns 3 values!\n"; print "[", "".$but->cget('-text'), "] CORRECT - 1 value in scalar context\n"; $int->MainLoop;
Actually, the example above will work correctly, because currently list of function names having list results are maintained. But please contact developers if you find misbehaving widget method!
Currently work is in progress, and some features could change in future versions.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
See http://www.perl.com/perl/misc/Artistic.html
To install Tcl::Tk, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Tcl::Tk
CPAN shell
perl -MCPAN -e shell install Tcl::Tk
For more information on module installation, please visit the detailed CPAN module installation guide.