Home / Resources / Easily build GUI natively with Tk
Introduction
Tcl is a high-level, general-purpose, interpreted, dynamic programming language. It was designed with the goal of being very simple but powerful. Tcl casts everything into the mold of a command, even programming constructs like variable assignment and procedure definition. Tcl supports multiple programming paradigms, including object-oriented, imperative, functional, and procedural styles.
Tk is a cross-platform widget toolkit that provides a library of basic elements of GUI widgets for building a graphical user interface (GUI) in many programming languages. It was originally developed as an extension for the Tcl scripting language.
Tcl backend
We can write a Tcl program dedicated to any particular purpose. In this example, we imagine a program to turn a wireguard VPN up and down.
We can store all the information dedicated to the backend and GUI into a namespace and write functions to:
- detect if our VPN connexion is up or down
- turn the VPN connexion up or down
Here is an example of such Tcl backend. We latter create a Tk frontend function
to update the window that we call wireguard::updateWindow
.
namespace eval wireguard {
# specific to the wireguard and desktop environment configurations:
set theInterface "wg0"
set theUpCommand "pkexec wg-quick up $theInterface"
set theDownCommand "pkexec wg-quick down $theInterface"
# content of the GUI:
set isActive 0
set theLabelText "VPN is not active"
set theButtonText "Activate"
}
proc wireguard::isActive {
theInterfaceName
} {
set theCode [catch { exec ip link } theText]
foreach theLine [split $theText "\n"] {
if { [string first " " $theLine] < 1 } {
continue
}
set theInterface [lindex [split $theLine ":"] 1]
set theInterface [string trim $theInterface]
if { [string equal $theInterface $theInterfaceName] } {
return 1
}
}
return 0
}
proc wireguard::activationButttonCommand {
} {
if { $wireguard::isActive } {
set theCommand $wireguard::theDownCommand
set theErrorMessage "Deactivation of the VPN returned an error."
} else {
set theCommand $wireguard::theUpCommand
set theErrorMessage "Activation of the VPN returned an error."
}
set theCode [catch { exec -- {*}$theCommand >& /tmp/wireguard } theText]
if { $theCode } {
# handle the error here
return 1
}
return [wireguard::updateWindow]
}
Tk frontend
We now can create our GUI with Tk. Here we only have one image and two buttons: one to switch the VPN up or down and one to exit the program. Images can be added from data in base64.
package require Tk
# update data with base64 content:
image create photo "VPN_OFF" -data {}
image create photo "VPN_ON" -data {}
wm title . "My VPN"
wm resizable . 0 0
wm protocol . WM_DELETE_WINDOW { destroy . ; exit 0 }
set theTopFrame [frame .theTopFrame]
set theImage [label .theTopFrame.theImage -image "VPN_OFF"]
set theLabel [label .theTopFrame.theLabel \
-textvariable "wireguard::theLabelText"]
pack $theTopFrame
pack $theImage $theLabel -side left
set theBottomFrame [frame .theBottomFrame]
set theButton [button .theBottomFrame.theButton \
-textvariable "wireguard::theButtonText" \
-command "wireguard::activationButttonCommand"]
set theQuitButton [button .theBottomFrame.theQuitButton \
-text "Exit" \
-command "exit 0"]
pack $theBottomFrame
pack configure $theButton $theQuitButton -side left
The last step is to write the wireguard::updateWindow
function to update the
GUI and call it at least once.
Here is an example of such function.
proc wireguard::updateWindow {
} {
set wireguard::isActive 0
if { [wireguard::isActive $wireguard::theInterface] } {
set wireguard::isActive 1
}
if { $wireguard::isActive } {
set wireguard::theLabelText "VPN is active"
set wireguard::theButtonText "Deactivate"
$::theImage configure -image "VPN_ON"
} else {
set wireguard::theLabelText "VPN is not active"
set wireguard::theButtonText "Activate"
$::theImage configure -image "VPN_OFF"
}
return 0
}
There is no need for a compilation, our program can be executed from the Tcl interpreter.
Associated resources
Here is an archive which contains the discussed example.