Changeset 13 for trunk/gui/scripts/energyLevels.tcl
- Timestamp:
- Jun 8, 2005 5:37:19 PM (19 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/gui/scripts/energyLevels.tcl
r11 r13 17 17 option add *EnergyLevels.width 4i widgetDefault 18 18 option add *EnergyLevels.height 4i widgetDefault 19 option add *EnergyLevels.levelColor blue widgetDefault 20 option add *EnergyLevels.levelTextForeground blue widgetDefault 21 option add *EnergyLevels.levelTextBackground #d9d9d9 widgetDefault 19 option add *EnergyLevels.padding 4 widgetDefault 20 option add *EnergyLevels.controlBackground gray widgetDefault 21 option add *EnergyLevels.shadeColor gray widgetDefault 22 option add *EnergyLevels.levelColor black widgetDefault 23 option add *EnergyLevels.levelTextForeground black widgetDefault 24 option add *EnergyLevels.levelTextBackground white widgetDefault 22 25 23 26 option add *EnergyLevels.font \ 24 27 -*-helvetica-medium-r-normal-*-*-120-* widgetDefault 25 28 26 option add *EnergyLevels.detailFont \ 27 -*-helvetica-medium-r-normal-*-*-100-* widgetDefault 29 blt::bitmap define EnergyLevels-reset { 30 #define reset_width 12 31 #define reset_height 12 32 static unsigned char reset_bits[] = { 33 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 34 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00}; 35 } 36 37 blt::bitmap define EnergyLevels-zoomin { 38 #define zoomin_width 12 39 #define zoomin_height 12 40 static unsigned char zoomin_bits[] = { 41 0x7c, 0x00, 0x82, 0x00, 0x11, 0x01, 0x11, 0x01, 0x7d, 0x01, 0x11, 0x01, 42 0x11, 0x01, 0x82, 0x03, 0xfc, 0x07, 0x80, 0x0f, 0x00, 0x0f, 0x00, 0x06}; 43 } 44 45 blt::bitmap define EnergyLevels-zoomout { 46 #define zoomout_width 12 47 #define zoomout_height 12 48 static unsigned char zoomout_bits[] = { 49 0x7c, 0x00, 0x82, 0x00, 0x01, 0x01, 0x01, 0x01, 0x7d, 0x01, 0x01, 0x01, 50 0x01, 0x01, 0x82, 0x03, 0xfc, 0x07, 0x80, 0x0f, 0x00, 0x0f, 0x00, 0x06}; 51 } 52 53 blt::bitmap define EnergyLevels-rdiag { 54 #define rdiag_width 8 55 #define rdiag_height 8 56 static unsigned char rdiag_bits[] = { 57 0x66, 0x33, 0x99, 0xcc, 0x66, 0x33, 0x99, 0xcc}; 58 } 59 28 60 29 61 itcl::class Rappture::EnergyLevels { 30 62 inherit itk::Widget 31 63 32 itk_option define - layout layout Layout ""33 itk_option define - output output Output""64 itk_option define -padding padding Padding 0 65 itk_option define -shadecolor shadeColor ShadeColor "" 34 66 itk_option define -levelcolor levelColor LevelColor "" 35 67 itk_option define -leveltextforeground levelTextForeground Foreground "" … … 37 69 38 70 constructor {args} { # defined below } 39 destructor { # defined below } 40 41 protected method _render {} 42 protected method _adjust {what val} 43 protected method _getColumn {name} 44 protected method _getUnits {name} 45 protected method _getMidPt {elist pos} 71 72 public proc columns {table} 73 74 public method add {table {settings ""}} 75 public method delete {args} 76 public method scale {args} 77 78 protected method _redraw {{what all}} 79 protected method _zoom {option args} 80 protected method _view {midE delE} 81 protected method _hilite {option args} 82 protected method _getLayout {} 83 84 private variable _dispatcher "" ;# dispatcher for !events 85 86 private variable _dlist "" ;# list of data objects 87 private variable _dobj2color ;# maps data obj => color option 88 private variable _dobj2raise ;# maps data obj => raise option 89 private variable _dobj2cols ;# maps data obj => column names 90 private variable _emin "" ;# autoscale min for energy 91 private variable _emax "" ;# autoscale max for energy 92 private variable _eviewmin "" ;# min for "zoom" view 93 private variable _eviewmax "" ;# max for "zoom" view 94 private variable _edefmin "" ;# min for default "zoom" view 95 private variable _edefmax "" ;# max for default "zoom" view 96 private variable _ehomo "" ;# energy of HOMO level in topmost dataset 97 private variable _elumo "" ;# energy of LUMO level in topmost dataset 98 private variable _hilite "" ;# item currently highlighted 46 99 } 47 100 48 101 itk::usual EnergyLevels { 102 keep -background -foreground -cursor -font 49 103 } 50 104 … … 53 107 # ---------------------------------------------------------------------- 54 108 itcl::body Rappture::EnergyLevels::constructor {args} { 109 Rappture::dispatcher _dispatcher 110 $_dispatcher register !redraw 111 $_dispatcher dispatch $this !redraw "[itcl::code $this _redraw all]; list" 112 $_dispatcher register !zoom 113 $_dispatcher dispatch $this !zoom "[itcl::code $this _redraw zoom]; list" 114 55 115 itk_option add hull.width hull.height 56 116 pack propagate $itk_component(hull) no 57 117 118 itk_component add controls { 119 frame $itk_interior.cntls 120 } { 121 usual 122 rename -background -controlbackground controlBackground Background 123 } 124 pack $itk_component(controls) -side right -fill y 125 126 itk_component add reset { 127 button $itk_component(controls).reset \ 128 -borderwidth 1 -padx 1 -pady 1 \ 129 -bitmap EnergyLevels-reset \ 130 -command [itcl::code $this _zoom reset] 131 } { 132 usual 133 ignore -borderwidth 134 rename -highlightbackground -controlbackground controlBackground Background } 135 pack $itk_component(reset) -padx 4 -pady 4 136 Rappture::Tooltip::for $itk_component(reset) "Reset the view to the default zoom level" 137 138 itk_component add zoomin { 139 button $itk_component(controls).zin \ 140 -borderwidth 1 -padx 1 -pady 1 \ 141 -bitmap EnergyLevels-zoomin \ 142 -command [itcl::code $this _zoom in] 143 } { 144 usual 145 ignore -borderwidth 146 rename -highlightbackground -controlbackground controlBackground Background 147 } 148 pack $itk_component(zoomin) -padx 4 -pady 4 149 Rappture::Tooltip::for $itk_component(zoomin) "Zoom in" 150 151 itk_component add zoomout { 152 button $itk_component(controls).zout \ 153 -borderwidth 1 -padx 1 -pady 1 \ 154 -bitmap EnergyLevels-zoomout \ 155 -command [itcl::code $this _zoom out] 156 } { 157 usual 158 ignore -borderwidth 159 rename -highlightbackground -controlbackground controlBackground Background 160 } 161 pack $itk_component(zoomout) -padx 4 -pady 4 162 Rappture::Tooltip::for $itk_component(zoomout) "Zoom out" 163 58 164 # 59 165 # Add label for the title. … … 64 170 pack $itk_component(title) -side top 65 171 66 67 itk_component add cntls { 68 frame $itk_interior.cntls 69 } 70 pack $itk_component(cntls) -side right -fill y 71 grid rowconfigure $itk_component(cntls) 0 -weight 1 72 grid rowconfigure $itk_component(cntls) 1 -minsize 10 73 grid rowconfigure $itk_component(cntls) 2 -weight 1 74 75 # 76 # Add MORE/FEWER levels control for TOP of graph 77 # 78 itk_component add upperE { 79 frame $itk_component(cntls).upperE 80 } 81 82 itk_component add upperEmore { 83 label $itk_component(upperE).morel -text "More" 172 # 173 # Add graph showing levels 174 # 175 itk_component add graph { 176 canvas $itk_interior.graph -highlightthickness 0 84 177 } { 85 178 usual 86 rename -font -detailfont detailFont Font 87 } 88 pack $itk_component(upperEmore) -side top 89 90 itk_component add upperEfewer { 91 label $itk_component(upperE).fewerl -text "Fewer" 92 } { 93 usual 94 rename -font -detailfont detailFont Font 95 } 96 pack $itk_component(upperEfewer) -side bottom 97 98 itk_component add upperEcntl { 99 scale $itk_component(upperE).cntl -orient vertical -showvalue 0 \ 100 -command [itcl::code $this _adjust upper] 101 } 102 pack $itk_component(upperEcntl) -side top -fill y 103 104 # 105 # Add MORE/FEWER levels control for BOTTOM of graph 106 # 107 itk_component add lowerE { 108 frame $itk_component(cntls).lowerE 109 } 110 111 itk_component add lowerEmore { 112 label $itk_component(lowerE).morel -text "More" 113 } { 114 usual 115 rename -font -detailfont detailFont Font 116 } 117 pack $itk_component(lowerEmore) -side bottom 118 119 itk_component add lowerEfewer { 120 label $itk_component(lowerE).fewerl -text "Fewer" 121 } { 122 usual 123 rename -font -detailfont detailFont Font 124 } 125 pack $itk_component(lowerEfewer) -side top 126 127 itk_component add lowerEcntl { 128 scale $itk_component(lowerE).cntl -orient vertical -showvalue 0 \ 129 -command [itcl::code $this _adjust lower] 130 } 131 pack $itk_component(lowerEcntl) -side top -fill y 132 133 # 134 # Add graph showing levels 135 # 136 itk_component add graph { 137 blt::graph $itk_interior.graph \ 138 -highlightthickness 0 -plotpadx 0 -plotpady 0 \ 139 -width 3i -height 3i 140 } { 141 keep -background -foreground -cursor -font 179 ignore -highlightthickness 142 180 } 143 181 pack $itk_component(graph) -expand yes -fill both 144 $itk_component(graph) legend configure -hide yes 182 183 bind $itk_component(graph) <Configure> \ 184 [list $_dispatcher event -idle !redraw] 185 186 bind $itk_component(graph) <ButtonPress-1> \ 187 [itcl::code $this _zoom at %x %y] 188 bind $itk_component(graph) <B1-Motion> \ 189 [itcl::code $this _zoom at %x %y] 190 191 bind $itk_component(graph) <Motion> \ 192 [itcl::code $this _hilite brush %x %y] 193 bind $itk_component(graph) <Leave> \ 194 [itcl::code $this _hilite hide] 195 196 bind $itk_component(graph) <KeyPress-Up> \ 197 [itcl::code $this _zoom nudge 1] 198 bind $itk_component(graph) <KeyPress-Right> \ 199 [itcl::code $this _zoom nudge 1] 200 bind $itk_component(graph) <KeyPress-plus> \ 201 [itcl::code $this _zoom nudge 1] 202 203 bind $itk_component(graph) <KeyPress-Down> \ 204 [itcl::code $this _zoom nudge -1] 205 bind $itk_component(graph) <KeyPress-Left> \ 206 [itcl::code $this _zoom nudge -1] 207 bind $itk_component(graph) <KeyPress-minus> \ 208 [itcl::code $this _zoom nudge -1] 145 209 146 210 eval itk_initialize $args … … 148 212 149 213 # ---------------------------------------------------------------------- 150 # DESTRUCTOR 151 # ---------------------------------------------------------------------- 152 itcl::body Rappture::EnergyLevels::destructor {} { 153 } 154 155 # ---------------------------------------------------------------------- 156 # USAGE: _render 214 # USAGE: columns <table> 215 # 216 # Clients use this to scan a <table> XML object and see if it contains 217 # columns for energy levels. If so, it returns a list of two column 218 # names: {labels energies}. 219 # ---------------------------------------------------------------------- 220 itcl::body Rappture::EnergyLevels::columns {dataobj} { 221 set names [$dataobj columns -component] 222 set epos [lsearch -exact $names column(levels)] 223 if {$epos >= 0} { 224 set units [$dataobj columns -units $epos] 225 if {![string match energy* [Rappture::Units::description $units]]} { 226 set epos -1 227 } 228 } 229 230 # can't find column named "levels"? then look for column with energies 231 if {$epos < 0} { 232 set index 0 233 foreach units [$dataobj columns -units] { 234 if {[string match energy* [Rappture::Units::description $units]]} { 235 if {$epos >= 0} { 236 # more than one energy column -- bail out 237 set epos -1 238 break 239 } 240 set epos $index 241 } 242 incr index 243 } 244 } 245 246 # look for a column with labels 247 set lpos -1 248 set index 0 249 foreach units [$dataobj columns -units] { 250 if {"" == $units} { 251 set vals [$dataobj values -column $index] 252 if {[regexp {(^|[[:space:]])HOMO([[:space:]]|$)} $vals]} { 253 if {$lpos >= 0} { 254 # more than one labels column -- bail out 255 set lpos -1 256 break 257 } 258 set lpos $index 259 } 260 } 261 incr index 262 } 263 264 if {$epos >= 0 || $lpos >= 0} { 265 return [list [lindex $names $lpos] [lindex $names $epos]] 266 } 267 return "" 268 } 269 270 # ---------------------------------------------------------------------- 271 # USAGE: add <dataobj> ?<settings>? 272 # 273 # Clients use this to add a data object to the plot. The optional 274 # <settings> are used to configure the plot. Allowed settings are 275 # -color, -brightness, -width, -linestyle and -raise. 276 # ---------------------------------------------------------------------- 277 itcl::body Rappture::EnergyLevels::add {dataobj {settings ""}} { 278 # 279 # Make sure this table contains energy levels. 280 # 281 set cols [Rappture::EnergyLevels::columns $dataobj] 282 if {"" == $cols} { 283 error "table \"$dataobj\" does not contain energy levels" 284 } 285 286 # 287 # Scan through the settings and resolve all values. 288 # 289 array set params { 290 -color black 291 -brightness 0 292 -width 1 293 -raise 0 294 -linestyle solid 295 } 296 foreach {opt val} $settings { 297 if {![info exists params($opt)]} { 298 error "bad setting \"$opt\": should be [join [lsort [array names params]] {, }]" 299 } 300 set params($opt) $val 301 } 302 303 # convert -linestyle to BLT -dashes 304 switch -- $params(-linestyle) { 305 dashed { set params(-linestyle) {4 4} } 306 dotted { set params(-linestyle) {2 4} } 307 default { set params(-linestyle) {} } 308 } 309 310 # if -brightness is set, then update the color 311 if {$params(-brightness) != 0} { 312 set params(-color) [Rappture::color::brightness \ 313 $params(-color) $params(-brightness)] 314 } 315 316 set pos [lsearch -exact $dataobj $_dlist] 317 if {$pos < 0} { 318 lappend _dlist $dataobj 319 set _dobj2color($dataobj) $params(-color) 320 set _dobj2raise($dataobj) $params(-raise) 321 322 foreach {lcol ecol} $cols break 323 set _dobj2cols($dataobj-label) $lcol 324 set _dobj2cols($dataobj-energy) $ecol 325 326 $_dispatcher event -idle !redraw 327 } 328 } 329 330 # ---------------------------------------------------------------------- 331 # USAGE: delete ?<dataobj1> <dataobj2> ...? 332 # 333 # Clients use this to delete a dataobj from the plot. If no dataobjs 334 # are specified, then all dataobjs are deleted. 335 # ---------------------------------------------------------------------- 336 itcl::body Rappture::EnergyLevels::delete {args} { 337 if {[llength $args] == 0} { 338 set args $_dlist 339 set _eviewmin "" 340 set _eviewmax "" 341 } 342 343 # delete all specified data objs 344 set changed 0 345 foreach dataobj $args { 346 set pos [lsearch -exact $_dlist $dataobj] 347 if {$pos >= 0} { 348 set _dlist [lreplace $_dlist $pos $pos] 349 catch {unset _dobj2color($dataobj)} 350 catch {unset _dobj2raise($dataobj)} 351 catch {unset _dobj2cols($dataobj-label)} 352 catch {unset _dobj2cols($dataobj-energy)} 353 set changed 1 354 } 355 } 356 357 # if anything changed, then rebuild the plot 358 if {$changed} { 359 $_dispatcher event -idle !redraw 360 } 361 } 362 363 # ---------------------------------------------------------------------- 364 # USAGE: scale ?<dataobj1> <dataobj2> ...? 365 # 366 # Sets the default limits for the overall plot according to the 367 # limits of the data for all of the given <dataobj> objects. This 368 # accounts for all dataobjs--even those not showing on the screen. 369 # Because of this, the limits are appropriate for all data as 370 # the user scans through data in the ResultSet viewer. 371 # ---------------------------------------------------------------------- 372 itcl::body Rappture::EnergyLevels::scale {args} { 373 set _emin "" 374 set _emax "" 375 foreach obj $args { 376 if {![info exists _dobj2cols($obj-energy)]} { 377 # don't recognize this object? then ignore it 378 continue 379 } 380 foreach {min max} [$obj limits $_dobj2cols($obj-energy)] break 381 382 if {"" != $min && "" != $max} { 383 if {"" == $_emin} { 384 set _emin $min 385 set _emax $max 386 } else { 387 if {$min < $_emin} { set _emin $min } 388 if {$max > $_emax} { set _emax $max } 389 } 390 } 391 } 392 } 393 394 # ---------------------------------------------------------------------- 395 # USAGE: _redraw 157 396 # 158 397 # Used internally to load a list of energy levels from a <table> within 159 # the -output XML object. The -layout object indicates how information 160 # should be extracted from the table. The <layout> should have an 161 # <energies> tag and perhaps a <labels> tag, which indicates the table 162 # and the column within the table containing the energies. 163 # ---------------------------------------------------------------------- 164 itcl::body Rappture::EnergyLevels::_render {} { 165 # 166 # Clear any information shown in the graph. 167 # 168 set graph $itk_component(graph) 169 eval $graph element delete [$graph element names] 170 eval $graph marker delete [$graph marker names] 171 172 # 173 # Plug in the title from the layout 174 # 175 set title "" 176 if {$itk_option(-layout) != ""} { 177 set title [$itk_option(-layout) get label] 178 } 179 if {"" != $title} { 180 pack $itk_component(title) -side top -before $graph 181 $itk_component(title) configure -text $title 398 # the data objects. 399 # ---------------------------------------------------------------------- 400 itcl::body Rappture::EnergyLevels::_redraw {{what all}} { 401 # scale data now, if we haven't already 402 if {"" == $_emin || "" == $_emax} { 403 eval scale $_dlist 404 } 405 406 # put the dataobj list in order according to -raise options 407 set dlist $_dlist 408 foreach obj $dlist { 409 if {[info exists _dobj2raise($obj)] && $_dobj2raise($obj)} { 410 set i [lsearch -exact $dlist $obj] 411 if {$i >= 0} { 412 set dlist [lreplace $dlist $i $i] 413 lappend dlist $obj 414 } 415 } 416 } 417 set topdobj [lindex $dlist end] 418 419 _getLayout 420 421 # 422 # Redraw the overall layout 423 # 424 if {$what == "all"} { 425 $c delete all 426 if {[llength $dlist] == 0} { 427 return 428 } 429 430 # 431 # Scan through all data objects and plot them in order from 432 # the bottom up. 433 # 434 set e2y [expr {($y1-$y0)/($_emax-$_emin)}] 435 436 set title "" 437 set dataobj "" 438 foreach dataobj $dlist { 439 if {"" == $title} { 440 set title [$dataobj hints label] 441 } 442 443 set ecol $_dobj2cols($dataobj-energy) 444 set color $_dobj2color($dataobj) 445 if {"" == $color} { 446 set color $itk_option(-levelcolor) 447 } 448 set color [Rappture::color::brightness $color 0.7] 449 450 foreach eval [$dataobj values -column $ecol] { 451 set y [expr {($eval-$_emin)*$e2y + $y0}] 452 $c create line $xx0 $y $xx1 $y -fill $color -width 1 453 } 454 } 455 456 # 457 # Scan through the data and look for HOMO/LUMO levels. 458 # Set the default view to the energy just above and 459 # just below the HOMO/LUMO levels. 460 # 461 set _edefmin [expr {0.4*($_emax-$_emin) + $_emin}] 462 set _edefmax [expr {0.6*($_emax-$_emin) + $_emin}] 463 464 set nlumo -1 465 set nhomo -1 466 467 set dataobj [lindex $dlist end] 468 if {"" != $dataobj} { 469 set lcol $_dobj2cols($dataobj-label) 470 set ecol $_dobj2cols($dataobj-energy) 471 set units [$dataobj columns -units $ecol] 472 473 set n 0 474 foreach eval [$dataobj values -column $ecol] \ 475 lval [$dataobj values -column $lcol] { 476 477 if {$lval == "HOMO"} { 478 set nhomo $n 479 set nlumo [expr {$n+1}] 480 } elseif {$lval == "LUMO"} { 481 set nlumo $n 482 } 483 incr n 484 } 485 486 if {$nhomo >= 0 && $nlumo >= 0} { 487 set elist [$dataobj values -column $ecol] 488 set _ehomo [lindex $elist $nhomo] 489 set _elumo [lindex $elist $nlumo] 490 set gap [expr {$_elumo - $_ehomo}] 491 set _edefmin [expr {$_ehomo - 0.3*$gap}] 492 set _edefmax [expr {$_elumo + 0.3*$gap}] 493 494 set y [expr {($_ehomo-$_emin)*$e2y + $y0}] 495 set id [$c create rectangle $xx0 $y $xx1 $y0 \ 496 -stipple EnergyLevels-rdiag \ 497 -outline "" -fill $itk_option(-shadecolor)] 498 $c lower $id 499 } 500 } 501 if {"" == $_eviewmin || "" == $_eviewmax} { 502 set _eviewmin $_edefmin 503 set _eviewmax $_edefmax 504 } 505 506 if {"" != $title} { 507 pack $itk_component(title) -side top -before $c 508 $itk_component(title) configure -text $title 509 } else { 510 pack forget $itk_component(title) 511 } 512 513 # draw the lines for the "zoom" view (fixed up below) 514 set color $itk_option(-foreground) 515 $c create line $x0 $y0 $x1 $y0 -fill $color -tags zmin 516 $c create line $x0 $y0 $x1 $y0 -fill $color -tags zmax 517 518 $c create line $x1 $y0 $x2 $y0 -fill $color -tags zoomup 519 $c create line $x1 $y0 $x2 $y1 -fill $color -tags zoomdn 520 521 $c create line $x2 $y0 $x3 $y0 -fill $color 522 $c create line $x2 $y1 $x3 $y1 -fill $color 523 } 524 525 # 526 # Redraw the "zoom" area on the right side 527 # 528 if {$what == "zoom" || $what == "all"} { 529 set e2y [expr {($y1-$y0)/($_emax-$_emin)}] 530 531 set y [expr {($_eviewmin-$_emin)*$e2y + $y0}] 532 $c coords zmin $x0 $y $x1 $y 533 $c coords zoomup $x1 $y $x2 $y0 534 535 set y [expr {($_eviewmax-$_emin)*$e2y + $y0}] 536 $c coords zmax $x0 $y $x1 $y 537 $c coords zoomdn $x1 $y $x2 $y1 538 539 # redraw all levels in the current view 540 $c delete zlevels zlabels 541 542 set e2y [expr {($y1-$y0)/($_eviewmax-$_eviewmin)}] 543 foreach dataobj $dlist { 544 set ecol $_dobj2cols($dataobj-energy) 545 set color $_dobj2color($dataobj) 546 if {"" == $color} { 547 set color $itk_option(-levelcolor) 548 } 549 550 set n 0 551 foreach eval [$dataobj values -column $ecol] { 552 if {$eval >= $_eviewmin && $eval <= $_eviewmax} { 553 set y [expr {($eval-$_eviewmin)*$e2y + $y0}] 554 set id [$c create line $xx2 $y $xx3 $y \ 555 -fill $color -width 1 \ 556 -tags [list zlevels $dataobj-$n]] 557 } 558 incr n 559 } 560 } 561 562 if {"" != $_ehomo && "" != $_elumo} { 563 set ecol $_dobj2cols($topdobj-energy) 564 set units [$topdobj columns -units $ecol] 565 566 set yy0 [expr {($_ehomo-$_eviewmin)*$e2y + $y0}] 567 set yy1 [expr {($_elumo-$_eviewmin)*$e2y + $y0}] 568 $c create line [expr {$x3-10}] $yy0 [expr {$x3-10}] $yy1 \ 569 -arrow both -fill $itk_option(-foreground) \ 570 -tags zlabels 571 $c create text [expr {$x3-15}] [expr {0.5*($yy0+$yy1)}] \ 572 -anchor e -text "Eg = [expr {$_elumo-$_ehomo}] $units" \ 573 -tags zlabels 574 575 # label the HOMO level 576 set tid [$c create text [expr {0.5*($x2+$x3)}] $yy0 -anchor c \ 577 -text "HOMO = $_ehomo $units" \ 578 -fill $itk_option(-leveltextforeground) \ 579 -tags zlabels] 580 581 foreach {xb0 yb0 xb1 yb1} [$c bbox $tid] break 582 set tid2 [$c create rectangle \ 583 [expr {$xb0-1}] [expr {$yb0-1}] \ 584 [expr {$xb1+1}] [expr {$yb1+1}] \ 585 -outline $itk_option(-leveltextforeground) \ 586 -fill $itk_option(-leveltextbackground) \ 587 -tags zlabels] 588 $c lower $tid2 $tid 589 590 # label the LUMO level 591 set tid [$c create text [expr {0.5*($x2+$x3)}] $yy1 -anchor c \ 592 -text "LUMO = $_elumo $units" \ 593 -fill $itk_option(-leveltextforeground) \ 594 -tags zlabels] 595 596 foreach {xb0 yb0 xb1 yb1} [$c bbox $tid] break 597 set tid2 [$c create rectangle \ 598 [expr {$xb0-1}] [expr {$yb0-1}] \ 599 [expr {$xb1+1}] [expr {$yb1+1}] \ 600 -outline $itk_option(-leveltextforeground) \ 601 -fill $itk_option(-leveltextbackground) \ 602 -tags zlabels] 603 $c lower $tid2 $tid 604 605 set id [$c create rectangle $xx2 $yy0 $xx3 $y0 \ 606 -stipple EnergyLevels-rdiag \ 607 -outline "" -fill $itk_option(-shadecolor) \ 608 -tags zlabels] 609 $c lower $id 610 } 611 } 612 } 613 614 # ---------------------------------------------------------------------- 615 # USAGE: _zoom in 616 # USAGE: _zoom out 617 # USAGE: _zoom reset 618 # USAGE: _zoom at <x> <y> 619 # USAGE: _zoom nudge <dir> 620 # 621 # Called automatically when the user clicks on one of the zoom 622 # controls for this widget. Changes the zoom for the current view. 623 # ---------------------------------------------------------------------- 624 itcl::body Rappture::EnergyLevels::_zoom {option args} { 625 switch -- $option { 626 in { 627 set midE [expr {0.5*($_eviewmax + $_eviewmin)}] 628 set delE [expr {0.8*($_eviewmax - $_eviewmin)}] 629 _view $midE $delE 630 } 631 out { 632 set midE [expr {0.5*($_eviewmax + $_eviewmin)}] 633 set delE [expr {1.25*($_eviewmax - $_eviewmin)}] 634 _view $midE $delE 635 } 636 reset { 637 set _eviewmin $_edefmin 638 set _eviewmax $_edefmax 639 $_dispatcher event -idle !zoom 640 } 641 at { 642 if {[llength $args] != 2} { 643 error "wrong # args: should be \"_zoom at x y\"" 644 } 645 set x [lindex $args 0] 646 set y [lindex $args 1] 647 648 _getLayout 649 set y2e [expr {($_emax-$_emin)/($y1-$y0)}] 650 651 if {$x > $x1} { 652 return 653 } 654 set midE [expr {($y-$y0)*$y2e + $_emin}] 655 set delE [expr {$_eviewmax - $_eviewmin}] 656 _view $midE $delE 657 } 658 nudge { 659 if {[llength $args] != 1} { 660 error "wrong # args: should be \"_zoom nudge dir\"" 661 } 662 set dir [lindex $args 0] 663 664 set midE [expr {0.5*($_eviewmax + $_eviewmin)}] 665 set delE [expr {$_eviewmax - $_eviewmin}] 666 set midE [expr {$midE + $dir*0.25*$delE}] 667 _view $midE $delE 668 } 669 } 670 focus $itk_component(graph) 671 } 672 673 # ---------------------------------------------------------------------- 674 # USAGE: _view <midE> <delE> 675 # 676 # Called automatically when the user clicks/drags on the left side 677 # of the widget where energy levels are displayed. Sets the zoom 678 # view so that it's centered on the <y> coordinate. 679 # ---------------------------------------------------------------------- 680 itcl::body Rappture::EnergyLevels::_view {midE delE} { 681 if {$delE > $_emax-$_emin} { 682 set delE [expr {$_emax - $_emin}] 683 } 684 if {$midE - 0.5*$delE < $_emin} { 685 set _eviewmin $_emin 686 set _eviewmax [expr {$_eviewmin+$delE}] 687 } elseif {$midE + 0.5*$delE > $_emax} { 688 set _eviewmax $_emax 689 set _eviewmin [expr {$_eviewmax-$delE}] 182 690 } else { 183 pack forget $itk_component(title) 184 } 185 186 # 187 # Look through the layout and figure out what to extract 188 # from the table. 189 # 190 set elist [_getColumn energies] 191 if {[llength $elist] == 0} { 192 return 193 } 194 set units [_getUnits energies] 195 196 set llist [_getColumn names] 197 if {[llength $llist] == 0} { 198 # no labels? then invent some 199 set i 0 200 foreach name $elist { 201 lappend llist "E$i" 202 incr i 203 } 204 } 205 206 # 207 # Update the graph to show the current set of levels. 208 # 209 set n 0 210 set nlumo -1 211 set emax "" 212 set emin "" 213 set ehomo "" 214 set elumo "" 215 foreach eval $elist lval $llist { 216 if {$lval == "HOMO"} { 217 set ehomo $eval 218 set lval "HOMO = $eval $units" 219 set nlumo [expr {$n+1}] 220 } elseif {$lval == "LUMO" || $n == $nlumo} { 221 set elumo $eval 222 set lval "LUMO = $eval $units" 223 } else { 224 set lval "" 225 } 226 227 set elem "elem[incr n]" 228 $graph element create $elem \ 229 -xdata {0 1} -ydata [list $eval $eval] \ 230 -color $itk_option(-levelcolor) -symbol "" -linewidth 1 231 232 if {$lval != ""} { 233 $graph marker create text -coords [list 0.5 $eval] \ 234 -text $lval -anchor c \ 235 -foreground $itk_option(-leveltextforeground) \ 236 -background $itk_option(-leveltextbackground) 237 } 238 239 if {$emax == ""} { 240 set emax $eval 241 set emin $eval 242 } else { 243 if {$eval > $emax} {set emax $eval} 244 if {$eval < $emin} {set emin $eval} 245 } 246 } 247 $graph xaxis configure -min 0 -max 1 -showticks off -linewidth 0 248 if {$units != ""} { 249 $graph yaxis configure -title "Energy ($units)" 250 } else { 251 $graph yaxis configure -title "Energy" 252 } 253 254 # bump the limits so they are big enough to show labels 255 set fnt $itk_option(-font) 256 set h [expr {0.5*([font metrics $fnt -linespace] + 5)}] 257 set emin [expr {$emin-($emax-$emin)*$h/150.0}] 258 set emax [expr {$emax+($emax-$emin)*$h/150.0}] 259 $graph yaxis configure -min $emin -max $emax 260 261 # 262 # If we found HOMO/LUMO levels, then add the band gap at 263 # that point. Also, fix the controls for energy range. 264 # 265 if {$ehomo != "" && $elumo != ""} { 266 set id [$graph marker create line \ 267 -coords [list 0.2 $elumo 0.2 $ehomo]] 268 $graph marker after $id 269 270 set egap [expr {$elumo-$ehomo}] 271 set emid [expr {0.5*($ehomo+$elumo)}] 272 $graph marker create text \ 273 -coords [list 0.21 $emid] -background "" \ 274 -text "Eg = [format %.2g $egap] $units" -anchor w 275 276 # fix the limits for the lower scale 277 set elim [_getMidPt $elist [expr {$nlumo-1}]] 278 if {"" != $elim} { 279 $itk_component(lowerEcntl) configure -from $elim -to $emin \ 280 -resolution [expr {0.02*($elim-$emin)}] 281 grid $itk_component(lowerE) -row 2 -column 0 -sticky ns 282 283 set e0 [_getMidPt $elist [expr {$nlumo-3}]] 284 if {"" != $e0} { 285 $itk_component(lowerEcntl) set $e0 691 set _eviewmin [expr {$midE - 0.5*$delE}] 692 set _eviewmax [expr {$midE + 0.5*$delE}] 693 } 694 $_dispatcher event -idle !zoom 695 } 696 697 # ---------------------------------------------------------------------- 698 # USAGE: _hilite brush <x> <y> 699 # USAGE: _hilite show <dataobj> <level> 700 # USAGE: _hilite hide 701 # 702 # Used internally to highlight energy levels in the zoom view and 703 # show their associated energy. The "brush" operation is called 704 # as the mouse moves in the zoom view, to see if the <x>,<y> 705 # coordinate is touching a level. The show/hide operations are 706 # then used to show/hide level info. 707 # ---------------------------------------------------------------------- 708 itcl::body Rappture::EnergyLevels::_hilite {option args} { 709 switch -- $option { 710 brush { 711 if {[llength $args] != 2} { 712 error "wrong # args: should be \"_hilite brush x y\"" 713 } 714 set x [lindex $args 0] 715 set y [lindex $args 1] 716 717 _getLayout 718 if {$x < $x2 || $x > $x3} { 719 return ;# pointer must be in "zoom" area 720 } 721 722 set c $itk_component(graph) 723 set id [$c find withtag current] 724 725 # touching a line? then find the level and show its info 726 if {"" != $id} { 727 set e2y [expr {($y1-$y0)/($_eviewmax-$_eviewmin)}] 728 729 # put the dataobj list in order according to -raise options 730 set dlist $_dlist 731 foreach obj $dlist { 732 if {[info exists _dobj2raise($obj)] && $_dobj2raise($obj)} { 733 set i [lsearch -exact $dlist $obj] 734 if {$i >= 0} { 735 set dlist [lreplace $dlist $i $i] 736 lappend dlist $obj 737 } 738 } 739 } 740 741 set found 0 742 foreach dataobj $dlist { 743 set ecol $_dobj2cols($dataobj-energy) 744 set n 0 745 foreach eval [$dataobj values -column $ecol] { 746 set ylevel [expr {($eval-$_eviewmin)*$e2y + $y0}] 747 if {$y >= $ylevel-3 && $y <= $ylevel+3} { 748 set found 1 749 break 750 } 751 incr n 752 } 753 if {$found} break 754 } 755 if {$found} { 756 _hilite show $dataobj $n 757 } else { 758 _hilite hide 759 } 286 760 } else { 287 $itk_component(lowerEcntl) set $elim 288 } 289 } else { 290 grid forget $itk_component(lowerE) 291 } 292 293 # fix the limits for the upper scale 294 set elim [_getMidPt $elist [expr {$nlumo+1}]] 295 if {"" != $elim} { 296 $itk_component(upperEcntl) configure -from $emax -to $elim \ 297 -resolution [expr {0.02*($emax-$elim)}] 298 grid $itk_component(upperE) -row 0 -column 0 -sticky ns 299 300 set e0 [_getMidPt $elist [expr {$nlumo+3}]] 301 if {"" != $e0} { 302 $itk_component(upperEcntl) set $e0 303 } else { 304 $itk_component(upperEcntl) set $elim 305 } 306 } else { 307 grid forget $itk_component(upperE) 308 } 309 } else { 310 grid forget $itk_component(upperE) 311 grid forget $itk_component(lowerE) 312 } 313 } 314 315 # ---------------------------------------------------------------------- 316 # USAGE: _adjust <what> <val> 761 _hilite hide 762 } 763 } 764 show { 765 if {[llength $args] != 2} { 766 error "wrong # args: should be \"_hilite show dataobj level\"" 767 } 768 set dataobj [lindex $args 0] 769 set level [lindex $args 1] 770 771 if {$_hilite == "$dataobj $level"} { 772 return 773 } 774 _hilite hide 775 776 set lcol $_dobj2cols($dataobj-label) 777 set lval [lindex [$dataobj values -column $lcol] $level] 778 set ecol $_dobj2cols($dataobj-energy) 779 set eval [lindex [$dataobj values -column $ecol] $level] 780 set units [$dataobj columns -units $ecol] 781 782 if {$eval == $_ehomo || $eval == $_elumo} { 783 # don't pop up info for the HOMO/LUMO levels 784 return 785 } 786 787 _getLayout 788 set e2y [expr {($y1-$y0)/($_eviewmax-$_eviewmin)}] 789 set y [expr {($eval-$_eviewmin)*$e2y + $y0}] 790 791 set tid [$c create text [expr {0.5*($x2+$x3)}] $y -anchor c \ 792 -text "$lval = $eval $units" \ 793 -fill $itk_option(-leveltextforeground) \ 794 -tags hilite] 795 796 foreach {x0 y0 x1 y1} [$c bbox $tid] break 797 set tid2 [$c create rectangle \ 798 [expr {$x0-1}] [expr {$y0-1}] \ 799 [expr {$x1+1}] [expr {$y1+1}] \ 800 -outline $itk_option(-leveltextforeground) \ 801 -fill $itk_option(-leveltextbackground) \ 802 -tags hilite] 803 $c lower $tid2 $tid 804 805 $c itemconfigure $dataobj-$level -width 2 806 set _hilite "$dataobj $level" 807 } 808 hide { 809 if {"" != $_hilite} { 810 $itk_component(graph) delete hilite 811 $itk_component(graph) itemconfigure zlevels -width 1 812 set _hilite "" 813 } 814 } 815 default { 816 error "bad option \"$option\": should be brush, show, hide" 817 } 818 } 819 } 820 821 # ---------------------------------------------------------------------- 822 # USAGE: _getLayout 317 823 # 318 # Used internally to adjust the upper/lower limits of the graph 319 # as the user drags the slider from "More" to "Fewer". Sets 320 # the specified limit to the given value. 321 # ---------------------------------------------------------------------- 322 itcl::body Rappture::EnergyLevels::_adjust {what val} { 323 switch -- $what { 324 upper { 325 $itk_component(graph) yaxis configure -max $val 326 } 327 lower { 328 $itk_component(graph) yaxis configure -min $val 329 } 330 default { 331 error "bad limit \"$what\": should be upper or lower" 332 } 333 } 334 } 335 336 # ---------------------------------------------------------------------- 337 # USAGE: _getColumn <name> 338 # 339 # Used internally to load a list of energy levels from a <table> within 340 # the -output XML object. The -layout object indicates how information 341 # should be extracted from the table. The <layout> should have an 342 # <energies> tag and perhaps a <labels> tag, which indicates the table 343 # and the column within the table containing the energies. 344 # ---------------------------------------------------------------------- 345 itcl::body Rappture::EnergyLevels::_getColumn {name} { 346 puts "_getColumn $name" 347 if {$itk_option(-output) == ""} { 348 return 349 } 350 351 # 352 # Figure out which column in which table contains the data. 353 # Then, find that table and extract the column. Figure out 354 # the position of the column from the list of all column names. 355 # 356 if {$itk_option(-layout) != ""} { 357 set table [$itk_option(-layout) get $name.table] 358 set col [$itk_option(-layout) get $name.column] 359 360 set clist "" 361 foreach c [$itk_option(-output) children -type column $table] { 362 lappend clist [$itk_option(-output) get $table.$c.label] 363 } 364 set ipos [lsearch $clist $col] 365 if {$ipos < 0} { 366 return ;# can't find data -- bail out! 367 } 368 set units [$itk_option(-output) get $table.column$ipos.units] 369 set path "$table.data" 370 } else { 371 set clist "" 372 foreach c [$itk_option(-output) children -type column] { 373 lappend clist [$itk_option(-output) get $c.units] 374 } 375 if {$name == "energies"} { 376 set units "eV" 377 } else { 378 set units "" 379 } 380 set ipos [lsearch -exact $clist $units] 381 if {$ipos < 0} { 382 return ;# can't find data -- bail out! 383 } 384 set path "data" 385 } 386 387 set rlist "" 388 foreach line [split [$itk_option(-output) get $path] "\n"] { 389 if {"" != [string trim $line]} { 390 set val [lindex $line $ipos] 391 392 if {$units != ""} { 393 set val [Rappture::Units::convert $val \ 394 -context $units -to $units -units off] 395 } 396 lappend rlist $val 397 } 398 } 399 return $rlist 400 } 401 402 # ---------------------------------------------------------------------- 403 # USAGE: _getUnits <name> 404 # 405 # Used internally to extract the units from a <table> within the 406 # -output XML object. The -layout object indicates how information 407 # should be extracted from the table. The <layout> should have an 408 # <energies> tag and perhaps a <labels> tag, which indicates the table 409 # and the column within the table containing the units. 410 # ---------------------------------------------------------------------- 411 itcl::body Rappture::EnergyLevels::_getUnits {name} { 412 if {$itk_option(-output) == ""} { 413 return 414 } 415 416 # 417 # Figure out which column in which table contains the data. 418 # Then, find that table and extract the column. Figure out 419 # the position of the column from the list of all column names. 420 # 421 if {$itk_option(-layout) != ""} { 422 set table [$itk_option(-layout) get $name.table] 423 set col [$itk_option(-layout) get $name.column] 424 425 set clist "" 426 foreach c [$itk_option(-output) children -type column $table] { 427 lappend clist [$itk_option(-output) get $table.$c.label] 428 } 429 set ipos [lsearch $clist $col] 430 if {$ipos < 0} { 431 return ;# can't find data -- bail out! 432 } 433 set units [$itk_option(-output) get $table.column$ipos.units] 434 } else { 435 if {$name == "energies"} { 436 set units "eV" 437 } else { 438 set units "" 439 } 440 } 441 return $units 442 } 443 444 # ---------------------------------------------------------------------- 445 # USAGE: _getMidPt <elist> <pos> 446 # 447 # Used internally to compute the midpoint between two energy levels 448 # at <pos> and <pos-1> in the <elist>. Returns a number representing 449 # the mid-point (average value) or "" if the levels involved do 450 # no exist in <elist>. 451 # ---------------------------------------------------------------------- 452 itcl::body Rappture::EnergyLevels::_getMidPt {elist pos} { 453 if {$pos < [llength $elist] && $pos > 1} { 454 set e1 [lindex $elist $pos] 455 set e0 [lindex $elist [expr {$pos-1}]] 456 return [expr {0.5*($e0+$e1)}] 457 } 458 return "" 459 } 460 461 # ---------------------------------------------------------------------- 462 # OPTION: -layout 463 # ---------------------------------------------------------------------- 464 itcl::configbody Rappture::EnergyLevels::layout { 465 if {$itk_option(-layout) != "" 466 && ![Rappture::library isvalid $itk_option(-layout)]} { 467 error "bad value \"$itk_option(-layout)\": should be Rappture::library object" 468 } 469 after idle [itcl::code $this _render] 470 } 471 472 # ---------------------------------------------------------------------- 473 # OPTION: -output 474 # ---------------------------------------------------------------------- 475 itcl::configbody Rappture::EnergyLevels::output { 476 if {$itk_option(-output) != "" 477 && ![Rappture::library isvalid $itk_option(-output)]} { 478 error "bad value \"$itk_option(-output)\": should be Rappture::library object" 479 } 480 after cancel [itcl::code $this _render] 481 after idle [itcl::code $this _render] 824 # Used internally to compute a series of variables used when redrawing 825 # the widget. Creates the variables with the proper values in the 826 # calling context. 827 # ---------------------------------------------------------------------- 828 itcl::body Rappture::EnergyLevels::_getLayout {} { 829 upvar c c 830 set c $itk_component(graph) 831 832 upvar w w 833 set w [winfo width $c] 834 835 upvar h h 836 set h [winfo height $c] 837 838 # 839 # Measure the size of a typical label and use that to size 840 # the left/right portions. If the label is too big, leave 841 # at least a little room for the labels. 842 # 843 set size [font measure $itk_option(-font) "HOMO = X.XXXXXXe-XX eV"] 844 set size [expr {$size + 6*$itk_option(-padding)}] 845 846 if {$size > $w-20} { 847 set size [expr {$w-20}] 848 } elseif {$size < 0.66*$w} { 849 set size [expr {0.66*$w}] 850 } 851 set xm [expr {$w - $size}] 852 853 upvar x0 x0 854 set x0 $itk_option(-padding) 855 856 upvar x1 x1 857 set x1 [expr {$xm - $itk_option(-padding)}] 858 859 upvar x2 x2 860 set x2 [expr {$xm + $itk_option(-padding)}] 861 862 upvar x3 x3 863 set x3 [expr {$w - $itk_option(-padding)}] 864 865 866 upvar xx0 xx0 867 set xx0 [expr {$x0 + $itk_option(-padding)}] 868 869 upvar xx1 xx1 870 set xx1 [expr {$x1 - $itk_option(-padding)}] 871 872 upvar xx2 xx2 873 set xx2 [expr {$x2 + $itk_option(-padding)}] 874 875 upvar xx3 xx3 876 set xx3 [expr {$x3 - $itk_option(-padding)}] 877 878 879 upvar y0 y0 880 set y0 [expr {$h - $itk_option(-padding)}] 881 882 upvar y1 y1 883 set y1 $itk_option(-padding) 482 884 } 483 885 … … 486 888 # ---------------------------------------------------------------------- 487 889 itcl::configbody Rappture::EnergyLevels::levelcolor { 488 after cancel [itcl::code $this _render] 489 after idle [itcl::code $this _render] 890 $_dispatcher event -idle !redraw 490 891 } 491 892 … … 494 895 # ---------------------------------------------------------------------- 495 896 itcl::configbody Rappture::EnergyLevels::leveltextforeground { 496 after cancel [itcl::code $this _render] 497 after idle [itcl::code $this _render] 897 $_dispatcher event -idle !redraw 498 898 } 499 899 … … 502 902 # ---------------------------------------------------------------------- 503 903 itcl::configbody Rappture::EnergyLevels::leveltextbackground { 504 after cancel [itcl::code $this _render] 505 after idle [itcl::code $this _render] 506 } 904 $_dispatcher event -idle !redraw 905 }
Note: See TracChangeset
for help on using the changeset viewer.