tkgui

  1. tkgui
    1. Description
    2. Author
    3. Repository
    4. Requirements
    5. mktkgui
    6. How mktkgui compiles applications
    7. Running compiled programs
    8. Scheme/Tk GUI syntax
    9. Configuration
    10. Writing configuration files
    11. Builtin setting defaults
      1. Unix platforms
      2. Windows platform
    12. Windows *.rc files
    13. License
    14. Version History

Description

tkgui is a Chicken extension for creating GUI applications on Windows and unix platforms. tkgui leverages the venerable Tcl/Tk toolkit through its C-api enabling good performance and uncomplicated deployment.

The egg compiles to an executable, mktkgui, described in this document.

Author

Jules Altfas

Repository

https://codeberg.org/jrapdx/tkgui

Requirements

mktkgui

mktkgui is easy to use, with only 2 required command line arguments.

mktkgui {options}
-src string         Space-separated list of Scheme source files [required]
-app name           Output app basename (without extension) [required]
-imports string     Space-separated list of additional extension imports
-stop 1..4/symbol   Stop @stage: 1/obj, 2/stk, 3/dll/so, 4/exe, #f/don't stop
-install appname    (Re)installs compiled <app>(.exe) and (lib)<app>(.dll/so)
-uninstall appname  Uninstalls previously installed <app basename>
-gui                Windows only: toggles 'console'/'gui', default="gui"
-verbose            Toggles verbose output, default="on"
-static             Toggles static egg libs (default="off")
-config filename    Config file to use, default="ext-config.scm"
-defaults           Prints built-in default settings.
-settings           Prints configured settings (e.g., ext-config.scm)
-help               Prints this text and exits

Command line arguments -gui, -verbose, -static work as switches, that is, when used without a value produce the inverse effect of the corresponding configuration setting. However these options also accept #t or #f values which override toggling configuration settings. (-gui option has effect only in Windows.)

By default gui and verbose options are #t, using the command line -<option> forces the option off. Conversely, with #f config setting, -<option> means option is on. However, when the command line includes -<option> #t the option is unconditionally on, and if #f, unconditionally off.

Note: on the command line #t and #f have to be enclosed in single or double quotes. (In shell syntax '#' initiates a line comment.)

How mktkgui compiles applications

mktkgui manages 4 major compiled stages without user intervention:

  1. Creates <app-name>.c, Tcl extension code, and compiled (by $(CC)) to an obj file (*.o or *.obj).
  2. Creates <app-name>-scheme-tk.c, containing source and 'glue' code. Then $(CSC) compiles this to a second obj file, using -link files if static egg libs linking is specified.
  3. $(CSC) invokes the linker which links the above object files (and egg *.o/obj files if linking statically) producing a shared library (.dll or .so) that is a fully qualified Tcl/Tk extension as well as a Scheme program. IOW the shared library's interacting Scheme and Tcl code are effectively a "hybrid" application. (The shared object can be loaded into a running wish program which starts the GUI.)
  4. Lastly, $(CC) generates a small dedicated wish-like binary that automatically loads the dll/so from stage 3. The GUI program starts immediately when the library is loaded.

Running compiled programs

On Windows, applications can be launched from anyk directory. Assuming the app was compiled in gui mode (the default), a console won't be rendered. Alternatively using a symbolic link or "shortcut" allows controlling program invocation. Note that attempting to launch the app from the source directory may produce errors about not finding Tcl/Tk init files. Placing a copy of (or symbolic link to) the Tk .dll in the source directory resolves the issue. However, calling the installed (vs. local) program from any directory works as expected.

Unix systems generally don't require anything special. Command-line invocation or shell scripts can be employed. Note that a Tcl/Tk REPL prompt is provided. Running Tcl/Tk commands can modify the program's UI exactly like wish*. It's a way to see effect of commands. Observed effects could potentially be incorporated into the program. On Windows using '-gui' (turn off gui mode) produces a console program like its unix counterpart.

