source: trunk/gui/scripts/imageresult.tcl @ 766

Last change on this file since 766 was 766, checked in by mmc, 17 years ago

Fixed the output viewer for numbers/integers to show a plot of
the value versus input parameters. As you change the ResultSet?
control, the x-axis updates to show the number versus values
in the result set.

Fixed the Rappture::result command to include the user's login
in the metadata, so we know who performed the computation.

File size: 17.1 KB
Line 
1# ----------------------------------------------------------------------
2#  COMPONENT: imageresult - picture image in a ResultSet
3#
4#  This widget displays an image found in the output of a Rappture
5#  tool run.  Use the "add" and "delete" methods to control the images
6#  showing in the widget.
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
15package require BLT
16package require Img
17
18option add *ImageResult.width 3i widgetDefault
19option add *ImageResult.height 3i widgetDefault
20option add *ImageResult.controlBackground gray widgetDefault
21option add *ImageResult.font \
22    -*-helvetica-medium-r-normal-*-12-* widgetDefault
23
24itcl::class Rappture::ImageResult {
25    inherit itk::Widget
26
27    constructor {args} { # defined below }
28    destructor { # defined below }
29
30    public method add {image {settings ""}}
31    public method get {}
32    public method delete {args}
33    public method scale {args}
34    public method parameters {title args} { # do nothing }
35    public method download {option args}
36
37    protected method _rebuild {args}
38    protected method _topimage {}
39    protected method _zoom {option args}
40    protected method _move {option args}
41
42    private variable _dispatcher "" ;# dispatcher for !events
43    private variable _dlist ""      ;# list of data objects
44    private variable _topmost ""    ;# topmost image in _dlist
45    private variable _max           ;# max size of all images
46    private variable _scale         ;# info related to zoom
47    private variable _image         ;# image buffers used for scaling
48}
49                                                                               
50itk::usual ImageResult {
51    keep -background -foreground -cursor -font
52}
53
54# ----------------------------------------------------------------------
55# CONSTRUCTOR
56# ----------------------------------------------------------------------
57itcl::body Rappture::ImageResult::constructor {args} {
58    Rappture::dispatcher _dispatcher
59    $_dispatcher register !rebuild
60    $_dispatcher dispatch $this !rebuild [itcl::code $this _rebuild]
61
62    array set _scale {
63        max 1.0
64        current 1.0
65        x 0
66        y 0
67    }
68
69    option add hull.width hull.height
70    pack propagate $itk_component(hull) no
71
72    itk_component add controls {
73        frame $itk_interior.cntls
74    } {
75        usual
76        rename -background -controlbackground controlBackground Background
77    }
78    pack $itk_component(controls) -side right -fill y
79
80    itk_component add reset {
81        button $itk_component(controls).reset \
82            -borderwidth 1 -padx 1 -pady 1 \
83            -bitmap [Rappture::icon reset] \
84            -command [itcl::code $this _zoom reset]
85    } {
86        usual
87        ignore -borderwidth
88        rename -highlightbackground -controlbackground controlBackground Background
89    }
90    pack $itk_component(reset) -padx 4 -pady 4
91    Rappture::Tooltip::for $itk_component(reset) "Reset the view to the default zoom level"
92
93    itk_component add zoomin {
94        button $itk_component(controls).zin \
95            -borderwidth 1 -padx 1 -pady 1 \
96            -bitmap [Rappture::icon zoomin] \
97            -command [itcl::code $this _zoom in]
98    } {
99        usual
100        ignore -borderwidth
101        rename -highlightbackground -controlbackground controlBackground Background
102    }
103    pack $itk_component(zoomin) -padx 4 -pady 4
104    Rappture::Tooltip::for $itk_component(zoomin) "Zoom in"
105
106    itk_component add zoomout {
107        button $itk_component(controls).zout \
108            -borderwidth 1 -padx 1 -pady 1 \
109            -bitmap [Rappture::icon zoomout] \
110            -command [itcl::code $this _zoom out]
111    } {
112        usual
113        ignore -borderwidth
114        rename -highlightbackground -controlbackground controlBackground Background
115    }
116    pack $itk_component(zoomout) -padx 4 -pady 4
117    Rappture::Tooltip::for $itk_component(zoomout) "Zoom out"
118
119
120    set _image(zoom) [image create photo]
121    set _image(final) [image create photo]
122
123    itk_component add image {
124        label $itk_interior.image -image $_image(final)
125    } {
126        keep -background -foreground -cursor -font
127    }
128    pack $itk_component(image) -expand yes -fill both
129
130    #
131    # Add bindings for resize/move
132    #
133    bind $itk_component(image) <Configure> \
134        [list $_dispatcher event -idle !rebuild resize 1]
135
136    bind $itk_component(image) <ButtonPress-1> \
137        [itcl::code $this _move click %x %y]
138    bind $itk_component(image) <B1-Motion> \
139        [itcl::code $this _move drag %x %y]
140    bind $itk_component(image) <ButtonRelease-1> \
141        [itcl::code $this _move release %x %y]
142
143    eval itk_initialize $args
144}
145
146# ----------------------------------------------------------------------
147# DESTRUCTOR
148# ----------------------------------------------------------------------
149itcl::body Rappture::ImageResult::destructor {} {
150    foreach name [array names _image] {
151        image delete $_image($name)
152    }
153}
154
155# ----------------------------------------------------------------------
156# USAGE: add <image> ?<settings>?
157#
158# Clients use this to add an image to the plot.  The optional <settings>
159# are used to configure the image.  Allowed settings are -color,
160# -brightness, -width, -linestyle and -raise.
161# ----------------------------------------------------------------------
162itcl::body Rappture::ImageResult::add {image {settings ""}} {
163    array set params {
164        -color auto
165        -brightness 0
166        -width 1
167        -raise 0
168        -linestyle solid
169        -description ""
170    }
171    foreach {opt val} $settings {
172        if {![info exists params($opt)]} {
173            error "bad setting \"$opt\": should be [join [lsort [array names params]] {, }]"
174        }
175        set params($opt) $val
176    }
177
178    if {$params(-raise)} {
179        set _topmost $image
180        $_dispatcher event -idle !rebuild
181    }
182
183    set pos [lsearch -exact $image $_dlist]
184    if {$pos < 0} {
185        lappend _dlist $image
186        $_dispatcher event -idle !rebuild
187    }
188}
189
190# ----------------------------------------------------------------------
191# USAGE: get
192#
193# Clients use this to query the list of images being displayed, in
194# order from bottom to top of this result.
195# ----------------------------------------------------------------------
196itcl::body Rappture::ImageResult::get {} {
197    # put the dataobj list in order according to -raise options
198    set dlist $_dlist
199
200    set i [lsearch $_dlist $_topmost]
201    if {$i >= 0} {
202        set dlist [lreplace $dlist $i $i]
203        set dlist [linsert $dlist 0 $_topmost]
204    }
205    return $dlist
206}
207
208# ----------------------------------------------------------------------
209# USAGE: delete ?<image1> <image2> ...?
210#
211# Clients use this to delete an image from the plot.  If no images
212# are specified, then all images are deleted.
213# ----------------------------------------------------------------------
214itcl::body Rappture::ImageResult::delete {args} {
215    if {[llength $args] == 0} {
216        set args $_dlist
217    }
218
219    # delete all specified curves
220    set changed 0
221    foreach image $args {
222        set pos [lsearch -exact $_dlist $image]
223        if {$pos >= 0} {
224            set _dlist [lreplace $_dlist $pos $pos]
225            set changed 1
226
227            if {$image == $_topmost} {
228                set _topmost ""
229            }
230        }
231    }
232
233    # if anything changed, then rebuild the plot
234    if {$changed} {
235        $_dispatcher event -idle !rebuild
236    }
237}
238
239# ----------------------------------------------------------------------
240# USAGE: scale ?<image1> <image2> ...?
241#
242# Sets the default limits for the overall plot according to the
243# limits of the data for all of the given <image> objects.  This
244# accounts for all images--even those not showing on the screen.
245# Because of this, the limits are appropriate for all images as
246# the user scans through data in the ResultSet viewer.
247# ----------------------------------------------------------------------
248itcl::body Rappture::ImageResult::scale {args} {
249    set _max(w) 0
250    set _max(h) 0
251    foreach image $args {
252        set imh [$image tkimage]
253
254        set w [image width $imh]
255        if {$w > $_max(w)} { set _max(w) $w }
256
257        set h [image height $imh]
258        if {$h > $_max(h)} { set _max(h) $h }
259    }
260
261    # scale is unknown for now... scale later at next _rebuild
262    set _scale(max) "?"
263    set _scale(current) "?"
264
265    $_dispatcher event -idle !rebuild
266}
267
268# ----------------------------------------------------------------------
269# USAGE: download coming
270# USAGE: download controls <downloadCommand>
271# USAGE: download now
272#
273# Clients use this method to create a downloadable representation
274# of the plot.  Returns a list of the form {ext string}, where
275# "ext" is the file extension (indicating the type of data) and
276# "string" is the data itself.
277# ----------------------------------------------------------------------
278itcl::body Rappture::ImageResult::download {option args} {
279    switch $option {
280        coming {
281            # nothing to do
282        }
283        controls {
284            # no controls for this download yet
285            return ""
286        }
287        now {
288            set top [_topimage]
289            if {$top == ""} {
290                return ""
291            }
292
293            #
294            # Hack alert!  Need data in binary format,
295            # so we'll save to a file and read it back.
296            #
297            set tmpfile /tmp/image[pid].jpg
298            $top write $tmpfile -format jpeg
299            set fid [open $tmpfile r]
300            fconfigure $fid -encoding binary -translation binary
301            set bytes [read $fid]
302            close $fid
303            file delete -force $tmpfile
304
305            return [list .jpg $bytes]
306        }
307        default {
308            error "bad option \"$option\": should be coming, controls, now"
309        }
310    }
311}
312
313# ----------------------------------------------------------------------
314# USAGE: _rebuild ?<eventData>...?
315#
316# Called automatically whenever something changes that affects the
317# data in the widget.  Clears any existing data and rebuilds the
318# widget to display new data.
319# ----------------------------------------------------------------------
320itcl::body Rappture::ImageResult::_rebuild {args} {
321    array set event $args
322    if {[info exists event(resize)] && $event(resize)} {
323        # window changed size -- recompute max scale below
324        set _scale(max) "?"
325    }
326
327    if {$_scale(max) == "?"} {
328        if {![_zoom rescale]} {
329            return
330        }
331    }
332    if {$_scale(current) == "?"} {
333        _zoom reset
334    }
335
336    set w [winfo width $itk_component(image)]
337    set h [winfo height $itk_component(image)]
338    $_image(final) configure -width $w -height $h
339    set bg [$itk_component(image) cget -background]
340    set rgb [winfo rgb . $bg]
341    set bg [format "#%03x%03x%03x" [lindex $rgb 0] [lindex $rgb 1] [lindex $rgb 2]]
342    $_image(final) put $bg -to 0 0 $w $h
343
344    set imh [_topimage]
345    if {$imh != ""} {
346        if {$_scale(current) <= 1.0} {
347            set wz [expr {round($_scale(current)*$w)}]
348            set hz [expr {round($_scale(current)*$h)}]
349            if {$wz > 1 && $hz > 1} {
350                $_image(zoom) configure -width $wz -height $hz
351                $_image(zoom) put $bg -to 0 0 $wz $hz
352                set sx [expr {round($_scale(x)*$_scale(current))}]
353                set sy [expr {round($_scale(y)*$_scale(current))}]
354                $_image(zoom) copy $imh -from $sx $sy
355                blt::winop resample $_image(zoom) $_image(final) sinc
356            }
357        } else {
358            set iw [image width $imh]
359            set ih [image height $imh]
360            set wz [expr {round(double($iw)/$_scale(current))}]
361            set hz [expr {round(double($ih)/$_scale(current))}]
362            if {$wz > 1 && $hz > 1} {
363                $_image(zoom) configure -width $wz -height $hz
364                $_image(zoom) put $bg -to 0 0 $wz $hz
365                blt::winop resample $imh $_image(zoom) sinc
366                $_image(final) copy $_image(zoom) -from $_scale(x) $_scale(y)
367            }
368        }
369    }
370}
371
372# ----------------------------------------------------------------------
373# USAGE: _topimage
374#
375# Used internally to get the topmost image currently being displayed.
376# ----------------------------------------------------------------------
377itcl::body Rappture::ImageResult::_topimage {} {
378    set top $_topmost
379    if {"" == $top} {
380        set top [lindex $_dlist 0]
381    }
382    if {"" != $top} {
383        return [$top tkimage]
384    }
385    return ""
386}
387
388# ----------------------------------------------------------------------
389# USAGE: _zoom reset
390# USAGE: _zoom in
391# USAGE: _zoom out
392#
393# Called automatically when the user clicks on one of the zoom
394# controls for this widget.  Changes the zoom for the current view.
395# ----------------------------------------------------------------------
396itcl::body Rappture::ImageResult::_zoom {option args} {
397    switch -- $option {
398        rescale {
399            # empty list? then reset w/h max size
400            if {[llength $_dlist] == 0} {
401                set _max(w) 0
402                set _max(h) 0
403                set _scale(max) 1.0
404            } else {
405                set w [winfo width $itk_component(image)]
406                set h [winfo height $itk_component(image)]
407                if {$w == 1 && $h == 1} {
408                    return 0
409                }
410
411                set wfac [expr {$_max(w)/double($w)}]
412                set hfac [expr {$_max(h)/double($h)}]
413                set _scale(max) [expr {($wfac > $hfac) ? $wfac : $hfac}]
414            }
415            return 1
416        }
417        reset {
418            set _scale(current) $_scale(max)
419            set _scale(x) 0
420            set _scale(y) 0
421        }
422        in {
423            set _scale(current) [expr {$_scale(current)*0.5}]
424        }
425        out {
426            set w [winfo width $itk_component(image)]
427            set h [winfo height $itk_component(image)]
428            if {$_max(w)/$_scale(current) > $w
429                  || $_max(h)/$_scale(current) > $h} {
430                # must be room left to zoom -- zoom out, but not beyond max
431                set _scale(current) [expr {$_scale(current)*2.0}]
432                if {$_scale(current) < $_scale(max)} {
433                    set _scale(current) $_scale(max)
434                }
435            } else {
436                # no room left to zoom -- zoom out max
437                if {$_scale(max) < 1} {
438                    set _scale(current) 1
439                } else {
440                    set _scale(current) $_scale(max)
441                }
442            }
443        }
444    }
445    $_dispatcher event -idle !rebuild
446}
447
448# ----------------------------------------------------------------------
449# USAGE: _move click <x> <y>
450# USAGE: _move drag <x> <y>
451# USAGE: _move release <x> <y>
452#
453# Called automatically when the user clicks and drags on the image
454# to pan the view.  Adjusts the (x,y) offset for the scaling info
455# and redraws the widget.
456# ----------------------------------------------------------------------
457itcl::body Rappture::ImageResult::_move {option args} {
458    switch -- $option {
459        click {
460            foreach {x y} $args break
461            $itk_component(image) configure -cursor fleur
462            set _scale(x0) $_scale(x)
463            set _scale(y0) $_scale(y)
464            set _scale(xclick) $x
465            set _scale(yclick) $y
466        }
467        drag {
468            foreach {x y} $args break
469            if {[info exists _scale(xclick)] && [info exists _scale(yclick)]} {
470                set w [winfo width $itk_component(image)]
471                set h [winfo height $itk_component(image)]
472                set wx [expr {round($_max(w)/$_scale(current))}]
473                set hy [expr {round($_max(h)/$_scale(current))}]
474                if {$wx > $w || $hy > $h} {
475                    set x [expr {$_scale(x0)-$x+$_scale(xclick)}]
476                    if {$x > $wx-$w} {set x [expr {$wx-$w}]}
477                    if {$x < 0} {set x 0}
478
479                    set y [expr {$_scale(y0)-$y+$_scale(yclick)}]
480                    if {$y > $hy-$h} {set y [expr {$hy-$h}]}
481                    if {$y < 0} {set y 0}
482
483                    set _scale(x) $x
484                    set _scale(y) $y
485                } else {
486                    set _scale(x) 0
487                    set _scale(y) 0
488                }
489                $_dispatcher event -idle !rebuild
490            }
491        }
492        release {
493            eval _move drag $args
494            $itk_component(image) configure -cursor ""
495            catch {unset _scale(xclick)}
496            catch {unset _scale(yclick)}
497        }
498        default {
499            error "bad option \"$option\": should be click, drag, release"
500        }
501    }
502}
Note: See TracBrowser for help on using the repository browser.