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:

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.



no cookie, no javascript, no external resource, KISS!