SourceForge VA Linux Systems
Copyright © 2000 Paul Sheer - Click here for copying permissions       Source by FTP

next up previous contents index
Next: Index Up: Rute Users Tutorial and Previous: ADC Server Daemon   Contents   Index

Data Graphing Application

Add comments here 

 
 
 
 
5 
 
 
 
 
10 
 
 
 
 
15 
 
 
 
 
20 
 
 
 
 
25 
 
 
 
 
30 
 
 
 
 
35 
 
 
 
 
40 
 
 
 
 
45 
 
 
 
 
50 
 
 
 
 
55 
 
 
 
 
60 
 
 
 
 
65 
 
 
 
 
70 
 
 
 
 
75 
 
 
 
 
80 
 
 
 
 
85 
 
 
 
 
90 
 
 
 
 
95 
 
 
 
 
100 
 
 
 
 
105 
 
 
 
 
110 
 
 
 
 
115 
 
 
 
 
120 
 
 
 
 
125 
 
 
 
 
130 
 
 
 
 
135 
 
 
 
 
140 
 
 
 
 
145 
 
 
 
 
150 
 
 
 
 
155 
 
 
 
 
160 
 
 
 
 
165 
 
 
 
 
170 
 
 
 
 
175 
 
 
 
 
180 
 
 
 
 
185 
 
 
 
 
190 
 
 
 
 
195 
 
 
 
 
200 
 
 
 
 
205 
 
 
 
 
210 
 
 
 
 
215 
 
 
 
 
220 
 
 
 
 
225 
 
 
 
 
230 
 
 
 
 
235 
 
 
 
 
240 
 
 
 
 
245 
 
 
 
 
250 
 
 
 
 
255 
 
 
 
 
260 
 
 
 
 
265 
 
 
 
 
270 
 
 
 
 
275 
 
 
 
 
280 
 
 
 
 
285 
 
 
 
 
290 
 
 
 
 
295 
 
 
 
 
300 
 
 
 
 
305 
 
 
 
 
310 
 
 
 
 
315 
 
 
 
 
320 
 
 
 
 
325 
 
 
 
 
330 
 
 
 
 
335 
 
 
 
 
340 
 
 
 
 
345 
 
 
 
 
350 
 
 
 
 
355 
 
 
 
 
360 
 
 
 
 
365 
 
 
 
 
370 
 
 
 
 
375 
 
 
 
 
380 
 
 
 
 
385 
 
 
 
 
390 
 
 
 
 
395 
 
 
 
 
400 
 
 
 
 
405 
 
 
 
 
410 
 
 
 
 
415 
 
 
 
 
420 
 
 
 
 
425 
 
 
 
 
430 
 
 
 
 
435 
 
 
 
 
440 
 
 
 
 
445 
 
 
 
 
450 
 
 
 
 
455 
 
 
 
 
460 
 
 
 
 
465 
 
 
 
 
470 
 
 
 
 
475 
 
 
 
 
480 
 
 
 
 
485 
 
 
 
 
490 
 
 
#!/usr/bin/python

#was:
## Author: Jesper Skov <jskov@cygnus.co.uk>
## A rewite of the C canvas example in the GNOME Developer's Information

import GDK
from gtk import *
from gnome.ui import *
from _gnomeuimodule import *
import gnome.affine
import GdkImlib
import sys, string, socket

server = "127.0.0.1"
port = 1928

initial_time_scale = 5.0
initial_weight_scale = 5.0

GTK_CAULDRON_TOPLEVEL     = 1
GTK_CAULDRON_IGNOREENTER        = 1 << 8
GTK_CAULDRON_GRAB            = 1 << 9

class timer:
    def __init__ (self, func, freq = 1.0, *args):
        if type (freq) == type (1):
            freq = float (freq)
        if type (freq) != type (1.0):
            raise TypeError, "frequency must be a float"
        if type (func) != type (self.__init__):
            raise TypeError, "second arg must be a function"
        self.func = func
        self.args = args
        self.time = 0.0
        self.freq = freq
        self.timer = timeout_add (int (1000.0 / self.freq), self)

    def __call__ (self, *args):
        self.time = self.time + 1.0 / self.freq;
        return apply (self.func, (self.time,) + self.args)

    def stop (self):
        if not self.timer:
            return
        timeout_remove (self.timer)
        self.timer = None

    def start (self):
        if self.timer:
            return
        self.timer = timeout_add (int (1000.0 / self.freq), self)

    def reset (self):
        self.time = 0.0

    def __del__ (self):
        timeout_remove (self.timer)


