ActiveBlog

Bundling images and libraries with TclApp
by Kevin "kj" Woolley

Kevin "kj" Woolley, November 29, 2005

Something I get asked fairly frequently is how to bundle images and dependent libraries with TclApp, such that the whole application can be distributed as a single .exe on Windows platforms. It's a great question -- it's easy to turn...

Something I get asked fairly frequently is how to bundle images and dependent libraries with TclApp, such that the whole application can be distributed as a single .exe on Windows platforms.  It's a great question -- it's easy to turn off users with complex installers or applications that bomb because they can't find "splash.gif".

Fortunately, it's not too difficult at all if you use TclApp, as distributed with the Tcl Development Kit.  If you don't have it already, grab an evaluation copy and follow along.  It'll take ten or fifteen minutes.

Suppose we wanted to make a simple application that displays a pretty picture and generates a UUID (Universally Unique IDentifier).  UUIDs aren't terribly useful to humans, but webservers and other programs use them fairly frequently.

First, let's grab a pretty picture.  It should be a reasonably standard picture format -- GIF, JPEG, PNG, TIFF, PostScript... Any of these will do.  For a full list of supported formats, see the documentation on the Img package for Tk.  I'll use a GIF in this example, because it's what I have sitting around.  For the purposes of this demo, we'll call the image file mylogo.gif.

The second step is to write our Tcl/Tk source file.  I've attached the version I'm using, and we'll take it apart line by line.

The first few lines are fairly standard boilerplate:

package require Tk
package require Tcl
package require uuid

These bring in the Tk, Tcl, and uuid libraries respectively.  uuid is part of Tcllib, which ships as part of ActiveTcl.  If you're not using ActiveTcl, you can get Tcllib from their Sourceforge project page.

proc MyUUID {} {
    return [::uuid::uuid generate]
}

Here we define a procedure that returns a UUID.  This code does not have to go in a procedure, of course -- it could be written in-line.

proc Main {} {
    image create photo foo -file [file join [file dirname [info script]] mylogo.gif]
    label .l1 -image foo -bd 1 -relief sunken
    pack .l1 -side top

Next, we define the Main procedure.  This isn't strictly necessary to do, but I define a Main procedure so nothing executes without it being called specifically.  I find it handy for debugging purposes, but your mileage may vary.

First up in Main is to load the image.  We'll call the image foo, for lack of a better name.  Notice the file specification -- there is a bit of a song and dance going on there that needs a little explanation.  The easiest way to read it is from the inside out.  [info script] returns the full pathname of the script.  [file dirname] returns the directory name of its argument, and [file join] composes a pathname from its arguments, inserting the appropriate path separator.

So why all the song and dance?  When you're packing an image inside your executable, it's not always obvious as to how to find it.  One thing you can usually rely on is for [info script] to return the proper path to the script that's running, and from there you can simply chop off the script name and add on the image name, as we have done above.  This works for any kind of file.

The label command puts the image on a label, which can be packed into a window.  In this case, we're using the toplevel window.  Next, we pack the label, so it is visible.

    label .l2 -text [MyUUID]
    pack .l2 -side bottom
    label .l3 -text [file dirname [info script]]
    pack .l3 -side bottom
}

Main

Now it's time to add the UUID.  Because MyUUID returns the text of the UUID, we can use the -text option to add it directly to the label.

For a finishing touch, we add the directory name as it is seen by the script, executing inside the wrapper.

Finally, we call Main -- and that's the end of the Tcl file.

The next step is to use TclApp to create the executable.  Open TclApp, and you will be presented with a screen that looks like this:

Click the button with the document on it, and select the files you would like to wrap.  In this case, we'll just wrap the Tcl file and the image.  Next, we choose the packages we would like to wrap in the package.  Click on the button with the yellow box, and select tcllib-1.8.  You will see an entry for the package appear.  Now hit the "Scan" button, and it should find the rest of the packages you need for you.

To make TclApp generate an executable file, we need to set the prefix file.  Because we're using Tk, we'll choose base-tk-thread-win32-ix86.exe.

Don't forget to set your output file, as it's no fun to go hunting for your new application.

Last, we click over to the "Run" tab, and click the "Wrap" button.  This wraps our application and shows any problems that might occur:

Now you can run your application.  Try renaming your image file -- the application still works, as it's using the wrapped image.  You can even install your application on another machine that doesn't have ActiveTcl or the TDK installed, and there is no problem.

Of course this is a fairly trivial example.  Things can get hairy when you're wrapping larger, more complex applications.  If you run into any problems, try signing onto the TDK mailing list and asking around.  There are great people there.

Subscribe to ActiveState Blogs by Email

Share this post:

About the Author: RSS

kj is ActiveState’s Systems Administrator. He was born in London, Ontario, grew up in North Vancouver, and spent several years in the Northwest Territories doing technical sales. As a technical infrastructure specialist, he brings over twenty years of systems administration experience to ActiveState.