widget(3itcl)

πŸ“¦
version 4.3.8 Β· 2 versions
, in ubuntu resolute, ubuntu stonking

πŸ’– widget(3itcl) β€” the [incr Tcl] manual, bestie edition πŸ’–

okay so we're in the [incr Tcl] manual, section 3itcl, and today's main character is itcl::widget. pull up a chair, grab a strawberry πŸ“, we're doing this.


✨ NAME ✨

itcl::widget β€” create a widget class of objects πŸŽ€


🚨 WARNING! 🚨

This is new functionality in [incr Tcl] where the API can still change!! 😭

fr fr, the API is still in its glow-up era, so don't get too attached to any one shape of it. it might shift under you. you've been warned, bestie πŸ’…


🌸 SYNOPSIS 🌸

itcl::widget widgetName {
    inherit baseWidget ?baseWidget...?
    constructor args ?init? body
    destructor body
    public method name ?args? ?body?
    protected method name ?args? ?body?
    private method name ?args? ?body?
    public proc name ?args? ?body?
    protected proc name ?args? ?body?
    private proc name ?args? ?body?
    public variable varName ?init? ?config?
    protected variable varName ?init? ?config?
    private variable varName ?init? ?config?
    public common varName ?init?
    protected common varName ?init?
    private common varName ?init?

    public command ?arg arg ...?
    protected command ?arg arg ...?
    private command ?arg arg ...?

    <delegation info> see delegation page

    <option info> see option page

    set varName ?value?
    array option ?arg arg ...?
}

widgetName objName ?arg arg ...?

objName method ?arg arg ...?

widgetName::proc ?arg arg ...?

like, look at all those options. she's got range πŸ‘‘


πŸ¦‹ DESCRIPTION πŸ¦‹

okay so one of the fundamental constructs in [incr Tcl] is the widget definition. and a widget? it's basically a class, but with extra features bolted on β€” think class but she went to finishing school ✨

Every widget acts as a template for actual objects that can be created. The widget itself is a namespace which contains things common to all objects. So the widget is like the group chat, and the objects are the members, iykyk πŸ’¬

Each object has its own unique bundle of data 🎁 which contains instances of the "variables" defined in the widget definition. Each object also has a built-in variable named "this", which contains the name of the object. yes, every object knows its own name. main character energy, honestly πŸ‘‘

Widgets can also have "common" data members that are shared by all objects in a widget. so like, the group chat's pinned message πŸ“Œ β€” everyone sees it, everyone shares it.


Two types of functions can be included in the widget definition:

  • "Methods" β€” functions which operate on a specific object, and therefore have access to both "variables" and "common" data members. they get the whole picture πŸ’–
  • "Procs" β€” ordinary procedures in the widget namespace, and they only have access to "common" data members. no private info for them πŸ™ˆ

If the body of any method or proc starts with "@", it is treated as the symbolic name for a C procedure. Otherwise, it is treated as a Tcl code script. See below for details on registering and using C procedures. (yes bestie, "below" β€” keep reading, we get there 🫢)


A widget can only be defined once, although the bodies of widget methods and procs can be defined again and again for interactive debugging. See the body and configbody commands for details. so like, the widget is set in stone, but you can keep redoing her makeup mid-debug-session πŸ’…


Each namespace can have its own collection of objects and widgets. The list of widgets available in the current context can be queried using the "itcl::find widgets" command, and the list of objects, with the "itcl::find objects" command. πŸ”

A widget can be deleted using the "delete widget" command. Individual objects can be deleted using the "delete object" command. πŸ—‘οΈ


more coming, bestie β€” this page is long and we're only on part 1 🌈

πŸ’« WIDGET DEFINITIONS πŸ’«

✨ widget widgetName definition ✨

okay so bestie, this is THE moment β€” you're literally giving the world a widget named widgetName. here's the tea though: if a widget called widgetName is already out there living its life, or if there's already a command named widgetName hanging out in the current namespace context, this command is gonna throw an error at you. no duplicates allowed, periodt. but if your widget definition parses cleanly? then widgetName becomes a command in the current context, and it's the one in charge of making objects for this widget. main character energy fr fr πŸ‘‘

the widget definition gets evaluated as a series of Tcl statements that define elements inside the widget. these are the widget definition commands you get to play with:


🌸 inherit baseWidget ?baseWidget...? 🌸

this is how your current widget gets its whole personality from one or more base widgets β€” it inherits their characteristics. the base widgets must have been defined by an earlier widget command, OR they gotta be reachable through the auto-loading facility (peep the "AUTO-LOADING" section below, iykyk). also important: a single widget definition can hold no more than one inherit command. one. that's it. don't be greedy 🫢

