WJG (02/08/2016) Now that the OO package has been integrated into the Tcl core module I thought it about time to take a look at creating Gnocl megawidgets using this method. This task proved much easier and quicker to implement than widget overloading and with no loss of flexibility. Here's the basic megawidget template which I now stash away in my ./config/geany/filedefs folder.
#{fileheader}
#---------------
# Boilerplate object builder package for Gnocl derived megawidgets.
# Based upon approach used in Gnocl source code.
#---------------
# USAGE: Substitute keywords "_prj_" and "_widget_" for unique project and object type identifier.
#---------------
#!/bin/sh
# the next line restarts using tclsh \
exec tclsh "$0" "$@"
package require Gnocl
#---------------
# lists of valid widget options, commands and components
#---------------
#
oo::class create _prj_:_widget_ {
constructor {} {
# declare variables to store data, settings and widget ids
my variable container
my variable but1
my variable but2
my variable parts
my variable data
set data "How Now Brown Cow"
# create and assemble megawidget parts
set container [gnocl::vBox]
set but1 [gnocl::button -text HIDIHI -icon %#Help]
set but2 [gnocl::button -text HODIHO -icon %#Stop]
$container add $but1
$container add $but2
# retain a list of parts
set parts [list container but1 but2]
}
destructor {
my variable container
$container delete
}
method getId {} {
my variable container
return $container
}
method class {} {
return _widget_
}
#---------------
# generic controller, directly access all elements of widget control
method cmd { args } {
my variable parts
foreach var $parts { my variable $var }
eval [set [lindex $args 0]] [lindex $args 1] [lrange $args 2 end]
}
method configure { args } {
my variable parts
foreach var $parts { my variable $var }
eval [set [lindex $args 0]] configure [lrange $args 1 end]
}
method cget { args } {
my variable parts
foreach var $parts { my variable $var }
eval [set [lindex $args 0]] cget [lrange $args 1 end]
}
# set or retrieve megawidget internal data
method getData {} {
my variable data
return $data
}
method setData {val} {
my variable data
set data $val
}
# object operations, or specfic methods
method tooltips { tt1 tt2 } {
my variable parts
foreach var $parts { my variable $var }
$but1 configure -tooltip $tt1
$but2 configure -tooltip $tt2
}
# manipulate internal data
method tocaps {} {
my variable data
set data [string toupper $data]
}
}
#===============
# DEMO
#===============
proc demo {} {
set b1 [_prj_:_widget_ new]
puts [$b1 class]
gnocl::window -child [$b1 getId] -title A -setSize 0.125 -x 200
$b1 configure but1 -onClicked { puts "HI %d" } -data PING
$b1 configure but2 -onClicked { puts "HO %d" } -data PONG
puts [$b1 cget but1 -data]
$b1 tooltips "HI DI HI" "HO DI HO"
puts [$b1 getData]
$b1 tocaps
puts [$b1 getData]
set b2 [_prj_:_widget_ new]
gnocl::window -child [$b2 getId] -title B -setSize 0.125 -x 400
$b2 destroy
catch { $b2 configure but2 -onClicked { puts "HO %d" } -data PONG } {}
}
demoAMG: It looks like those [eval]s can be written in terms of {*}:
eval [set [lindex $args 0]] [lindex $args 1] [lrange $args 2 end]
{*}[set [lindex $args 0]] {*}[lrange $args 1 end]DKF: Interesting how you are bringing variables into the scope of the methods doing the delegation, but I think you can do it more simply:
method cmd { part subcommand args } {
my variable parts
if {$part ni $parts} { return -code error "no such part \"$part\"" }
{*}[set [my varname $part]] $subcommand {*}$args
}This has fewer hazards (and makes a nicer error message) and yet otherwise works the same in all sane cases. Anything where it doesn't... well you're probably better off writing a method then.