class CanvasExample:
    def __init__(self, channel):
        self.width = 400
        self.height = 400
        self.channel = []
        self.numlines = len (channel)
        for i in xrange (0, self.numlines):
            self.channel.append (string.atoi (str (channel[i])))

    def configure (self, widget, event):
        if event.type == GDK.CONFIGURE:
            self.grid.affine_relative (gnome.affine.scale (float (event.width) / float (self.width), \
                            float (event.height) / float (self.height)))
            self.height = event.height
            self.width = event.width
            self.canvas.set_scroll_region (0, 0, self.width, self.height)
            self.line_redraw ()
        return FALSE

    def get_y_pos (self, line, y):
        if y < 0.0:
            y = 0.0
        shift = (int (line) / 2) * (int (self.height) / 10)
        return float (self.height) - 1.0 - (y * (float (self.height) / self.weight_scale) + shift)

    def get_weight (self, i, voltage):
        return self.channel_gradient[i] * voltage + self.channel_offset[i]

    def save_line (self, line):
        win = GtkFileSelection ("Save Graph")
        def delete_event (win, event = None):
            win.destroy ()
        win.connect ("delete_event", delete_event)
        def file_selection_ok(_button, fs, line):
            try:
               f = open (fs.get_filename (), "w+")
               for x, y in line:
                   f.write ("%.2f\t%.3f\n" % (x, y))
               f.close ()
            except:
               gtk_dialog_cauldron ("Error", GTK_CAULDRON_TOPLEVEL | GTK_CAULDRON_GRAB,
                   " ( [[(Error writing to file)xf]xf]xf )f / ( [%L]xf )f / ( %Bgxfrq | )f ", \
                            (str (sys.exc_value), "Button_Cancel"))
            fs.destroy ()
        win.ok_button.connect ("clicked", file_selection_ok, win, line)
        win.cancel_button.connect ("clicked", win.hide)
        win.show ()

    def line_clicked (self, line):
        x = gtk_dialog_cauldron ("Select", GTK_CAULDRON_TOPLEVEL | GTK_CAULDRON_GRAB,
            " %Bgxfrq // %Bgxfrq ", ("Save", "Button_Cancel"))
        if x == "Save":
            self.save_line (line)
        return TRUE

    def bold_event (self, widget, event):
        if event.type == GDK.ENTER_NOTIFY:
            widget.set (font = '-adobe-helvetica-bold-r-normal--14-140-75-75-p-77-iso8859-1')
            return TRUE

        elif event.type == GDK.LEAVE_NOTIFY:
            widget.set (font = '-adobe-helvetica-medium-r-normal--14-140-75-75-p-77-iso8859-1')
            return TRUE

        return FALSE

    def reset (self):
            try:
               self.timer.reset ()
            except:
               pass
            self.last_redraw = 0.0
            self.time_scale = initial_time_scale
            self.weight_scale = initial_weight_scale
            self.lines = []
            for i in xrange (0, self.numlines):
               self.lines.append ([(0.0, 0.0)])
               self.channel_socket[i].send ("reset");

            self.weight_division = 2
            self.time_division = 2
            self.line_redraw ()

    def reset_event (self, widget, event):
        if event.type == GDK.BUTTON_PRESS:
            self.reset ()
        return self.bold_event (widget, event)

    def start_event (self, widget, event):
        if event.type == GDK.BUTTON_PRESS:
            for i in xrange (0, self.numlines):
               self.channel_socket[i].send ("reset");
            try:
               self.timer.start ()
            except:
               self.timer = timer (self.next_line, self.freq)
            self.line_redraw ()
            return TRUE
        return self.bold_event (widget, event)

    def freq_event (self, widget, event):
        if event.type == GDK.BUTTON_PRESS:
            x = gtk_dialog_cauldron ("Set Frequency", GTK_CAULDRON_TOPLEVEL,
    " ( [(Enter the new sampling frequency:)]xf )f / ( %Ngxf )f / ( %Bgxfrq | %Bgxfq )f ", \
                    (str (self.freq), "datarecord-freq", "Frequency", "Button_Ok", "Button_Cancel"))
            if not x:
               return TRUE
            if x[0] != "Button_Ok" and x[0] != "GTK_CAULDRON_ENTER":
               return TRUE
            self.freq = float (x[1])
            try:
               self.timer.stop ()
            except:
               pass
            self.timer = None
            self.reset ()
            self.line_redraw ()
            return TRUE
        return self.bold_event (widget, event)

    def quit_event (self, widget, event):
        if event.type == GDK.BUTTON_PRESS:
            self.win.destroy ()
            return TRUE
        return self.bold_event (widget, event)

    def stop_event (self, widget, event):
        if event.type == GDK.BUTTON_PRESS:
            try:
               self.timer.stop ()
            except:
               pass
            self.line_redraw ()
            return TRUE
        return self.bold_event (widget, event)