the order of the baseWidget names in that inherit list literally changes how names get resolved for widget members. when the same member name shows up in two or more base widgets, the base widget that comes first in the inherit list wins. like, it takes precedence. example time:

if widgets "Foo" and "Bar" both contain the member "x", and another widget has this:

inherit Foo Bar

then the name "x" means "Foo::x". any other inherited members named "x" have to be referenced with their explicit name, like "Bar::x". no shortcuts for those ones, sorry bestie.


🌈 constructor args ?init? body 🌈

declares the args argument list and the body used for the constructor β€” and the constructor is automatically invoked whenever an object is created. automatic. no manual dialing.

before the body runs, the optional init statement is used to invoke any base widget constructors that need arguments. variables in the args specification can be accessed inside the init code fragment, and passed along to base widget constructors. after the init statement is evaluated, any base widget constructors that haven't been executed get invoked automatically, without arguments. this is the safety net so that all base widgets are fully constructed before the constructor body gets its turn. by default, this scheme makes constructors fire in order from least- to most-specific. and yes, that is exactly the opposite of the order widgets are reported by the info heritage command. iconic reversal behavior πŸ’…

if construction goes well, the constructor always returns the object name β€” no matter how the body is defined β€” and the object name becomes a command in the current namespace context. if construction fails, you get an error message back. that's the deal.


πŸ¦‹ destructor body πŸ¦‹

declares the body used for the destructor, which is automatically invoked when an object is deleted. if the destructor succeeds, the object data is destroyed and the object name is removed as a command from the interpreter. clean exit, slay. if destruction fails, an error message comes back and the object stays. it just... remains. haunting the place 😭

when an object gets destroyed, all destructors in its widget hierarchy are invoked in order from most- to least-specific. that's the order the widgets are reported by the info heritage command, and it's exactly the opposite of the default constructor order. mirror vibes ✨


πŸŽ€ method name ?args? ?body? πŸŽ€

declares a method called name. when the method body runs, it automatically has access to object-specific variables AND common data members. automatic access, no asking.

if the args list is specified, it sets up the usage information for this method. the body command can be used to redefine the method body, but the args list has to match this specification. consistency, bestie.

inside the body of another widget method, a method can be invoked like any other command β€” just use its name. easy. but outside the widget context, the method name has to be prefaced with an object name, because that's what provides the context for the data it manipulates. methods in a base widget that get redefined in the current widget, or hidden by another base widget, can be qualified using the "widgetName::method" syntax. that's your full-name access pass 🫢


πŸ“ proc name ?args? ?body? πŸ“

declares a proc called name. a proc is an ordinary procedure sitting inside the widget namespace. unlike a method, a proc gets invoked without referring to a specific object. when the proc body runs, it automatically has access only to common data members. that's the whole access list. no object stuff.

if the args list is specified, it sets up the usage information for this proc. the body command can be used to redefine the proc body, but the args list has to match this specification.

within the body of another widget method or proc, a proc can be invoked like any other command β€” just say its name. in any other namespace context, you invoke the proc using a qualified name like "widgetName::proc". procs in a base widget that get redefined in the current widget, or hidden by another base widget, can also be accessed through their qualified name. same rules, different member type πŸ’œ


🧚 variable varName ?init? ?config? 🧚

defines an object-specific variable named varName. all object-specific variables are automatically available in widget methods β€” you do not need to declare them with anything like the global command. they just show up. blessed behavior.

if the optional init string is given, it's used as the initial value of the variable when a new object is created. and listen: initialization forces the variable to be a simple scalar value. uninitialized variables, on the other hand, can be set within the constructor and used as arrays. so pick your fighter.

the optional config script is only allowed for public variables. if specified, this code fragment is executed whenever a public variable is modified by the built-in "configure" method. the config script can also be specified outside of the widget definition using the configbody command. flexible queen πŸ’…


🌟 common varName ?init? 🌟

declares a common variable named varName. common variables live in the widget namespace and are shared by all objects belonging to the widget. they're basically just like global variables, except you don't have to declare them with the usual global command. they're automatically visible in all widget methods and procs. group chat access, everyone's in it πŸ«‚

if the optional init string is specified, it's used as the initial value of the variable. initialization forces the variable to be a simple scalar value. uninitialized variables, on the other hand, can be set with subsequent set and array commands and used as arrays. your call.

once a common data member has been defined, it can be set using set and array commands within the widget definition. this lets common data members get initialized as arrays. for example:

itcl::widget Foo {
    protected common boolean
    set boolean(true) 1
    set boolean(false) 0
}
⚠️ note that if common data members are initialized within the constructor, they get initialized again and again whenever new objects are created. not a one-time thing. every single object creation. be careful bestie, that's a loop 😭

