Updated 2015-02-17 04:12:48 by APN

Purpose: to collect tips, suggestions, questions, and examples where developers have written tcl/tk code that wraps around an existing GUI window.

In particular, I'm interested in hearing people talk about doing this under the X Window System.

Has anyone an example, for instance, of embedding perhaps an xterm inside a tcl/tk frame?

AMG: Try this.
exec xterm -into [scan [winfo id .] %x] &

But Tk has no knowledge of the xterm stowing away in its toplevel, and the xterm doesn't seem to know much about its parent window, so there's no geometry propagation whatsoever. Hey, what do you expect from one line of code!? ;^)

tonytraductor I've noted that, using the above technique, closing the parent windows makes the xterm disappear, but does not kill the xterm process, which must then be killed manually (using a sysmon or with a new terminal on the command line), unless, of course, one includes something more like
set ::pid [exec xterm -into ...]
exec kill -9 $::pid; exit

I'm interested in learning how to embed other applications, such as an mplayer video screen. Mplayer -into does not work.

sg man mplayer. Seek for a keyword... Okay, embed, for example. Aha, there is -wid option in MP's options set. I'm trying a line...
exec /usr/bin/mplayer -wid [winfo id .f] /usr/share/example-content/Experience\ ubuntu.ogg &

LV From a recent discussion on comp.lang.tcl regarding a request for code so that an application could let the user use a personal preference of editor.
package require Tk
frame .f -container yes
pack .f -fill both -expand yes
# Solaris users should note that neither /usr/openwin/bin/xterm or
#   /opt/sfw/bin/xterm apparently are new enough to have the -into.
#   so you are resigned to do without or to get a newer version of xterm
exec xterm -into [ expr [winfo id .f ]] -e $env(EDITOR) & 

See BLT's 'container' command. From the man page...

The container widget lets you embed an X11 window from a foreign application into your Tk application. The foreign window is reparented inside of the widget. You can then place and arrange the container just as you would any Tk widget.

- Marty Backe -- 24 Nov 2004

See the page about the TkXext extension at http://wiki.tcl.tk/2116 It's a compiled Tcl/Tk extension. I think it only works on X11 on Unix/Linux. But you can embed non-Tcl/Tk windows inside a Tk frame with it. I used it in a project on Slackware 7.2 Linux with Tcl/Tk 8.4 and it worked well.

LV Has anyone used the frame -container option to do this sort of thing?

Googie Pretty easy example is to embed mplayer into Tk frame or toplevel window. Mplayer has -wid option, which orders mplayer to embed in X11 window with given ident. We can resolve ident of our toplevel or frame by wm.

Rildo Pragana - For a multimedia window, you may even crop and pan its shown media, with Tk'k place geometry manager, see Crop multimedia with tk's place.

PT 16-Dec-2009: Here is a more concrete example of embedding mplayer into a Tk window to display a webcam video source. This one is tied to the first video source and 640x480 but the window can be resized fine. Mplayer has a -slave option which tells the program to read commands from standard input. Using this we can tell it when to terminate nicely and have it take screenshots on demand.
# showcam.tcl - Copyright (C) 2009 Pat Thoyts <[email protected]>
#
#        Demonstration of mplayer embedding into a Tk application.
#
# mplayer can be embedded in a Tk window if it is passed the window id
# of a frame. We can then control it via its slave mode over standard
# input and output. There are a number of possible commands but here
# we use 'screenshot' to have it save a snapshot to a unique filename
# and reply on the pipe with the filename. We can read the filename
# and delete the file to suck captures into Tk images.
#
package require Tk
variable uid     ; if {![info exists uid]} {set uid 0}
variable mplayer ; if {![info exists mplayer]} {set mplayer ""}

proc Screenshot {} {
    variable mplayer
    if {$mplayer ne ""} { puts $mplayer "screenshot 0" }
}

proc OnScreenshot {filename} {
    variable uid
    set image [image create photo -file $filename]
    file delete $filename
    set dlg [toplevel .dlg_$image -class Dialog]
    wm title $dlg "Screenshot #[incr uid]"
    wm transient $dlg .
    pack [label $dlg.image -image $image] -fill both
}

# Read lines from mplayer stdout
proc ReadPipe {chan} {
    set d [read $chan]
    foreach line [split $d \n] {
        set line [string trim $line]
        if {[regexp {^\*\*\* screenshot '(.*?)' \*\*\*} $line -> filename]} {
            # delay to permit the file to appear
            after 250 [list OnScreenshot $filename]
        }
    }
    if {[eof $chan]} {
        fileevent $chan readable {}
        close $chan
        variable mplayer ""
    }
}

proc Embed {w {device /dev/video0}} {
    set r [catch {
        set height 640
        set width 480
        set cmd mplayer
        lappend cmd tv:// -quiet -slave -idle \
            -tv driver=v4l2:width=$width:height=$height:device=$device \
            -vf screenshot -fps 15 -wid [winfo id $w]
        set pipe [open |$cmd r+]
        fconfigure $pipe -blocking 0 -buffering line
        fileevent $pipe readable [list ReadPipe $pipe]
        variable mplayer $pipe
    } err]

    if {$r} {
        tk_messageBox -icon error -title "Show camera error" \
            -message $err
    }
}

proc Exit {} {
    variable mplayer
    if {[info exists mplayer] && $mplayer ne ""} {
        catch {puts $mplayer quit}
        return [after idle [list after 10 [list Exit]]]
    }
    set ::forever 1
}

proc Main {} {
    wm title . "Show camera"
    wm protocol . WM_DELETE_WINDOW [list Exit]
    wm withdraw .

    option add *tearOff false
    . configure -menu [menu .menu]
    .menu add cascade -label File -menu [menu .menu.file]
    .menu.file add command -label Screenshot -command Screenshot
    .menu.file add command -label Exit -command Exit

    frame .video -width 640 -height 480 

    grid .video  - -sticky news
    grid rowconfigure . 0 -weight 1
    grid columnconfigure . 0 -weight 1

    # add the video once the window exists and is mapped
    bind .video <Map> {
        bind %W <Map> {}
        Embed %W
    }

    wm deiconify .
}

if {!$tcl_interactive} {
    if {![info exists initialized]} {
        set initialized 1
        set r [catch [linsert $argv 0 Main] err]
        vwait ::forever   
        if {$r} {
            tk_messageBox -icon error -title "Show camera" \
                -message $err
        }
        exit $r
    }
}

uniquename 2014jul22

To help people realize that the above code 'works out of the box', I present the image above.

All I had to do was add the line
   #!/usr/bin/wish

to the top of the code above and it executed --- on my Ubuntu 9.10 Linux system.

The code discovered my webcam at '/dev/video0' within a few seconds and replaced an initial green (?) background in the 640x480 window frame by a moving image of my aged mug.

The window is even resizable. I grabbed a window corner and shrunk the window down. The image in the window automatically resized without preserving aspect ratio. Thus the small, squat image above. (That's me in a dark room.)

The 'File' menu provides 2 options: 'Screenshot' and 'Exit'.

Even the 'Screenshot' option worked for me. Each click created files shot0001.png, shot0002.png, etc. Each image was the full frame size (640x480), in spite of the squashed window.

The only thing that did not work for me was the 'OnScreenshot' proc, which apparently tries to show each '.png' in a popup window. The proc failed with the message
   couldn't recognize data in image file "shot0001.png"

at the 'image create photo' command.

This probably happened because I am running Tcl-Tk 8.5, which does not support the built-in PNG capabilities that are supported by the 8.6 'wish' interpreter.

Nice work, PT.

Also see Reparenting native applications on Windows