123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541 |
- ############################################################################
- #
- # LIBRARY: Gronsole program run and output widget
- # AUTHOR(S): Cedric Shock (cedricgrass AT shockfamily.net)
- # Based on lib/gis/gui.tcl
- # PURPOSE: Runs programs, displays output
- # COPYRIGHT: (C) 2006 Grass Development Team
- #
- # This program is free software under the GNU General Public
- # License (>=v2). Read the file COPYING that comes with GRASS
- # for details.
- #
- #############################################################################
- namespace eval Gronsole {
- variable _data
- variable _options
- set _options [list [list -clickcmd clickCmd ClickCmd {} {}]]
- proc ::Gronsole { path args } { return [eval Gronsole::create $path $args] }
- proc use {} {}
- }
- proc Gronsole::dooptions {path args init} {
- variable _data
- variable _options
-
- foreach opt $_options {
- set sw [lindex $opt 0]
- set db [lindex $opt 1]
- set def [lindex $opt 4]
- if {[set idx [lsearch -exact $args $sw]] != -1} {
- set _data($path,$db) [lindex $args [expr $idx + 1]]
- set args [concat [lrange $args 0 [expr $idx - 1]] [lrange $args [expr $idx + 2] end]]
- } elseif {$init} {
- set _data($path,$db) $def
- }
- }
- }
- proc Gronsole::create {path args} {
- global keycontrol
- global bgcolor
- variable _data
- set args [Gronsole::dooptions $path $args 1]
- set gronsolewin [ScrolledWindow $path -relief flat -borderwidth 1 -auto horizontal]
- set gronsole [eval text $gronsolewin.text $args]
- $gronsolewin setwidget $gronsole
- set _data($path,count) 0
- bind $path.text <Destroy> "Gronsole::_destroy $path"
- bind $path.text <$keycontrol-c> "tk_textCopy %W"
- bind $path.text <$keycontrol-v> "tk_textPaste %W"
- bind $path.text <$keycontrol-x> "tk_textCut %W"
- rename $path ::$path:scrollwin
- proc ::$path { cmd args } "return \[eval Gronsole::\$cmd $path \$args\]"
- return $path
- }
- proc Gronsole::configure { path args } {
- variable _options
- variable _data
- if {$args == {}} {
- set res {}
- foreach opt $_options {
- set sw [lindex $opt 0]
- set db [lindex $opt 1]
- set title [lindex $opt 2]
- lappend res [list $sw $db $title $_data($path,$db) $_data($path,$db)]
- }
- return [concat $res [$path.text configure]]
- }
- set args [Gronsole::dooptions $path $args 0]
-
- $path.text configure $args
- return
- }
- proc Gronsole::cget { path option } {
- variable _options
- variable _data
- if {[lsearch -exact $_options $option] != -1} {
- set res $_data($path,$option)
- } else {
- set res [$path.text cget $option]
- }
- return $res
- }
- proc Gronsole::_destroy { path } {
- variable _data
- array unset _data "$path,*"
- catch {rename $path {}}
- }
- ##########################################################################
- # Public contents management
- proc Gronsole::clear {path} {
- variable _data
- $path.text delete 1.0 end
- }
- # save text in output window
- proc Gronsole::save {path} {
- global env
- set dtxt $path.text
- if ![catch {$dtxt get sel.first}] {
- set svtxt [$dtxt get sel.first sel.last]
- } else {
- set svtxt [$dtxt get 1.0 end]
- }
-
- set types {
- {{TXT} {.txt}}
- }
- if { [info exists HOME] } {
- set dir $env(HOME)
- set path [tk_getSaveFile -initialdir $dir -filetypes $types \
- -defaultextension ".txt"]
- } else {
- set path [tk_getSaveFile -filetypes $types \
- -defaultextension ".txt"]
- }
- if { $path == "" } { return }
- set txtfile [open $path w]
- puts $txtfile $svtxt
- close $txtfile
- return
- }
- proc Gronsole::destroy_command {path ci} {
- variable _data
- catch {close $_data($path,$ci,fh)}
- if {[info exists _data($path,$ci,donecmd)] && $_data($path,$ci,donecmd) != {}} {
- eval $_data($path,$ci,donecmd)
- }
- set textarea $path.text
- set frame $_data($path,$ci,frame)
- set indices [$textarea tag ranges cmd$ci]
- eval $textarea delete $indices
- destroy $frame
- array unset _data "$path,$ci,*"
- }
- ##########################################################################
- # Private
- proc Gronsole::do_click {path ci} {
- variable _data
- # Use this commands click command if it exists
- if {[info exists _data($path,$ci,clickCmd)]} {
- set cc $_data($path,$ci,clickCmd)
- } else {
- set cc $_data($path,clickCmd)
- }
- if {$cc != {}} {
- eval $cc $ci [list $_data($path,$ci,cmd)]
- }
- }
- proc Gronsole::create_command {path cmd} {
- variable _data
- set textarea $path.text
- incr _data($path,count)
- set ci $_data($path,count)
- set _data($path,$ci,cmd) $cmd
-
- set module [lindex $cmd 0]
- set icon [icon module $module]
- set frame $textarea.cmd$ci
- set _data($path,$ci,frame) $frame
-
- frame $frame
- frame $frame.cmdline
- set tagframe [frame $frame.cmdline.tags]
- set cmdlabel [label $frame.cmdline.cmd -textvariable Gronsole::_data($path,$ci,cmd) -anchor nw]
- bind $cmdlabel <Button-1> "Gronsole::do_click $path $ci"
- # set cmdlabel [text $frame.cmdline.cmd -height 1 -width 10]
- # $cmdlabel insert end $cmd
- set ex [button $frame.cmdline.eX -text "X" -command "Gronsole::destroy_command $path $ci"]
- pack $ex -side right
- pack $frame.cmdline.tags -side right
- set iconwidth ""
- if {$icon != 0} {
- set iconwidth " - \[winfo width $frame.cmdline.icon\]"
- button $frame.cmdline.icon -image $icon -anchor n -command "Gronsole::do_click $path $ci"
- pack $frame.cmdline.icon -side left
- }
- pack $frame.cmdline.cmd -side left -expand yes -fill x
- pack $frame.cmdline -side top -expand yes -fill x
- set pbar [ProgressBar $frame.progress -fg green -bg white -height 20 -relief raised \
- -maximum 100 -variable Gronsole::_data($path,$ci,progress)]
- pack $pbar -side left
- set _data($path,$ci,progress) -1
- set _data($path,$ci,progressbar) $pbar
- set _data($path,$ci,tags) {}
- $textarea insert end "\n" [list cmd$ci e1]
- $textarea insert end "\n" [list cmd$ci e2]
- $textarea mark set cmdinsert$ci "end - 2 char"
- $textarea window create cmdinsert$ci -window $frame
- $textarea tag add cmd$ci $frame
- $textarea insert cmdinsert$ci "$cmd\n" [list cmd$ci e2]
- # $textarea tag add cmd$ci "cmdinsert$ci - 1 char"
- # $textarea tag add e2 "cmdinsert$ci - 1 char"
- $textarea tag configure e1 -elide 1
- $textarea tag configure e2 -elide 1
- set pspace 12
- $pbar configure -width [expr [winfo width $textarea] - $pspace]
- # $pbar configure -width [expr [winfo width $textarea] - $pspace] -height 20
- bind $textarea <Configure> "+catch {$pbar configure -width \[expr \[winfo width $textarea\] - $pspace\]}"
- bind $textarea <Configure> "+catch {$cmdlabel configure -wraplength \[expr \[winfo width $textarea\] - $pspace - \[winfo width $tagframe\] - \[winfo width $ex\] $iconwidth\]}"
- # bind $cmdlabel <Configure> "$cmdlabel configure -wraplength \[winfo width $cmdlabel\]"
- return $ci
- }
- ##########################################################################
- # Public tag management. add_data_tag is private
- proc Gronsole::set_click_command {path ci cmd} {
- variable _data
- set _data($path,$ci,clickCmd) $cmd
- }
- proc Gronsole::show_hide_tag_data {path ci tag} {
- variable _data
- set textarea $path.text
-
- set e [$textarea tag cget cmd$ci-$tag -elide]
- if {$e == {}} {
- $textarea tag configure cmd$ci-$tag -elide 1
- } else {
- $textarea tag configure cmd$ci-$tag -elide {}
- }
- }
- proc Gronsole::add_tag {path ci tag} {
- variable _data
- set textarea $path.text
- set frame $_data($path,$ci,frame)
- if {[lsearch -exact $_data($path,$ci,tags) $tag] != -1} {
- return
- }
- lappend _data($path,$ci,tags) $tag
- button $frame.cmdline.tags.tag$tag -text $tag -relief flat
- set icon [icon status $tag]
- if {$icon != 0} {
- $frame.cmdline.tags.tag$tag configure -image $icon
- }
- pack $frame.cmdline.tags.tag$tag -side right
- }
- # This is private:
- proc Gronsole::add_data_tag {path ci tag} {
- variable _data
- set textarea $path.text
- set frame $_data($path,$ci,frame)
- if {[lsearch -exact $_data($path,$ci,tags) $tag] != -1} {
- return
- }
- Gronsole::add_tag $path $ci $tag
- $frame.cmdline.tags.tag$tag configure -relief raised -command "Gronsole::show_hide_tag_data $path $ci $tag"
- }
- proc Gronsole::remove_tag {path ci tag} {
- variable _data
- set frame $_data($path,$ci,frame)
- pack forget $frame.cmdline.tags.tag$tag
- # destroy $frame.cmdline.tags.tag$tag
- }
- ##########################################################################
- # Private (stuff done when commands are run)
- # This procedure doesn't really seem necessary. I've left it in
- # in case there is something I'm missing (M. Barton 29 April 2007)
- proc Gronsole::progress {path ci percent} {
- variable _data
- if {[info exists _data($path,$ci,progress)]} {
- set _data($path,$ci,progress) $percent
- }
- if {[info exists _data($path,$ci,progressbar)]} {
- set pbar $_data($path,$ci,progressbar)
- }
- if {$percent == -1} {
- $pbar configure -height 1
- } else {
- $pbar configure -height 20
- }
- # it seems that there is a bug in ProgressBar and it is not always updated ->
- $pbar _modify
- }
- proc Gronsole::output_to_gronsole {path mark ci tags str} {
- set outtext $path.text
- set tagbase cmd$ci
- # Back out backspaces:
- if {0} {
- while {[set idx [string first "\b" $str]] != -1} {
- set last [expr $idx - 1]
- set str1 [string range $str 1 $last]
- set first [expr $idx + 1]
- set str [string range $str $first end]
- set pos [$outtext index "$mark - 1 chars"]
- $outtext delete $pos
- $outtext insert $mark $str1 $tags
- }
- }
- if { [regexp -- {^GRASS_INFO_([^(]+)\(([0-9]+),([0-9]+)\): (.+)$} $str match key message_pid message_id val rest] } {
- set lkey [string tolower $key]
- Gronsole::add_tag $path $ci $lkey
- set icon [icon status $lkey]
- if {$icon != 0} {
- $outtext image create $mark -image $icon
- # $outtext tag add $tagbase "$mark -1 char"
- }
- $outtext insert $mark $val $tagbase
- } elseif { [regexp -- {^GRASS_INFO_PERCENT: (.+)$} $str match val rest] } {
- if { $val > 0 && $val < 100} {
- set Gronsole::_data($path,$ci,progress) $val
- # Gronsole::progress $path $ci $val
- } else {
- # Gronsole::progress $path $ci -1
- set Gronsole::_data($path,$ci,progress) -1
- $outtext insert $mark "\n" $tags
- }
- } elseif { [regexp -- {^GRASS_INFO_END.+} $str match key rest] } {
- # nothing
- } else {
- $outtext insert $mark $str $tags
- }
- }
- proc Gronsole::readeof {path ci mark fh} {
- variable _data
- # This doesn't actually get the result
- set result [catch {close $fh} error_text]
- set _data($path,$ci,result) $result
- # if {$result == 0} {
- # Gronsole::add_tag $path $ci success
- # set donecmd $_data($path,$ci,successcmd)
- #} else {
- # Gronsole::add_tag $path $ci failure
- # set donecmd $_data($path,$ci,failurecmd)
- #}
- Gronsole::remove_tag $path $ci running
- }
- proc Gronsole::readout {path ci mark fh} {
- # global mingw
- set lines {}
-
- while {[gets $fh line] >= 0} {
- lappend lines $line
- }
-
- if {[llength $lines] != 0} {
- Gronsole::add_data_tag $path $ci out
- # if { $mingw == "1" } {
- # FIXME bug #606
- # Gronsole::output_to_gronsole $path $mark $ci [list cmd$ci cmd$ci-out] "\n"
- # }
- }
- foreach line $lines {
- Gronsole::output_to_gronsole $path $mark $ci [list cmd$ci cmd$ci-out] "$line\n"
- }
- $path.text see $mark
- }
- proc Gronsole::done_command {path ci} {
- variable _data
- if {[info exists _data($path,$ci,donecmd)] && $_data($path,$ci,donecmd) != {}} {
- set donecmd $_data($path,$ci,donecmd)
- set _data($path,$ci,donecmd) {}
- }
- if {[info exists donecmd] && $donecmd != {}} {
- eval $donecmd
- }
- }
- proc Gronsole::file_callback {path ci mark fh} {
- if [eof $fh] {
- Gronsole::readeof $path $ci $mark $fh
- Gronsole::done_command $path $ci
- } else {
- Gronsole::readout $path $ci $mark $fh
- }
- }
- proc Gronsole::execbg {path ci mark fh} {
- fconfigure $fh -blocking 0
- fileevent $fh readable [list Gronsole::file_callback $path $ci $mark $fh]
- }
- proc Gronsole::execwait {path ci mark fh} {
- while {! [eof $fh]} {
- Gronsole::readout $path $ci $mark $fh
- update
- }
- Gronsole::readeof $path $ci $mark $fh
- update
- }
- proc Gronsole::execout {path cmd ci execcmd} {
- global env
- set mark cmdinsert$ci
- # Actually run the program
- # |& grocat merges stdout and stderr because Tcl treats
- # anything written to stderr as an error condition
- set cmd [concat | $cmd |& $env(GISBASE)/etc/grocat]
- set message_env [exec g.gisenv get=GRASS_MESSAGE_FORMAT]
- set env(GRASS_MESSAGE_FORMAT) gui
- set ret [catch {open $cmd r} fh]
- set env(GRASS_MESSAGE_FORMAT) $message_env
- set _data($path,$ci,fh) $fh
- if { $ret } {
- Gronsole::remove_tag $path $ci running
- Gronsole::add_tag $path $ci error
- catch {close $fh}
- Gronsole::done_command $path $ci
- } {
- $execcmd $path $ci $mark $fh
- }
- update idletasks
- }
- ##########################################################################
- # Public interface for running commands
- proc Gronsole::annotate {path cmd tags} {
- variable _data
- set ci [Gronsole::create_command $path $cmd]
- foreach tag $tags {
- Gronsole::add_tag $path $ci $tag
- }
- $path.text yview end
-
- return $ci
- }
- proc Gronsole::annotate_text {path ci text} {
- Gronsole::output_to_gronsole $path cmdinsert$ci $ci [list cmd$ci cmd$ci-out] $text
- $path.text see cmdinsert$ci
- }
- proc Gronsole::run {path cmd tags donecmd} {
- variable _data
-
- set tags [concat running $tags]
- set ci [Gronsole::annotate $path $cmd $tags]
- set _data($path,$ci,donecmd) $donecmd
- Gronsole::execout $path $cmd $ci Gronsole::execbg
- return $ci
- }
- proc Gronsole::run_wait {path cmd tags} {
- set tags [concat running $tags]
- set ci [Gronsole::annotate $path $cmd $tags]
- Gronsole::execout $path $cmd $ci Gronsole::execwait
- }
- proc Gronsole::run_xterm {path cmd tags} {
- global env
- global mingw
- Gronsole::annotate $path $cmd [concat xterm $tags]
- if { $mingw == "1" } {
- eval [list exec -- cmd.exe /c start \
- $env(GISBASE)/etc/grass-run.bat ] $cmd &
- } else {
- eval [list exec -- $env(GISBASE)/etc/grass-xterm-wrapper \
- -name xterm-grass -e $env(GISBASE)/etc/grass-run.sh ] \
- $cmd &
- }
- update idletasks
- }
|