NAME
Tcl::Tk - Extension module for Perl giving access to Tk via the Tcl extension
SYNOPSIS
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;
DESCRIPTION
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
).
Prerequisites
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.
Access to the Tcl and Tcl::Tk extensions
To get access to the Tcl and Tcl::Tk extensions, put the command near the top of your program.
use Tcl::Tk;
Creating a Tcl interpreter for 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)
The Tcl/Tk interpreter is created automatically by the call to MainWindow
and tkinit
methods, and main window object is returned in this case:
use Tcl::Tk;
my $mw = Tcl::Tk::MainWindow;
my $int = $mw->interp;
Entering the main event loop
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.
Creating and using widgets
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;
random access with
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.
Tcl/Tk syntax
interpreter
Tcl interpreter is used to process Tcl/Tk widgets; within
Tcl::Tk
you create it withnew
, and, given any widget object, you can retreive it by$widget->interp
method. Within pure Tcl/Tk it is already exist.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 itbutt
then you should specify name.fram.butt
. Widget paths are refered in miscellaneous widget operations, and geometry management is one of them.At any time widget's path could be retreived with
$widget->path;
withinTcl::Tk
.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.
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.
Perl/Tk syntax
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.
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
OO explanations of Widget-s of Tcl::Tk
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
.
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.
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.
explanations how Widget-s of Tcl::Tk methods correspond to Tcl/Tk
Suppose $widget
isa-Tcl::Tk::Widget
, its path is .path
and method method
invoked on it with a list of parameters, @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:
- if
$method
is all lowercase,m/^[a-z]$/
-
.path method parameter1 parameter2
.... - if
$method
contains exactly one capital letter inside name,m/^[a-z]+[A-Z][a-z]+$/
-
.path method submethod parameter1 parameter2
.... - if
$method
contains several capital letter inside name,methodSubmethSubsubmeth
-
.path method submeth subsubmeth parameter1 parameter2
....
faster way of invoking methods on widgets
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.
using any Tcl/Tk feature with Tcl::Tk module
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'));
using documentation of Tcl/Tk widgets for applying within Tcl::Tk module
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
$widget->method-name(optional-parameters);
$widget is that widget with pathName, created with perl/Tk syntax, or fetched by $int->widget
method.
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 options are same within Tcl::Tk and Tcl/Tk.
$int->widget( path, widget-type )
method
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:
# 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.
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.
widget_data
method
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';
Non-widget Tk commands
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.
Miscellaneous methods
$widget->tooltip("text")
method
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:
$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.
$int->create_rotext()
method
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")
method
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.
Points of special care
- list context and scalar context
-
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!
BUGS
Currently work is in progress, and some features could change in future versions.
AUTHORS
- Malcolm Beattie.
- Vadim Konovalov, vadim_tcltk@vkonovalov.ru 19 May 2003.
- Jeff Hobbs, jeffh _a_ activestate com, February 2004.
- Gisle Aas, gisle _a_ activestate . com, 14 Apr 2004.
COPYRIGHT
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