source: branches/blt4/gui/scripts/imageentry.tcl @ 1932

Last change on this file since 1932 was 1932, checked in by gah, 14 years ago
File size: 14.3 KB
Line 
1
2# ----------------------------------------------------------------------
3#  COMPONENT: ImageEntry - widget for displaying images
4#
5#  This widget represents an <image> entry on a control panel.
6#  It displays images which help explain the input.
7# ======================================================================
8#  AUTHOR:  Michael McLennan, Purdue University
9#  Copyright (c) 2004-2005  Purdue Research Foundation
10#
11#  See the file "license.terms" for information on usage and
12#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13# ======================================================================
14package require Itk
15#package require Img
16
17itcl::class Rappture::ImageEntry {
18    inherit itk::Widget
19
20    itk_option define -state state State "normal"
21
22    constructor {owner path args} { # defined below }
23    destructor { # defined below }
24
25    public method value {args}
26
27    public method label {}
28    public method tooltip {}
29
30    protected method _redraw {}
31    protected method _outline {imh color}
32    protected method _uploadValue {args}
33    protected method _downloadValue {}
34
35    private variable _owner ""    ;# thing managing this control
36    private variable _path ""     ;# path in XML to this image
37    private variable _data ""     ;# current image data
38    private variable _imh ""      ;# image handle for current value
39    private variable _resize ""   ;# image for resize operations
40
41    private common _thumbsize 100 ;# std size for thumbnail images
42}
43
44itk::usual ImageEntry {
45    keep -cursor -font
46    keep -foreground -background
47    keep -textbackground
48    keep -selectbackground -selectforeground -selectborderwidth
49}
50
51# ----------------------------------------------------------------------
52# CONSTRUCTOR
53# ----------------------------------------------------------------------
54itcl::body Rappture::ImageEntry::constructor {owner path args} {
55    if {[catch {$owner isa Rappture::ControlOwner} valid] != 0 || !$valid} {
56        error "bad object \"$owner\": should be Rappture::ControlOwner"
57    }
58    set _owner $owner
59    set _path $path
60    set _resize [image create picture]
61
62    #
63    # Create the widget and configure it properly based on other
64    # hints in the XML.  Two ways to display:  Old apps use images
65    # without labels as decorations.  In that case, show the image
66    # alone, probably full size.  Newer apps use images as inputs.
67    # In that case, show a thumbnail of the image with some extra
68    # facts about image type, file size, etc.
69    #
70    itk_component add image {
71        ::label $itk_interior.image -borderwidth 0
72    }
73
74    itk_component add info {
75        ::label $itk_interior.info -borderwidth 0 -width 5 \
76            -anchor w -justify left
77    }
78
79    itk_component add rmenu {
80        menu $itk_interior.menu -tearoff 0
81    } {
82        usual
83        ignore -tearoff
84    }
85    $itk_component(rmenu) add command \
86        -label [Rappture::filexfer::label upload] \
87        -command [itcl::code $this _uploadValue -start]
88    $itk_component(rmenu) add command \
89        -label [Rappture::filexfer::label download] \
90        -command [itcl::code $this _downloadValue]
91
92
93    if {[string length [label]] == 0} {
94        # old mode -- big image
95        pack $itk_component(image) -expand yes -fill both
96        bind $itk_component(image) <Configure> [itcl::code $this _redraw]
97    } else {
98        # new mode -- thumbnail and details
99        pack $itk_component(image) -side left
100        pack $itk_component(info) -side left -expand yes -fill both -padx 4
101
102        bind $itk_component(image) <<PopupMenu>> \
103            [list tk_popup $itk_component(rmenu) %X %Y]
104        bind $itk_component(info) <<PopupMenu>> \
105            [list tk_popup $itk_component(rmenu) %X %Y]
106
107        _redraw  ;# draw Empty image/info
108    }
109
110    set str [$_owner xml get $path.current]
111    if {[string length $str] == 0} {
112        set str [$_owner xml get $path.default]
113    }
114    if {[string length $str] > 0} {
115        value $str
116    }
117
118    eval itk_initialize $args
119}
120
121# ----------------------------------------------------------------------
122# DESTRUCTOR
123# ----------------------------------------------------------------------
124itcl::body Rappture::ImageEntry::destructor {} {
125    if {"" != $_imh} { image delete $_imh }
126    if {"" != $_resize} { image delete $_resize }
127}
128
129# ----------------------------------------------------------------------
130# USAGE: value ?-check? ?<newval>?
131#
132# Clients use this to query/set the value for this widget.  With
133# no args, it returns the current value for the widget.  If the
134# <newval> is specified, it sets the value of the widget and
135# sends a <<Value>> event.  If the -check flag is included, the
136# new value is not actually applied, but just checked for correctness.
137# ----------------------------------------------------------------------
138itcl::body Rappture::ImageEntry::value {args} {
139    set onlycheck 0
140    set i [lsearch -exact $args -check]
141    if {$i >= 0} {
142        set onlycheck 1
143        set args [lreplace $args $i $i]
144    }
145
146    if {[llength $args] == 1} {
147        if {$onlycheck} {
148            # someday we may add validation...
149            return
150        }
151        set newval [lindex $args 0]
152        if {[string length $newval] > 0} {
153            set imh [image create picture -data $newval]
154        } else {
155            set imh ""
156        }
157
158        if {$_imh != ""} {
159            image delete $_imh
160        }
161        set _imh $imh
162        set _data $newval
163
164        _redraw
165
166        return $newval
167
168    } elseif {[llength $args] != 0} {
169        error "wrong # args: should be \"value ?-check? ?newval?\""
170    }
171
172    #
173    # Query the value and return.
174    #
175    set bytes $_data
176    set fmt [$_owner xml get $_path.convert]
177    if {"" != $fmt && "" != $_imh} {
178        switch -- $fmt {
179            "pgm" - "ppm" {
180                $_imh export pbm -data bytes
181            }
182            "jpeg" {
183                $_imh export jpg -data bytes
184            }
185            "tiff" {
186                $_imh export tif -data bytes
187            }
188            "bmp" - "png" - "xbm" - "xpm" {
189                $_imh export $fmt -data bytes
190            }
191            default {
192                error "unknown image format \"$fmt\""
193            }
194        }
195    }
196    return $bytes
197}
198
199# ----------------------------------------------------------------------
200# USAGE: label
201#
202# Clients use this to query the label associated with this widget.
203# Reaches into the XML and pulls out the appropriate label string.
204# ----------------------------------------------------------------------
205itcl::body Rappture::ImageEntry::label {} {
206    set label [$_owner xml get $_path.about.label]
207    return [string trim $label]
208}
209
210# ----------------------------------------------------------------------
211# USAGE: tooltip
212#
213# Clients use this to query the tooltip associated with this widget.
214# Reaches into the XML and pulls out the appropriate description
215# string.  Returns the string that should be used with the
216# Rappture::Tooltip facility.
217# ----------------------------------------------------------------------
218itcl::body Rappture::ImageEntry::tooltip {} {
219    set str [$_owner xml get $_path.about.description]
220    return [string trim $str]
221}
222
223# ----------------------------------------------------------------------
224# USAGE: _redraw
225#
226# Used internally to update the image displayed in this entry.
227# If the <resize> parameter is set, then the image is resized.
228# The "auto" value resizes to the current area.  The "width=XX" or
229# "height=xx" form resizes to a particular size.  The "none" value
230# leaves the image as-is; this is the default.
231# ----------------------------------------------------------------------
232itcl::body Rappture::ImageEntry::_redraw {} {
233    if {"" == $_imh} {
234        # generate a big diagonal cross-hatch image
235        set diag [Rappture::icon diag]
236        set dw [image width $diag]
237        set dh [image height $diag]
238        $_resize configure -width $_thumbsize -height $_thumbsize
239        for {set i 0} {$i < $_thumbsize/$dw+1} {incr i} {
240            for {set j 0} {$j < $_thumbsize/$dh+1} {incr j} {
241                set x [expr {$i*$dw}]
242                set y [expr {$j*$dh}]
243                $_resize copy $diag -to "$x $y"
244            }
245        }
246        _outline $_resize black
247        $itk_component(image) configure -image $_resize
248        $itk_component(info) configure -text "Empty"
249        return
250    }
251
252    set iw [image width $_imh]
253    set ih [image height $_imh]
254    $itk_component(image) configure -image "" -width $iw -height $ih
255
256    #
257    # Build a description of the image if the info is showing.
258    #
259    set desc ""
260    if {[string length [label]] != 0} {
261        # if data is base64-encoded, try to decode it
262        if {![regexp {^[a-zA-Z0-9+/=]+(\n[a-zA-Z0-9+/=]+)*$} $_data]
263              || [catch {Rappture::encoding::decode -as b64 $_data} bytes]} {
264            # oops! not base64 -- use data directly
265            set bytes $_data
266        }
267        set desc [Rappture::utils::datatype $bytes]
268        if {[string equal $desc "Binary data"]} {
269            # generic description -- we can do a little better
270            set iw [image width $_imh]
271            set ih [image height $_imh]
272            set desc "Image, ${iw} x ${ih}"
273        }
274        append desc "\n[Rappture::utils::binsize [string length $_data]]"
275    }
276    $itk_component(info) configure -text $desc
277
278    #
279    # Put up the preview image, resizing if necessary.
280    #
281    set str [string trim [$_owner xml get $_path.resize]]
282    if {"" == $str} {
283        set str "none"
284    }
285    switch -glob -- $str {
286        width=* - height=* {
287            if {[regexp {^width=([0-9]+)$} $str match size]} {
288                set w $size
289                set h [expr {round($w*$ih/double($iw))}]
290                $_resize configure -width $w -height $h
291                $_resize blank
292                $_resize resample $_imh
293                _outline $_resize black
294                $itk_component(image) configure -image $_resize \
295                    -width $w -height $h
296            } elseif {[regexp {^height=([0-9]+)$} $str match size]} {
297                set h $size
298                set w [expr {round($h*$iw/double($ih))}]
299                $_resize configure -width $w -height $h
300                $_resize blank
301                $_resize resample $_imh
302                _outline $_resize black
303                $itk_component(image) configure -image $_resize \
304                    -width $w -height $h
305            } else {
306                $itk_component(image) configure -image $_imh
307            }
308        }
309        auto - none - default {
310            if {[string length [label]] == 0} {
311                # old mode -- big image with no label
312                $itk_component(image) configure -image $_imh
313            } else {
314                # new mode -- thumbnail and image info
315                set w $_thumbsize
316                set h $_thumbsize
317                $itk_component(image) configure -width $w -height $h
318
319                if {$iw <= $_thumbsize && $ih <= $_thumbsize} {
320                    $_resize configure -width $iw -height $ih
321                    $_resize copy $_imh
322                    _outline $_resize black
323                } else {
324                    # large image -- scale it down
325                    if {$iw > $ih} {
326                        set h [expr {round($w/double($iw)*$ih)}]
327                    } else {
328                        set w [expr {round($h/double($ih)*$iw)}]
329                    }
330                    $_resize configure -width $w -height $h
331                    $_resize blank
332                    $_resize resample $_imh
333                    _outline $_resize black
334                }
335                $itk_component(image) configure -image $_resize
336            }
337        }
338    }
339}
340
341# ----------------------------------------------------------------------
342# USAGE: _outline <image> <color>
343#
344# Used internally to outline the given <image> with a single-pixel
345# line of the specified <color>.  Updates the image in place.
346# ----------------------------------------------------------------------
347itcl::body Rappture::ImageEntry::_outline {im color} {
348    if {"" != $im} {
349        set w [image width $im]
350        set h [image height $im]
351        $im put $color -to 0 0 $w 1
352        $im put $color -to 0 0 1 $h
353        $im put $color -to 0 [expr {$h-1}] $w $h
354        $im put $color -to [expr {$w-1}] 0 $w $h
355    }
356}
357
358# ----------------------------------------------------------------------
359# USAGE: _uploadValue -start
360# USAGE: _uploadValue -assign <key> <value> <key> <value> ...
361#
362# Used internally to initiate an upload operation.  Prompts the
363# user to upload into the image area of this widget.
364# ----------------------------------------------------------------------
365itcl::body Rappture::ImageEntry::_uploadValue {args} {
366    set opt [lindex $args 0]
367    switch -- $opt {
368        -start {
369            set tool [Rappture::Tool::resources -appname]
370            set cntls [list $_path [label] [tooltip]]
371            Rappture::filexfer::upload \
372                $tool $cntls [itcl::code $this _uploadValue -assign]
373        }
374        -assign {
375            array set data [lrange $args 1 end] ;# skip option
376            if {[info exists data(error)]} {
377                Rappture::Tooltip::cue $itk_component(image) $data(error)
378            }
379            if {[info exists data(data)]} {
380                Rappture::Tooltip::cue hide  ;# take down note about the popup
381                if {[catch {value $data(data)} err]} {
382                    Rappture::Tooltip::cue $itk_component(image) "Upload failed:\n$err"
383                }
384            }
385        }
386        default {
387            error "bad option \"$opt\": should be -start or -assign"
388        }
389    }
390}
391
392# ----------------------------------------------------------------------
393# USAGE: _downloadValue
394#
395# Used internally to initiate a download operation.  Takes the current
396# value and downloads it to the user in a new browser window.
397# ----------------------------------------------------------------------
398itcl::body Rappture::ImageEntry::_downloadValue {} {
399    set bytes [$_imh export png]
400    set mesg [Rappture::filexfer::download $bytes image.png]
401
402    if {"" != $mesg} {
403        Rappture::Tooltip::cue $itk_component(image) $mesg
404    }
405}
406
407# ----------------------------------------------------------------------
408# CONFIGURATION OPTION: -state
409# ----------------------------------------------------------------------
410itcl::configbody Rappture::ImageEntry::state {
411    set valid {normal disabled}
412    if {[lsearch -exact $valid $itk_option(-state)] < 0} {
413        error "bad value \"$itk_option(-state)\": should be [join $valid {, }]"
414    }
415    $itk_component(image) configure -state $itk_option(-state)
416}
Note: See TracBrowser for help on using the repository browser.