BLT - graph - how to draw a sophisticated time axis

Example of how to set up a horizontal time axis on a BLT graph labelled both with dates and intermediate hour markers, and with the dates centralized with respect to the hours. Execute in wish, and it should look like this:

http://www.crossrad.uklinux.net/blt_pdw_190602.png

The ideas used here are:

  • Create custom tick labels with easy to read time formats using "clock scan" and "clock format".
  • The custom tick labels may contain \n to create multi-line labels.
  • Labels may be centralized between ticks by shifting them to the right with spaces.
  • The number of spaces may be calculated by using the BLT "axis transform" method to get the distance between the ticks in pixels, and using "font measure" to convert this to a number of spaces.
  • This should work with all fonts, because the actual font used is queried with "axis cget" and passed to "font measure".

Version D - Paul Welton - 20.06.2002


package require BLT

pack [blt::graph .g -width 10i]

#  Proc passed as a callback to BLT to draw custom tick labels.
#
proc format_timeAxis_tick {win seconds} {
    set hour [clock format $seconds -format "%H"]
    regsub {^0*} $hour {} label
    if { $label eq "" } {
        set label 0
    }
    if {$label} {
        return $label
    } else {
        return "$label\n[string repeat { } $::nSpaces]\
                [clock format $seconds -format "%d/%m"]"
    }
}

#  Construct a list of major tick positions in seconds - the
#  month, year and the range of days can be varied to suit
#  the application.
#
for {set day 20} {$day <= 23} {incr day} {
    foreach hours {0 4 8 12 16 20} {
        lappend majorticks [clock scan "3/$day/2001 $hours:00"]
    }
}
lappend majorticks [clock scan "3/$day/2001 00:00"]

#  Create the graph.
.g axis configure x                            \
        -min          [lindex $majorticks 0]   \
        -max          [lindex $majorticks end] \
        -title        "Day"                    \
        -majorticks   $majorticks

#  Need to do an update to display the graph before the
#  distance can be measured.
update idletasks

#  Measure the width of a day on the graph - the example
#  dates need not be in the displayed range.
set dayFieldWidth [expr {
        [.g axis transform x [clock scan 3/2/2001]] -
        [.g axis transform x [clock scan 3/1/2001]]}]

#  Work out how many spaces this corresponds to in the
#  font for the tick labels.
set nSpaces [expr {$dayFieldWidth /
                   [font measure [.g axis cget x -tickfont] " "]}]

#  Configure the axis to use the custom label command.
.g axis configure x -command format_timeAxis_tick

TV Looks cool.


Also works with Refactored BLT Components.

Some minor changes are needed. Do not import the rbc::* commands. The vector needs a subcommand create.

package require rbc
# namespace import rbc::*

# vector and stripchart are blt components.
# if you have a vector v, you can update it in realtime with
# v set $list

# init the vectors to a fixed size.

set Hz 200

rbc::vector create xvec($Hz) vector y1vec($Hz) vector y2vec($Hz)

# fill xvec with 0 .. $Hz-1

xvec seq 0 [expr {$Hz - 1}]

rbc::stripchart .s1 -height 2i -width 8i -bufferelements no
rbc::stripchart .s2 -height 2i -width 8i -bufferelements no

See also BLT