GUIs with TCL, Tk and TclOO

notes about some more obscure things when building GUIs with TCL/Tk using TclOO. i'll describe windows here, but most also is the case for other, less special widgets.

referencing methods and variables

as Tk is in another namespace than our objects, variables and methods need to be properly referenced. this uses self and varname.

instance variables

    variable bar
    pack [ ttk::label .foo -textvariable [ my varname bar ] ]

instance methods

    method foo {} {
        puts "method foo called"
    }

    pack [ ttk::button .foo -text "foo" -command [ list [self] foo ] ]

a list is constructed to defer evaluation of the objects foo method until the command is called.

path in constructor

to create more than one instance of a class representing a widget the Tk-path has to be stored internally.

package require TclOO

oo::class create MyWindow {
    constructor { _path } {
        variable path $_path

        my ui
    }

    method ui {} {
        variable path

        toplevel $path
        wm title $path "window title"

        pack [ ttk::button $path.close -text "Close" -command [ list [ self ] destroy ] ]
    }

    destructor {
        variable path
        destroy $path
    }
}

this can be instanciated with

MyWindow new .foo

hiding the default toplevel window

useful to remove the default window as it complicates things to reuse the path . for an instance of a class based window.

wm withdraw .

waiting for another window

if one wants to wait for another modal dialog window, a solution is to use vwait to wait for a variable being updated.

oo::class create Foo {
    constructor { _path } {
        variable path $_path
        toplevel $path
        wm title $path "modal"

        pack [ ttk::button $path.openmodal -text "open modal" -command [ list [ self ] waitForModal ] ]
    }

    method setx {newx} {
        variable x
        set x $newx
    }

    method waitForModal {} {
        variable x
        set x [ Bar new .modal [ list [ self ] setx ] ]
        vwait [ my varname x ]
    }
}

oo::class create Bar {
    constructor {_path _callback } {
        variable path $_path
        variable callback $_callback
    
        toplevel $path
        wm title $path "modal"

        pack [ ttk::button $path.close -text "callback" -command [ list [ self ] call ] ]
    }

    method call {} {
        variable path
        variable callback
        {*}$callback "value from modal"
        destroy $path
    }
}