# saves all channels
    def save_event (self, widget, event):
        if event.type == GDK.BUTTON_PRESS:
               win = GtkFileSelection ("Save All Graphs")
               def delete_event (win, event = None):
                   win.destroy ()
               win.connect ("delete_event", delete_event)
               def file_selection_ok(_button, fs, lines):
                   try:
                     f = open (fs.get_filename (), "w+")
                     for i in xrange (0, len (lines[0])):
                         for j in xrange (0, len (lines)):
                          f.write ("%.2f\t%.3f\t\t" % lines[j][i])
                         f.write ("\n")
                     f.close ()
                   except:
                     gtk_dialog_cauldron ("Error", GTK_CAULDRON_TOPLEVEL | GTK_CAULDRON_GRAB,
                         " ( [[(Error writing to file)xf]xf]xf )f / ( [%L]xf )f / ( %Bgxfrq | )f ", \
                                                                (str (sys.exc_value), "Button_Cancel"))
                   fs.destroy ()
               win.ok_button.connect ("clicked", file_selection_ok, win, self.lines)
               win.cancel_button.connect ("clicked", win.hide)
               win.show ()
               self.line_redraw ()
               return TRUE

        return self.bold_event (widget, event)

    def line_event (self, widget, event, line):
        if event.type == GDK.BUTTON_PRESS:
            self.line_clicked (line)
            return TRUE

        elif event.type == GDK.ENTER_NOTIFY:
            widget.set (width_units = 3)
            return TRUE

        elif event.type == GDK.LEAVE_NOTIFY:
            widget.set (width_units = 1)
            return TRUE

        return FALSE

    def line_redraw (self):
        i = 0
        for line in self.lines:
            p = []
            # do not draw every point to reduce CPU load
            step = 1 + len (line) / 100
            j = 0
            for k in xrange (0, len (line) - step + 1, step):
               y = 0.0
               for xt, yt in line[k:k + step]:
                   x = xt
                   y = y + yt
               y = y / float (step)
               p.append (x * float (self.width / 2) / self.time_scale + float ((self.width / 2) * (i % 2)))
               p.append (self.get_y_pos (i, y))
               j = j + 1
            if len (p) == 2:
               p = p + p
            xs = p[-2]
            # FIXME: 80 thumb suck value -->
            if xs < 80:
               xs = 80
            if self.channel_gradient[i] == 1.0:
               unit = "V"
            else:
               unit = "g"
            if self.canvas_lines[i]:
               self.canvas_text[i].set (text="%d(%.2f, %.3f%s)" % (i + 1, line[-1][0], \
                                                            line[-1][1], unit), x = xs, y = p[-1])
               self.canvas_lines[i].set (points = tuple (p))
            else:
               self.canvas_text[i] = self.canvas.root ().add ('text', text="%d(%.2f, %.3f%s)" \
                                                        % (i + 1, line[-1][0], line[-1][1], unit), 
                       x = xs, y = p[-1],
                       font = '-adobe-helvetica-bold-r-normal--10-100-75-75-p-60-iso8859-1',
                       anchor = ANCHOR_SOUTH_EAST,
                       fill_color = 'black')
               self.canvas_lines[i] = self.canvas.root ().add ('line', points=p, \
                                    width_pixels=1.0, fill_color=self.color[i])
               self.canvas_lines[i].connect ("event", self.line_event, line)
            i = i + 1

    def read_channel (self, i):
        self.channel_socket[i].send ("%d, 20" % (self.channel[i],));
        x = string.split (self.channel_socket[i].recv (1024), ",");
        return (string.atof (x[0]), 5.0 * string.atof (x[1]) / 2048.0)

    def read_channel_weight (self, i):
        t, y = self.read_channel (i)
        return (t, y * self.channel_gradient[i] + self.channel_offset[i])

    def calibrate (self, widget, event):
        if event.type == GDK.BUTTON_PRESS:
            import time
            Sxx = 0.0
            Sxy = 0.0
            Sx = 0.0
            Sy = 0.0
            n = 0
            v = gtk_dialog_cauldron ("Calibrate", GTK_CAULDRON_TOPLEVEL,
               " ( [[(Enter line to calibrate  1 to " + str(self.numlines) + \
                ":)xf]xf]xf )f / ( %Nxf )f / ( %Bgxfrq || %Bgxfq )f ", ("1", "Button_Ok", "Button_Cancel"))
            if not v:
               return TRUE
            if v[0] != "Button_Ok" and v[0] != "GTK_CAULDRON_ENTER":
               return TRUE
            try:
               line = int (v[1]) - 1
            except:
               return TRUE
            if line < 0 or line >= self.numlines:
               return TRUE
            while 1:
               v = gtk_dialog_cauldron ("Calibrate", GTK_CAULDRON_TOPLEVEL,
                   " ( [[(Apply weight " + str (n) + ", enter its value in grams, and wait for 1 " \
                        "second:)xf]xf]xf )f / ( %Ngxf )f / ( %Bxfrq || %Bxfrq || %Bgxfq )f ", \
                        ("0.0", "datarecord-cal", "Weight", "Done", "Enter Value", "Button_Cancel"))
               if not v:
                   break
               if v[0] != "Done" and v[0] != "GTK_CAULDRON_ENTER" and v[0] != "Enter Value":
                   break
               if n > 1 and v[0] == "Done":
                   break
               x = 0.0
               for i in xrange (10):
                   time.sleep (0.1)
                   x = x + self.read_channel (line)[1]
               x = x / 10.0
               y = float (v[1])
               Sxx = Sxx + x * x
               Sx = Sx + x
               Sy = Sy + y
               Sxy = Sxy + x * y
               n = n + 1
            m = (float (n) * Sxy - Sx * Sy) / (float (n) * Sxx - Sx * Sx)
            c = (Sy - m * Sx) / float (n)
            self.channel_gradient[line] = m
            self.channel_offset[line] = c
            return TRUE
        return self.bold_event (widget, event)

    def next_line (self, time, *args):
        i = 0
        for line in self.lines:
            x = self.read_channel_weight (i)
            time = x[0]
            line.append (x)
            if time > self.time_scale:
               if self.time_division == 5:
                   self.time_scale = self.time_scale * 5.0
                   self.time_division = 2
               else:
                   self.time_scale = self.time_scale * 2.0
                   self.time_division = 5
            if self.get_y_pos (i, line[-1][1]) < 0:
               if self.weight_division == 5:
                   self.weight_scale = self.weight_scale * 5.0
                   self.weight_division = 2
               else:
                   self.weight_scale = self.weight_scale * 2.0
                   self.weight_division = 5
            i = i + 1