Scheme/Tk GUI syntax

All Scheme to Tcl/Tk translations are handled by the tk procdure:

[procedure] tk 'widget-or-Tcl-command 'argument|"argument" ... '(lambda () ...)

Arguments to tk are symbols or strings, except callbacks which are lists and last argument on the command line. (Except when a -validatecommand callback is being used with entry or spinbox widgets. In this case, the widget -command callback sequence can be placed before or after -validatecommand callback.) A callback is a Scheme lambda that takes no arguments. '(lambda () ...) The body of the lambda can contain any expressions including (tk ...) commands.

Be aware that callbacks do not have lexical scope. That is, the lambda's expressions can only access procedures and variables local to the lambda or at toplevel. (tk ...) returns values returned to it by Tcl.

Widget callbacks can have return values but do not return a value to Scheme. Tcl ignores values returned from callbacks with some exceptions. Entry validation callbacks -validatecommand are required to return a boolean value (#t/#f, 0/1, indicating success or failure of validation procedures). Some bind callbacks return values. Also a widget command callback can be invoked with <widget path> invoke which does return values to its caller.

A tkgui program can create Tcl proc commands which can later be called from callback bodies or other code. Here's a simple example:

 (import scheme.base nutils)
 (tk 'proc "x y" '(lambda ()
                     (let ((x (tk 'set 'x))
                           (y (tk 'set 'y)))
                       (combinesp x y))))
 ;; call the Tcl proc
 (define r (tk 'testing "hello," "out there")
 (writenl r)
 "hello, out there"

A proc command body can return numbers, strings or symbols. Values are returned as strings. Conversion to other types is straightforward.

tk can also create Tcl/Tk entities entirely in Tcl/Tk:

 (tk "proc test2 {x y} {
        puts \"x == $x, y == $y\"
        return [expr {$x + $y}]
      }")

 (printnl 'test2 (tk 'test2 43 54)) 
 ;; -> x == 43, y == 54
 ;; -> test2 97

Here's an example of using (tk ...) calls to show a frame and button:

 (define topvar)
 ...
 (define frm (tk 'frame ".frm")) ;; frm -> ".frm"
 (define frm.btn (tk 'button (string-append frm ".btn") -text "Press me"
                     '-command '(lambda () (set! topvar #t) ...)))

Procedures are provided to make tk programming more convenient:

[procedure] tk/nm widget wgtpath '-option ...

tk/nm derived procedures:

[procedure] tk/frame wgtpath '-option ...
[procedure] tk/label wgtpath "label text" '-option ...
[procedure] tk/labelframe wgtpath "label text" '-option ...
[procedure] tk/button wgtpath "button text" '-option ...
[procedure] tk/entry wgtpath "width" '-option ...
[procedure] tk/spinbox wgtpath '-option ...
[procedure] tk/chkbtn wgtpath "label text" '-option ...
[procedure] tk/radiobtn wgtpath label '-option ...

Tcl/Tk provides window managers to specify widget layout. Two most used are 'pack' and 'grid'. Each has its merits, but 'grid' becomes more advantageous as UIs start to become elaborate. Of course (tk ...) works with any Tk window manager, but 'grid' has been successful and at this point well integrated into the project.

[procedure] tk/grid wgtpath column row '-px '-py '-sy '-rsp '-csp '-option ...
[procedure] tk/grid* subcmd wgtpath '-option ...
[procedure] tk/bind wgtpath event callback

This is equivalent to the prior example:

 (define frm (tk/frame '(frm)))
 (define frm.btn (tk/button `(,frm btn) "Press me" 
                            '-command '(lambda () ...)))

Here's a complete example (example.scm):

 (import scheme.base nutils )
 (tk 'wm 'withdraw ".")
 (define rr 122)
 (tk 'font 'configure 'TkDefaultFont '-family "helvetica" '-size "10")
 (tk 'font 'configure 'TkCaptionFont '-family "helvetica" '-size "10")
 (define frm0 (tk/labelframe '(frm0) "Msgs:"))
 (define msgs (tk/nm 'text `(,frm0 outmsg)
                  '-font "-family helvetica -size 10"
                  '-wrap 'word
                  '-width 32 '-height 9))
 (printnl 'expr (tk "expr {122 * 3}") )
 (define (update-msg newmsg)
   (tk msgs 'replace "@0,0" 'end newmsg))
 (update-msg "Start...")
 (define btn1
   (tk/button `(,frm0 btn) "Press here" 
     '-command
     '(lambda ()
        (tk frm0 'configure '-text "Output:")
        (tk 'tk_messageBox '-message
                             (combine "button works!\nrr is " rr))
        (update-msg (combine "Great job! Now at " rr))
        (set! rr (+ rr 1)))))
 (tk/grid frm0 0 0 '-px 20 '-py 20) 
 (tk/grid msgs 0 0 '-px 10 '-py 12)
 (tk/grid btn1 1 0 '-px 22 '-py "9 19")
 (tk 'wm 'deiconify ".")

Installing the Tcl/Tk html documentation is highly recommended. It's available at Sourceforge. These URLs are for TclTk/9.0 docs but other versions are available.

Configuration

As of tkgui v0.8.0 format of configuration files was changed. The original format is still supported.

Specifically, removed option: app-01. Options with changed names (from/to): exelib/libdir, libchkn/libchicken, cflag/cflags, csc-flag csc-cflag/csc-cflags. The dllflag option is used with the c-compiler, '-shared' (unix) or '-mdll' (windows). When csc produces the dll/shared library, csc-dllflag is '-dll' (windows) and '-s' (unix). (Currently csc compiles dll/shared lib using 'csc-dllflag'.)

Config values are parsed as lists, and transformed into strings to meet requirements of process or system shell calls.

Callback support for TclTk entities includes widget commands, validation callbacks for entry/spinbox widgets, and callbacks for bind, wm and other commands. In addition, (tk 'proc ...) commands have callback support. Note that callbacks may have particular TclTk requirements for return values.

Writing configuration files

mktkgui has platform-specific built-in configurations that are overridden by config file key/value settings. Each source directory can (and should) have its own config file. If a config file named "ext-config.scm" exists it's used by default. However when a directory has multiple configs the -config command line option can be used to select the appropriate config file.

Config files consist of a Scheme list containing ~30 key-value lists. The first element (car) of an option list is its key. When a key (a symbol) matches a default key the (cdr) of the option list replaces the default value fpr that key. A config value can be null, or contain one or more strings, symbols, booleans, or numbers.

Defaults are printed to the terminal using mktkgui -defaults. Use mktkgui -settingsfor configured settings (config overrides).

The option 'static-egg-libs' determines how extension (egg) libraries are linked: static or shared. By default 'static-egg-libs' is #f, that is, shared linking is the initial setting. Setting (static-egg-libs #t) turns on static linking. Alternatively, the mktkgui command line -static option can be used to activate static egg library linking without changing the default. (-static toggles the currently configured setting unless followed by '#f' or '#t'.)

Note: on unix platforms static linking of egg libs requires compiling the egg with "-C" "-fPIC" added to 'csc-options'. Shared linking doesn't require '-fPIC' but having it does no harm.

Example config:

 ((cc  /usr/local/bin/gcc)
  (csc  /usr/local/bin/csc)
  (includes  -I. -I/usr/local/include/chicken -I/usr/local/include)
  (linkdir  /usr/local/lib/chicken/12)
  (imports  myegg my-other-egg)
  ...
  (static-egg-libs  #t)
  ...

Using -static '#f' on the command line guarantees the option is off. With -static '#t' the option is on overriding config file settings. (Also true for -gui and -verbose options.)

Builtin setting defaults

Unix platforms

cc                /usr/bin/gcc
cflags            -Og -g -fPIC
config-check      "unix defaults"
csc               /usr/local/bin/csc
csc-cflags        -C -g -C -fPIC
csc-dllflag       -s
csc-libchicken    -L -lchicken)
csc-stublibs      -L -ltclstub9.0 -L -ltkstub9.0
csc-tcltklibs     -L -ltcl9.0 -L -ltcl9tk9.0 
csc-winflag              
defs              -DUSE_TCL_STUBS -DUSE_TK_STUBS
dll               .so
dllflag           -shared
exedir            /usr/local/bin
gui               #f
imports           
includes          -I. -I/usr/local/include/chicken -I/usr/local/include
ldconfig          ldconfig
ldflags           -L/usr/local/lib -L.
libchicken        -lchicken
libdir            /usr/local/lib
libprefix         lib
linkdir           "."                                                                                                                                                                                                                                                                                                                                                                                                                                                            
link-list         srfi-1 srfi-13 srfi-14 nutils
obj               .o
platform          unix
rc                
static-egg-libs   #f
stublibs          -ltclstub9.0 -ltkstub9.0
sudo              sudo
tcltklibs         (-ltcl9.0 -ltcl9tk9.0)
verbose           #t
winflag           

Windows platform

cc                c:/w64devkit/bin/gcc.exe
cflags            -Og -g
config-check      "windows defaults"
csc               c:/usr/local/bin/csc.exe
csc-cflags   
csc-dllflag       -dll
csc-libchicken    -L -lchicken)
csc-stublibs      -L -ltclstub9.0 -L -ltkstub9.0
csc-tcltklibs     -L -ltcl9.0 -L -ltcl9tk9.0 
csc-winflag       -gui
defs              -DUSE_TCL_STUBS -DUSE_TK_STUBS
dll               .dll
dllflag           -mdll
exe               .exe
exedir            c:/usr/local/bin
gui               #t
imports           
includes          -I. -Ic:/usr/local/include/chicken -Ic:/usr/local/include
ldconfig          
ldflags           -Lc:/usr/local/lib -L.
libchicken           -lchicken
libdir            c:/usr/local/bin
libprefix         
linkdir           "."
link-list         srfi-1 srfi-13 srfi-14 nutils
obj               .obj
platform          windows
rc                ./rc
static-egg-libs   #f
stublibs          (-ltclstub -ltkstub)
sudo              
tcltklibs         -ltcl91 -ltcl9tk91
verbose           #t
winflag           -mwindows

Windows *.rc files

Windows uses *rc files to assign resources like icons, cursors and metadata to a library or executable. In a Tk application, .rc data isn't really optional as it contains rules defining geometry of widget layout.

Fortunately it's not difficult to manage. The Tk source distribution includes an 'rc' directory under 'win' in the source tree. Run the './configure' script in the win directory to generate 'wish.exe.manifest'. If not already copied, copy wish.exe.manifest to the rc directory and copy the rc dir to your GUI program location.

Creating a unique icon for an app is a good idea. Using the Tk icon in source distribution as a template, replace the default Tk icon with a new one also named 'tk.ico'. To complete this customization, place your app 'tk.ico' in the Tk rc directory before compiling Tk from source. Finally, rename the tcl9tk9x.dll and libtcl9tk9x.dll.a files. This requires editing the Makefile, not too difficult a procedure.

Obtain the latest sources (Github) and after running the ./configure script a Makefile file should be located in the unix or windows directory. In an editor look for TK_LIB_FILE which should be like libtcl9tk9.1.so depending on exact version. Modify the file name, perhaps libtcl9tk9.1x.so. Use the same name modification for related variables like TK_LIB_FLAG -ltcl9tk9.1x. Put the custom tk.ico in the rc directory. Compiling as normal gives a custom-named shared lib.

License

Copyright (c) 2025 Jules Altfas

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Version History