πŸ‘‘ public command ?arg arg ...? πŸ‘‘

πŸ‘‘ protected command ?arg arg ...? πŸ‘‘

πŸ‘‘ private command ?arg arg ...? πŸ‘‘

these three set the protection level for widget members that are created when command is evaluated. the command is usually method, proc, variable or common, and the remaining arg's complete the member definition. but plot twist: command can also be a script containing many different member definitions, and the protection level applies to all of the members that get created. one label, whole squad 🫢


πŸ’– WIDGET USAGE πŸ’–

once a widget has been defined, the widget name can be used as a command to create new objects belonging to that widget. that's the whole point, and it's so satisfying.

✨ widgetName objName ?args...? ✨

creates a new object in widget widgetName with the name objName. the remaining arguments get passed to the constructor of the most-specific widget. and that in turn passes arguments to base widget constructors before invoking its own body of commands. very much a chain reaction ✨

if construction succeeds, a command called objName is created in the current namespace context, and objName is returned as the result of this operation. if an error happens during construction, the destructors are automatically invoked to free any resources that have been allocated, the object is deleted, and an error is returned. cleanup crew, immediately 🧹

if objName contains the string "#auto", that string gets replaced with an automatically generated name. these names have the form widgetName<number>, where the widgetName part gets modified to start with a lowercase letter. so in widget "Toaster", for example, the "#auto" specification would produce names like toaster0, toaster1, etc. cute, right?

and note that "#auto" can also be buried within an object name:

fileselectiondialog .foo.bar.#auto -background red

this would generate an object named ".foo.bar.fileselectiondialog0". buried but still found. iconic 🌈

πŸŽ€ OBJECT USAGE πŸŽ€

okay so the moment your object exists, its name literally becomes a command, and you use it to poke at the object's methods. main character behaviour fr.

objName method ?args...?

That invokes a method named method on an object named objName. Whatever extra args you tack on get passed straight into that method's argument list. And method can be:

  • "constructor" β€” the birth moment 🐣
  • "destructor" β€” the goodbye πŸ’€
  • literally any method name that shows up in the widget definition
  • or any of the built-in methods below, no cap

✨ BUILT-IN METHODS ✨

πŸ“ objName cget option

Gives you access to public variables as if they were configuration options β€” it's giving major Tk widget energy, because it mimics the usual cget operation. The option argument is a string shaped like -varName, and this method hands back the current value of the public variable varName. Slay.

πŸ“ objName configure ?option? ?value option value ...?

Same deal but for configure β€” it mimics the usual configure operation on Tk widgets. Here's the tea:

  • With no arguments, it returns a list of lists describing all the public variables. Each one of those lists has exactly three elements: the variable name, its initial value, and its current value.
  • If you pass a single option of the form -varName, you get the info for just that one variable.
  • Otherwise the arguments are treated as option/value pairs, assigning new values to public variables. Each variable gets its new value, and if it has any "config" code attached to it, that code is executed in the context of the widget where it was defined. And if that "config" code throws an error? Bestie, the variable gets set back to its previous value and the configure method returns an error. Rewind. ↩️

πŸ“ objName isa widgetName

Returns non-zero if widgetName can be found in the object's heritage, and zero otherwise. Simple. Iconic. Yes/no vibes.

πŸ“ objName info option ?args...?

Returns information about a particular object named objName, or about its widget definition. The option parameter includes all of the following, plus whatever options the usual Tcl info command recognises:

  • objName info widget β€” returns the name of the most-specific widget for object objName.
  • objName info inherit β€” returns the list of base widgets exactly as they were defined in the "inherit" command, or an empty string if this widget has no base widgets.
  • objName info heritage β€” returns the current widget name and the entire list of base widgets, in the order they get traversed for member lookup and object destruction.
  • objName info function ?cmdName? ?-protection? ?-type? ?-name? ?-args? ?-body? β€” with no arguments, this returns a list of all widget methods and procs. If cmdName is specified, you get info for that specific method or proc. If you pass no flags, you get a list with these elements: the protection level, the type (method/proc), the qualified name, the argument list, and the body. Flags let you request specific elements out of that list.
  • objName info variable ?varName? ?-protection? ?-type? ?-name? ?-init? ?-value? ?-config? β€” with no arguments, this returns a list of all object-specific variables and common data members. If varName is specified, you get info for that specific data member. If you pass no flags, you get a list with these elements: the protection level, the type (variable/common), the qualified name, the initial value, and the current value. If varName is a public variable, the "config" code gets included on that list too. Flags let you request specific elements out of that list.

πŸ¦‹ CHAINING METHODS/PROCS πŸ¦‹