# if five seconds have past, or five pixels have advanced, then redraw:
# we don't want to redraw too often, 'cos it slows the machine down with long lines.
        if time * float (self.width / 2) / self.time_scale > \
               self.last_redraw * float (self.width / 2) / self.time_scale + 5.0 \
               or time > self.last_redraw + 5.0:
            self.last_redraw = time
            self.line_redraw ()
        return TRUE

    def main (self):
        # Open window to hold canvas.
        self.win = GtkWindow ()
        self.win.connect ('destroy', mainquit)
        self.win.set_title ('Canvas Example')

        # Create VBox to hold canvas and buttons.
        vbox = GtkVBox ()
        self.win.add (vbox)
        vbox.show ()

        # Create canvas.
        self.canvas = GnomeCanvas ()
        self.canvas.set_usize (self.width, self.height)
        self.canvas.set_scroll_region (0,0, self.width, self.height)
        vbox.pack_start (self.canvas, expand=TRUE, fill=TRUE)
        self.canvas.show ()

        self.color = ('blue', 'blue', 'red', 'red', 'yellow', 'yellow', 'cyan', 'cyan', 'blue', 'blue', \
                    'red', 'red', 'yellow', 'yellow', 'cyan', 'cyan', 'blue', 'blue', 'red', 'red', 'yellow', \
                    'yellow', 'cyan', 'cyan', 'blue', 'blue', 'red', 'red', 'yellow', 'yellow', 'cyan', 'cyan', )

        self.grid = self.canvas.root ().add ('group')

        for i in xrange (0, 50):
            if not i % 5:
               continue
            self.grid.add ('line', points = (i * self.width / 50, 0, i * self.width / 50, self.height),
                 fill_color='gray',
                 width_pixels = 1)
            self.grid.add ('line', points = (0, i * self.height / 50, self.width, i * self.height / 50),
                 fill_color='gray',
                 width_pixels = 1)

        for i in xrange (0, 10):
            self.grid.add ('line', points = (i * self.width / 10, 0, i * self.width / 10, self.height),
                 fill_color='black',
                 width_pixels = 1)
            self.grid.add ('line', points = (0, i * self.height / 10, self.width, i * self.height / 10),
                 fill_color='black',
                 width_pixels = 1)

        self.button_start = self.canvas.root ().add ('text', text="Start",
                       x = 5, y = 5,
                       font = '-adobe-helvetica-medium-r-normal--14-140-75-75-p-77-iso8859-1',
                       anchor = ANCHOR_NORTH_WEST,
                       fill_color = 'red')
        self.button_start.connect ("event", self.start_event)

        self.button_stop = self.canvas.root ().add ('text', text="Stop",
                       x = 5, y = 25,
                       font = '-adobe-helvetica-medium-r-normal--14-140-75-75-p-77-iso8859-1',
                       anchor = ANCHOR_NORTH_WEST,
                       fill_color = 'red')
        self.button_stop.connect ("event", self.stop_event)

        self.button_reset = self.canvas.root ().add ('text', text="Reset",
                       x = 5, y = 45,
                       font = '-adobe-helvetica-medium-r-normal--14-140-75-75-p-77-iso8859-1',
                       anchor = ANCHOR_NORTH_WEST,
                       fill_color = 'red')
        self.button_reset.connect ("event", self.reset_event)

        self.button_save = self.canvas.root ().add ('text', text="Save All",
                       x = 5, y = 65,
                       font = '-adobe-helvetica-medium-r-normal--14-140-75-75-p-77-iso8859-1',
                       anchor = ANCHOR_NORTH_WEST,
                       fill_color = 'red')
        self.button_save.connect ("event", self.save_event)

        self.button_save = self.canvas.root ().add ('text', text="Set Frequency",
                       x = 5, y = 85,
                       font = '-adobe-helvetica-medium-r-normal--14-140-75-75-p-77-iso8859-1',
                       anchor = ANCHOR_NORTH_WEST,
                       fill_color = 'red')
        self.button_save.connect ("event", self.freq_event)

        self.button_reset = self.canvas.root ().add ('text', text="Calibrate",
                       x = 5, y = 105,
                       font = '-adobe-helvetica-medium-r-normal--14-140-75-75-p-77-iso8859-1',
                       anchor = ANCHOR_NORTH_WEST,
                       fill_color = 'red')
        self.button_reset.connect ("event", self.calibrate)

        self.button_save = self.canvas.root ().add ('text', text="Quit",
                       x = 5, y = 125,
                       font = '-adobe-helvetica-medium-r-normal--14-140-75-75-p-77-iso8859-1',
                       anchor = ANCHOR_NORTH_WEST,
                       fill_color = 'red')
        self.button_save.connect ("event", self.quit_event)

        self.last_redraw = 0.0
        self.time_scale = initial_time_scale
        self.weight_scale = initial_weight_scale
        self.lines = []
        self.canvas_lines = []
        self.canvas_text = []
        self.freq = 10.0
        self.channel_socket = []
        self.channel_offset = []
        self.channel_gradient = []

        for i in xrange (0, self.numlines):
            s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
            s.connect ((server, port))
            self.channel_socket.append (s)
            self.lines.append ([(0.0, 0.0)])
            self.canvas_lines.append (0)
            self.canvas_text.append (0)
            self.channel_offset.append (0.0)
            self.channel_gradient.append (1.0)

        self.weight_division = 2
        self.time_division = 2

        self.win.show ()
        self.win.connect ("event", self.configure)

if __name__ == '__main__':
    c = CanvasExample (sys.argv[1:])
    c.main ()
    mainloop ()



Paul Sheer 2000-10-07