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

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