An example of data objects

Arjen Markus 2010-02-01: In a discussion about my work on a LAPACK wrapper, Kevin Kenny mentioned Data Objects , by George Howlett, 1998, which are, roughly speaking, collections of data that have a set of operations connected to them. The concept differs from object-oriented objects, because data objects do not have inheritance and so on.

The program below illustrates how you can have operations on data performed automatically, whenever the data change. It simply connects two variables to a Plotchart plot and whenever the values of these variables change, the plot gets redrawn. Redrawing occurs when the event loop is run (so when the program is waiting for input from the user).

The idea is not at all limited to plotting the data, you can also store the data in a database, run a statistical analysis or whatever.

# binddata.tcl --
#     Simple example of using the concept of "data objects" as described
#     in the article by George Howlett.
#
#     Idea:
#     Variables are updated and this automatically provokes an action, such updating
#     the plot that is associated with them
#

package require Plotchart

# bindplotdata --
#     Bind variables to a Plotchart plot
#
# Arguments:
#     plot           The Plotchart plot they belong to
#     series         The series name
#     xvar           Global (!) variable for x values
#     yvar           Global (!) variable for y values
#
# Returns:
#     None
#
# Side effects:
#     The procedure creates a trace for these two variables
#
proc bindplotdata {plot series xvar yvar} {

    trace add variable ::$xvar write [list UpdatePlot $plot $series $xvar $yvar]
    trace add variable ::$yvar write [list UpdatePlot $plot $series $xvar $yvar]

}

# UpdatePlot --
#     Queue an after event for updating the plot if the data change
#
# Arguments:
#     plot           The Plotchart plot the data belong to
#     series         The series name
#     xvar           Global (!) variable for x values
#     yvar           Global (!) variable for y values
#     name1          Name of the changed variable (not used)
#     name2          Name of the element - if any (not used)
#     op             Operation (not used)
#
# Returns:
#     None
#
# Side effects:
#     An event is queued - it will fire when the program's event loop is run
#
proc UpdatePlot {plot series xvar yvar name1 name2 op} {

    global eventID

    if { [info exists eventID] } {
        after cancel $eventID
    }

    set eventID [after 0 RedrawData $plot $series $xvar $yvar]
}

# RedrawData --
#     Redraw the data
#
# Arguments:
#     plot           The Plotchart plot the data belong to
#     series         The series name
#     xvar           Global (!) variable for x values
#     yvar           Global (!) variable for y values
#
# Returns:
#     None
#
# Side effects:
#     All data in the plot is deleted first, so this works for one series only
#
proc RedrawData {plot series xvar yvar} {

    global notfirst
    upvar #0 $xvar xdata
    upvar #0 $yvar ydata

    set canvas [string range $plot 7 end]

    $canvas delete data

    if { [info exist notfirst] } {
        $plot plot $series {} {}
    } else {
        set notfirst 1 ;# Workaround for small glitch in Plotchart
    }


    foreach x $xdata y $ydata {
        $plot plot $series $x $y
    }
}

# main --
#     Test the procedure
#

pack [canvas .c -bg white -width 400 -height 400]
set p [::Plotchart::createXYPlot .c {-2 2 0.5} {-2 2 0.5}]

bindplotdata $p circle x y

set x {}
set y {}
set pi 3.1415926

for {set i 0} {$i <= 100} {incr i} {
    lappend x [expr {cos($pi*$i/50.0)}]
    lappend y [expr {sin($pi*$i/50.0)}]
}