so here's the situation: sometimes a base widget has a method or proc that gets redefined with the same name in a derived widget. That's the whole point β€” the derived widget handles the same operations as the base widget, just with its own specialised behaviour. periodt.

Say we've got a Toaster widget that looks like this:

itcl::widget Toaster {
    variable crumbs 0
    method toast {nslices} {
        if {$crumbs > 50} {
            error "== FIRE! FIRE! =="
        }
        set crumbs [expr {$crumbs+4*$nslices}]
    }
    method clean {} {
        set crumbs 0
    }
}

Now we make another widget, SmartToaster, that redefines the "toast" method. If we want to reach the base widget's version of the method, we can qualify it with the base widget name so there's zero ambiguity:

itcl::widget SmartToaster {
    inherit Toaster
    method toast {nslices} {
        if {$crumbs > 40} {
            clean
        }
        return [Toaster::toast $nslices]
    }
}

But instead of hard-coding that base widget name (so rigid πŸ™ˆ), we can use the "chain" command like this:

itcl::widget SmartToaster {
    inherit Toaster
    method toast {nslices} {
        if {$crumbs > 40} {
            clean
        }
        return [chain $nslices]
    }
}

And what does chain actually do? It searches through the widget hierarchy for a slightly more generic (base widget) implementation of a method or proc, and invokes it with the arguments you specified. It starts at the current widget context and searches through base widgets in the order reported by the "info heritage" command. And if no other implementation turns up? It does nothing and returns the null string. no thoughts, head empty, moving on πŸ’…


🌈 AUTO-LOADING 🌈

Widget definitions do not have to be loaded explicitly, bestie β€” they can be loaded on demand by the usual Tcl auto-loading facility. Every directory that holds widget definition files should have an accompanying "tclIndex" file, and each line in that file identifies a Tcl procedure or [incr Tcl] widget definition plus the file where that definition lives.

Say a directory has the definitions for widgets "Toaster" and "SmartToaster". Then the "tclIndex" file for that directory would look like:

# Tcl autoload index file, version 2.0 for [incr Tcl]
# This file is generated by the "auto_mkindex" command
# and sourced to set up indexing information for one or
# more commands.  Typically each line is a command that
# sets an element in the auto_index array, where the
# element name is the name of a command and the value is
# a script that loads the command.

set auto_index(::Toaster) "source $dir/Toaster.itcl"
set auto_index(::SmartToaster) "source $dir/SmartToaster.itcl"

The auto_mkindex command is what you use to automatically generate those "tclIndex" files. And the auto-loader needs to know about the directory β€” so append the directory name to the "auto_path" variable. Once that's in place, widgets get auto-loaded as needed whenever they're used in an application. chef's kiss πŸ‘‘


πŸ’œ C PROCEDURES πŸ’œ

C procedures can be integrated right into an [incr Tcl] widget definition to implement methods, procs, and the "config" code for public variables. The rule: any body that starts with @ is treated as the symbolic name for a C procedure.

Symbolic names get established by registering procedures via Itcl_RegisterObjC(). Usually you do this in the Tcl_AppInit() procedure, which gets called automatically when the interpreter starts up. In this example, the procedure My_FooObjCmd() is registered with the symbolic name "foo", and it can then be referenced in the body command as @foo:

int
Tcl_AppInit(interp)
    Tcl_Interp *interp;     /* Interpreter for application. */
{
    if (Itcl_Init(interp) == TCL_ERROR) {
        return TCL_ERROR;
    }

    if (Itcl_RegisterObjC(interp, "foo", My_FooObjCmd) != TCL_OK) {
        return TCL_ERROR;
    }
}

C procedures are implemented just like ordinary Tcl commands β€” go read the CrtCommand man page for the deets. Inside the procedure, widget data members can be accessed like ordinary variables using Tcl_SetVar(), Tcl_GetVar(), Tcl_TraceVar(), etc. And widget methods and procs can be executed like ordinary commands using Tcl_Eval(). [incr Tcl] makes all of this possible by automatically setting up the context before executing the C procedure. the way it just… handles that for you 😭

This scheme gives you a natural migration path for code development:

  1. Develop widgets quickly using Tcl code to implement the bodies.
  2. Build and test an entire application.
  3. When necessary, implement individual bodies with C code to improve performance.

⭐ KEYWORDS ⭐

widget, object, object-oriented


and that's the whole page, bestie!! 🫢 this was itcl 4.0, the widget(3itcl) manual page β€” dated with that lil footer timestamp at the bottom (you know the one, the version-and-date line that lives right down there πŸ‘€). go forth and build something iconic πŸ’…βœ¨

πŸ“¦ where this page comes from