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 options
      1. Unix defaults
      2. Windows defaults
    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 options.

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 options -gui, -verbose, -static work as switches, that is, when used without a value produce the inverse effect of the corresponding configuration specifier. However these options also accept #t or #f values which override toggling configuration settings. (gui options are Windows only.)

Since default gui and verbose options are #t, using the command line -<option> or -<option> '#t' forces the option off. Conversely, -<option> '#f' means option is on.

This table summarizes the (default) interaction of config and command line options:


Config default [on/off] Cmd line Cmd line [force off] Cmd line [force on]
(winflag -mwindows) [on] -gui [off] -gui '#t' -gui '#f'
(winflag ) [off] -gui [on] -gui '#f' -gui '#t'
(verbose #t) [on] -verbose [off] -verbose '#t' -verbose '#f'
(static-egg-libs #f) [off] -static [on] -static '#f' -static '#t'

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

 ## static linking of egg libs (with default configuration)
 $ mktkgui -src myapp.scm -app myapp -static
 
 ## static linking of egg libs (default configuration)
 $ mktkgui -src myapp.scm -app myapp -static '#t'
 $ mktkgui -src myapp.scm -app myapp -static '#t' \
   -config "my-config.scm"
 ## force shared egg lib linking
 $ mktkgui -src myapp.scm -app myapp -static "#f"

 ## this invocation turns off verbose terminal output
 $ mktkgui -src my-gui-app.scm -app myapp -verbose
 config-check: ext-config.scm
 myapp.obj -> OK.
 myapp-scheme-tk.obj -> OK.
 myapp.dll -> OK.
 myapp.exe -> OK.
 install: myapp.dll and myapp.exe -> OK.
 ## installed to configured locations. 

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. The above object files (and egg *.o/obj files if linking statically) are linked to form a shared library (.dll or .so) that is a fully qualified Tcl/Tk extension as well as a Scheme program. IOW the intereacting 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, mktkgui generates a small wish-like binary dedicated to automatically loading 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 directly from the installation directory. Assuming the app was compiled in gui mode (the default), a console won't be rendered. Alternatively using a link or "shortcut" allows controlling program invocation. Note that attempting to launch from the source directory may produce errors about finding Tcl/Tk init files. Placing a copy of (or symbolic link to) the Tk .dll in the source directory resolves the issue.

Unix systems generally don't require anything special. Command-line invocation or shell scripts can be employed.

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 -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 list that takes no arguments. '(lambda () ...) The body of the lambda can contain any expressions including (tk ...) commands.

It's important to note that callbacks do not have lexical scope. That is, the lambda's expressions can only access internal or toplevel Scheme variables and procedures. (tk ...) returns values returned to it by Tcl.

Tcl widget callbacks do not return a value to Scheme. Tcl expects to receive a a "neutral" value from these callbacks. For this reason it's a good practice to put 'return or (tk 'return) as the final expression of widget and bind callbacks.

An exception is that -validatecommand callbacks must return a standard boolean to TclTk. That can take the form of a Scheme boolean (#f/#t) or (tk 'return 0|1).

A tkgui program can create Tcl proc commands which can later be called from (tk 'procname ...) bodies. Here's a brief example:

 (import scheme.base nutils)
 (tk 'proc "x y" '(lambda ()
                     (let ((x (tk 'set 'x))
                           (y (tk 'set 'y)))
                       (tk 'return (combinesp x y)))))
 (printnl (tk 'testing "hello," "out there"))
 hello, out there

A proc command's callback can return various types (string, number, boolean) via (tk 'return ...)).

Certain other callbacks return a value to a caller under particular circumstances. Calling 'invoke on a Button,CheckButton or RadioButton command will run the widget's callback and return its result. Calling 'validate on an Entry or Spinbutton returns a boolean (0 or 1).

Here's an example of widget contruction using (tk ...) calls to create a frame and interior button:

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

Several builtin procedures are provided to make programming with tk more convenient:

[procedure] tk/nm widget wpath '-option ...
[procedure] tk/frame wpath '-option ...
[procedure] tk/label wpath "label text" '-option ...
[procedure] tk/labelframe wpath "label text" '-option ...
[procedure] tk/button wpath "button text" '-option ...
[procedure] tk/entry wpath "width" '-option ...
[procedure] tk/spinbox wpath '-option ...
[procedure] tk/chkbtn wpath "label text" '-option ...
[procedure] tk/grid wpath column row '-px '-py '-sy '-rsp '-csp '-option ...
[procedure] tk/grid* subcmd wpath '-option ...
[procedure] tk/bind wpath event callback

Using a few of these additional capabilities, this is another way to write 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 0 1 '-px 22 '-py "9 19")
 (tk 'wm 'deiconify ".")

Given the multitude of widget (and Tcl/Tk command) options, 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 can also be downloaded from the same site.

Configuration

As of tkgui v0.8.0 changes have been made to configuration options and format of configuration files. However, the original format still works provided files are edited to update option names and values.

Specifically, these options have been completely removed: app-01, csc_stublibs. Options with changed names (from/to): exelib/libdir, libchkn/libchicken, cflag/cflags, csc-flag csc-cflag/csc-cflags. The dllflag option now has single value, '-shared' or '-mdll', '-o' is no longer included.

The new configuration format is simpler: each option is a Scheme proper list rather than dotted pair. An advantage is uniformity. All config values are parsed as lists, and subsequently transformed into strings to comply with the requirements of subprocess or system shell calls. However as noted above mktkgui will continue to work with the "old style" configs.

Callback support for TclTk elements has been expanded to include 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 these callbacks have particular TclTk requirements for return values, described in detail below.

Writing configuration files

mktkgui has platform-specific built-in configurations which are overridden by key/value settings contained in config files. Config files are per-directory, each source directory can (and should) have its own config file. A per-directory config file named "ext-config.scm" is automatically used. However any number of configs can exists in a directory. The -config command line option is used to specify the appropriate config file.

Config files are expected to contain a Scheme list whose elements are option lists. The first element (car) of the option list is its key. When a key (a symbol) matches a default key the list of elements after the first element (cdr) of the option list replaces the default value. A config value is therefore always a list which may be null, or contain one or more strings, symbols, booleans, or numbers.

Defaults are printed to the terminal using mktkgui -defaults. Configured option values are printed by invoking mktkgui -settings.

The option 'static-egg-libs' determines how extension (egg) libs are linked: as static or shared objects. By default 'static-egg-libs' is #f, that is, shared linking is the initial setting. Inserting (static-egg-libs . #t) in a local config file enables linking statically. Alternatively, the mktkgui command line -static option can be used to activate static egg library linking without changing the default. (-static merely inverts 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 in the egg's .egg file. Shared linking works with unmodified eggs and those compiled with -fPIC.

Example config:

 ((cc  /usr/local/bin/gcc)
  (csc  /usr/local/bin/csc)
  (includes  -I. -I/usr/local/include/chicken -I/usr/local/include)
  (csclinkdir  /usr/local/lib/chicken/12)
  (imports  myegg my-other-egg)
  ...
  (static-egg-libs  #t)
  (config-check  =gui-prog/gui-config.scm=))

This config sets static egg linking to true by default. With this config using -static on the mktkgui command line turns off static linking. Using -static '#f' guarantees the option is off. With -static '#t' the option is always on regardless of config setting. (The same is true for -gui and -verbose options.)

Builtin options

Builtins are overridden by options in per-directory config file(s).

Unix defaults

cc                /usr/bin/gcc
cflags            -Og -g -fPIC
config-check      "unix defaults"
csc               /usr/local/bin/csc
csc-cflags        -C -g -C -fPIC
csclinkdir        "."
defs              -DUSE_TCL_STUBS -DUSE_TK_STUBS
dll               .so
dllflag           -shared
exe               
exedir            /usr/local/bin
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
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 defaults

cc                c:/w64devkit/bin/gcc.exe
cflags            -Og -g
config-check      "windows defaults"
csc               c:/usr/local/bin/csc.exe
csc-cflags   
csclinkdir        "."
defs              -DUSE_TCL_STUBS -DUSE_TK_STUBS
dll               .dll
dllflag           -mdll
exe               .exe
exedir            c:/usr/local/bin
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         
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 include resources like icons, cursors and metadata in 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. Use the Tk icon in source distribution as a template, replace the default Tk icon with the 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 names like TK_LIB_FLAG -ltcl9tk9.1x. Put the custom tk.ico in the rc directory. Compiling as normal should give 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