| 1 | [[TOC]] |
| 2 | = Submitting TCL/TK Code (GRASS 6 ONLY) = |
| 3 | |
| 4 | When submitting TCL and TK SCRIPTS to GRASS SVN repository, |
| 5 | please take care of following rules: |
| 6 | |
| 7 | == Directory Structure == |
| 8 | |
| 9 | Use the directory structure to place your program appropriately into |
| 10 | the source tree |
| 11 | - general grass tcl/tk libraries and reusable code go into lib/gtcltk |
| 12 | - user interfaces go in gui/tcltk |
| 13 | - scripts go in scripts/ |
| 14 | Programs here must have a proper Makefile and description.html |
| 15 | |
| 16 | == Headers == |
| 17 | |
| 18 | Add a header section to the script you submit and make sure you include |
| 19 | the copyright. The purpose section is meant to contain a general |
| 20 | overview of the code in the file to assist other programmers that will |
| 21 | need to make changes to your code. |
| 22 | |
| 23 | Example (fictitious header for a script called r.myscript) : |
| 24 | |
| 25 | ############################################################################ |
| 26 | # |
| 27 | # MODULE: r.myscript |
| 28 | # AUTHOR(S): Me <email AT some domain> |
| 29 | # PURPOSE: Calculates univariate statistics from a GRASS raster map |
| 30 | # COPYRIGHT: (C) 2005 by the GRASS Development Team |
| 31 | # |
| 32 | # This program is free software under the GNU General Public |
| 33 | # License (>=v2). Read the file COPYING that comes with GRASS |
| 34 | # for details. |
| 35 | # |
| 36 | ############################################################################# |
| 37 | |
| 38 | The copyright protects your rights according to GNU General Public |
| 39 | License (www.gnu.org). |
| 40 | |
| 41 | == Comments == |
| 42 | |
| 43 | PLEASE take the time to add comments throughout your code explaining what |
| 44 | the code is doing. It will save a HUGE amount of time and frustration for |
| 45 | other programmers that need to change or understand your code in the future. |
| 46 | Many of the programmers working on grass are not heavily invested in tcl |
| 47 | and tk, so extra documentation and explanation are greatly appreciated. |
| 48 | |
| 49 | == Test Code == |
| 50 | |
| 51 | Test your program with tcl/tk 8.5. |
| 52 | |
| 53 | == Messages == |
| 54 | |
| 55 | Always use the gettext macros with [G_msg "..."] for user messages. |
| 56 | The string must be quoted using quotation marks, not braces, for |
| 57 | xgettext to find it. The string cannot include variable ($) or |
| 58 | command ([...]) substitutions. If you need substitutions use |
| 59 | [format ...]. |
| 60 | |
| 61 | Examples: |
| 62 | button .ok -text [G_msg "Ok"] |
| 63 | |
| 64 | set statusbartext [format [G_msg "Monitor %d running"] $monitor_number]] |
| 65 | |
| 66 | Use positional parameters if substitutions might be rearranged in another language: |
| 67 | |
| 68 | format [G_msg "We produced %1\$d units in location %2\$s"] $num $city |
| 69 | format [G_msg "In location %2\$s we produced %1\$d units"] $num $city |
| 70 | |
| 71 | == Environmental Variables == |
| 72 | |
| 73 | Use "GRASS_TCLSH" and "GRASS_WISH" environment variables instead of |
| 74 | "tclsh" and "wish" at the start of Tcl/Tk scripts. This allows users to |
| 75 | override the default names, so that developers don't need worry about the |
| 76 | shell names. |
| 77 | |
| 78 | Tcl script: |
| 79 | |
| 80 | #!/bin/sh |
| 81 | # the next line restarts using tclsh. Note the backslash: \ |
| 82 | exec $GRASS_TCLSH "$0" "$@" |
| 83 | |
| 84 | |
| 85 | Tk script: |
| 86 | |
| 87 | #!/bin/sh |
| 88 | # the next line restarts using wish. Note the backslash: \ |
| 89 | exec $GRASS_WISH "$0" "$@" |
| 90 | |
| 91 | === Global Variables === |
| 92 | |
| 93 | Avoid using global variables. Thay are a frequent source of bugs, make code |
| 94 | harder to understand, and make your program difficult to reuse. Additionally, |
| 95 | putting code into procs usually makes it run faster (it gets compiled). |
| 96 | |
| 97 | == Source Files == |
| 98 | |
| 99 | Do not source files that have already been sourced. |
| 100 | |
| 101 | gui.tcl sources: |
| 102 | options.tcl |
| 103 | select.tcl |
| 104 | gronsole.tcl |
| 105 | |
| 106 | If your code requires something to be sourced before it note so |
| 107 | in a comment at the top of the file. |
| 108 | |
| 109 | == Indentation == |
| 110 | |
| 111 | Set tabstops in your editor to 8 spaces. |
| 112 | When modifying files use the indentation style that is already present. |
| 113 | Please use consistent indentation style in your new code. Whether you use |
| 114 | tabs or spaces to indent please be consistent. Where tabs and spaces are |
| 115 | mixed please remember that a tab is 8 spaces. |
| 116 | |
| 117 | == User Interface == |
| 118 | |
| 119 | Use the tk options database to control the appearance of your user interface. |
| 120 | In general do not set options on tk widgets unless that option is truly |
| 121 | specific to that widget. It makes them harder to customize. |
| 122 | |
| 123 | Example: Don't set options like -foreground or -background or -font |
| 124 | when creating widgets, unless there's a really _really_ specific reason to |
| 125 | have it that color (like it's demonstrating that color). |
| 126 | |
| 127 | If you want something like a label to look different than everything else |
| 128 | of that class (other labels) give it a distinctive name, like |
| 129 | .moduletitlelabel . If you have a bunch of them give them all the same |
| 130 | distinctive name. This allows them to have their options controlled by the |
| 131 | options database. |
| 132 | |
| 133 | You can put your options at the start of your script (before creating any |
| 134 | widgets) like this: |
| 135 | option add *modultitlelabel.background red |
| 136 | More examples are in lib/gtcltk/options.tcl |
| 137 | |
| 138 | Many common options, like font, background and foreground colors, |
| 139 | highlighting, scrollbar colors, and help bubble appearance are controlled |
| 140 | by options.tcl. You can include it at the start of your script with: |
| 141 | source $env(GISBASE)/etc/gtcltk/options.tcl |
| 142 | |
| 143 | == Shell Wrapper == |
| 144 | |
| 145 | Use of GRASS commands in shell wrapper. |
| 146 | |
| 147 | Any GRASS program run in an xterm (those which do interactive query) needs |
| 148 | to use grass-run.sh, e.g.: |
| 149 | |
| 150 | exec -- $env(GISBASE)/etc/grass-xterm-wrapper -e $env(GISBASE)/etc/grass-run.sh g.proj ... |
| 151 | |
| 152 | You should probably also use "-T g.proj -n g.proj" to set the title |
| 153 | back (otherwise it will be "grass-run.sh", which isn't particularly |
| 154 | informative). |
| 155 | |
| 156 | The xterm will close as soon as the command completes (whether it |
| 157 | succeeds or fails). You can use the -hold switch to retain the xterm |
| 158 | window after the command completes, but you should only do that for |
| 159 | debugging; having to manually close the xterm window each time would |
| 160 | be annoying in normal use. Alternatively, redirect stdout/stderr to a |
| 161 | file, to catch any error messages. |
| 162 | |
| 163 | == Evaluation == |
| 164 | |
| 165 | Try to evaluate things only once. Tcl compiles the program to bytecode which |
| 166 | can be interpreted fairly quickly. If there are strings that must still be |
| 167 | evaluated tcl must parse and either compile or interpret them |
| 168 | each time they are encountered. In general this means put braces around |
| 169 | expressions and especially regular expressions (Tcl also compiles regular |
| 170 | expressions). Multiple evaluation can also lead to bugs. |
| 171 | |
| 172 | Expressions via expr command: |
| 173 | Slow: |
| 174 | set y [expr $a * $x + $b] |
| 175 | Fast: |
| 176 | set y [expr {$a * $x + $b}] |
| 177 | |
| 178 | Expressions in conditions: |
| 179 | Slow: |
| 180 | if [...] {... |
| 181 | Fast: |
| 182 | if {[...]} {... |
| 183 | |
| 184 | Regular expressions: |
| 185 | Very slow: |
| 186 | regex "x(\[0-9\]+).*not" $string trash number |
| 187 | Fast: |
| 188 | regex {x([0-9]+).*not} $string trash number |
| 189 | |
| 190 | If you really want speed: |
| 191 | If a regular expression needs to be constructed from variables but used |
| 192 | multiple times store it in a variable that will not be destroyed or |
| 193 | changed between reuse. Tcl stores the compiled regex with the variable. |
| 194 | |
| 195 | == Decompose Lists == |
| 196 | |
| 197 | You might want to decompose lists in a somewhat easy way: |
| 198 | |
| 199 | Difficult and slow: |
| 200 | # Make x1 y1 x2 y2 the first four things in the list |
| 201 | set list [commandMakesList] |
| 202 | set x1 [lindex $list 0] |
| 203 | set y1 [lindex $list 1] |
| 204 | set x2 [lindex $list 2] |
| 205 | set y2 [lindex $list 3] |
| 206 | |
| 207 | Easier and faster: |
| 208 | # Make x1 y1 x2 y2 the first four things in the list |
| 209 | foreach {x1 y1 x2 y2} [commandMakesList] {break} |
| 210 | |
| 211 | Be sure to include a comment as to what you are doing. |
| 212 | |
| 213 | == List Functions == |
| 214 | |
| 215 | Use the Tcl list functions (list, lappend etc) for manipulating lists. |
| 216 | |
| 217 | For example, use: |
| 218 | |
| 219 | set vals [list $foo $bar] |
| 220 | |
| 221 | rather than: |
| 222 | |
| 223 | set vals "$foo $bar" |
| 224 | |
| 225 | The former will always create a list with two elements, adding braces |
| 226 | if necessary, while the latter will split $foo and $bar into multiple |
| 227 | elements if they contain spaces. Additionally the first is faster |
| 228 | because tcl is not internally converting between strings and lists. |
| 229 | |
| 230 | A related issue is to remember that command lines (as used by exec and |
| 231 | open "|...") are lists. exec behaves like execl(), spawnl() etc, and |
| 232 | not like system(). |
| 233 | |
| 234 | Overlooking either of these points is likely to result in code which |
| 235 | fails when a command argument contains a space. |
| 236 | |
| 237 | == Tcl C Library == |
| 238 | |
| 239 | Tcl C library: |
| 240 | Memory allocated with Tcl_Alloc (such as the result of Tcl_Merge) |
| 241 | must be freed with Tcl_Free. This means that the ->freeProc of |
| 242 | an interpreter when returning a string using Tcl_Merge should be |
| 243 | TCL_DYNAMIC. Incorrectly freeing memory with glibc free will |
| 244 | cause segfaults in Tcl 8.4 and later. |
| 245 | |
| 246 | == SVN Properties == |
| 247 | |
| 248 | When submitting new files to the repository set SVN properties, |
| 249 | e.g. |
| 250 | |
| 251 | svn:executable : * |
| 252 | svn:mime-type : text/x-tcl |
| 253 | svn:keywords : Author Date Id |
| 254 | svn:eol-style : native |
| 255 | |
| 256 | See |
| 257 | http://svnbook.red-bean.com/en/1.4/svn.advanced.props.html |