Commits

Ralph Bean committed 74d1267 Merge

Merge branch 'release/0.0.5'

Comments (0)

Files changed (8)

 
 setup(
     name='tw2.d3',
-    version='0.0.3',
+    version='0.0.5',
     description='toscawidgets2 wrapper for d3 (data-driven documents)',
     long_description=long_description,
     author='Ralph Bean',

tw2/d3/__init__.py

 
 from widgets import (
     BarChart,
+    TimeSeriesChart,
 )

tw2/d3/samples.py

 
 from widgets import (
     BarChart,
+    TimeSeriesChart,
 )
 
 import random
             self.attrs['id'],
             100
         ))
+
+class DemoTimeSeriesChart(TimeSeriesChart):
+    n = 200
+    data = [random.random() for i in range(n)]

tw2/d3/static/ext/bar.js

-
 if ( typeof tw2 == "undefined" ) tw2 = {};
 if ( typeof tw2.store == "undefined" ) tw2.store = {};
 if ( typeof tw2.d3 == "undefined" ) tw2.d3 = {};
                     tw2.store[selector].data[i].value += Math.random() * 3;
                 }
             }, interval);
-        }
+        },
+        index_of: function(selector, key) {
+            var index = 0;
+            for (var i = 0; i < tw2.store[selector].data.length; i++) {
+                if (tw2.store[selector].data[i].key == key) {
+                    return i;
+                }
+            }
+            return null;
+        },
+        set_value: function(selector, key, value) {
+            var index = tw2.d3.util.index_of(selector, key);
+            tw2.store[selector].data[index].value = value;
+        },
+        bump_value: function(selector, key, value) {
+            var index = tw2.d3.util.index_of(selector, key);
+            if (index == null) {
+                tw2.store[selector].data.push({'key': key, 'value': +value});
+            } else {
+                tw2.store[selector].data[index].value =
+                tw2.store[selector].data[index].value + 1;
+            }
+        },
     },
 
     bar: {

tw2/d3/static/ext/timeseries.css

+.x.axis line {
+  shape-rendering: auto;
+}
+
+.line {
+  fill: none;
+  stroke: #000;
+  stroke-width: 1.5px;
+}

tw2/d3/static/ext/timeseries.js

+if ( typeof tw2 == "undefined" ) tw2 = {};
+if ( typeof tw2.store == "undefined" ) tw2.store = {};
+if ( typeof tw2.d3 == "undefined" ) tw2.d3 = {};
+
+$.extend(tw2.d3, {
+    timeseries: {
+        init: function (
+            selector, data, _width, _height,
+            padding, interval, n, duration
+            ) {
+            $(document).ready(function() {
+                tw2.d3.timeseries._init(
+                    selector, data, _width, _height,
+                    padding, interval, n, duration
+                );
+            });
+        },
+        _init: function (
+            selector, data, _width, _height,
+            padding, interval, n, duration
+            ) {
+            var now = new Date(Date.now() - duration);
+            tw2.store[selector] = {value: 0};
+
+            var margin = {
+                top: padding[0],
+                right: padding[1],
+                bottom: padding[2],
+                left: padding[3],
+            };
+            var width = _width - margin.right;
+            var height = _height - margin.top - margin.bottom;
+
+            var x = d3.time.scale()
+            .domain([now - (n - 2) * duration, now - duration])
+            .range([0, width]);
+
+            var y = d3.scale.linear()
+            .range([height, 0]);
+
+            var line = d3.svg.line()
+            .interpolate("basis")
+            .x(function(d, i) { return x(now - (n - 1 - i) * duration); })
+            .y(function(d, i) { return y(d); });
+
+            var svg = d3.select("#" + selector).append("p").append("svg")
+            .attr("width", width + margin.left + margin.right)
+            .attr("height", height + margin.top + margin.bottom)
+            .style("margin-left", -margin.left + "px")
+            .append("g")
+            .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
+
+            svg.append("defs").append("clipPath")
+            .attr("id", "clip")
+            .append("rect")
+            .attr("width", width)
+            .attr("height", height);
+
+            var axis = svg.append("g")
+            .attr("class", "x axis")
+            .attr("transform", "translate(0," + height + ")")
+            .call(x.axis = d3.svg.axis().scale(x).orient("bottom"));
+
+            var path = svg.append("g")
+            .attr("clip-path", "url(#clip)")
+            .append("path")
+            .data([data])
+            .attr("class", "line");
+
+            tick();
+
+            function tick() {
+                // update the domains
+                now = new Date();
+                x.domain([now - (n - 2) * duration, now - duration]);
+                y.domain([0, d3.max(data)]);
+
+                // push the accumulated value onto the back, and reset the value
+                data.push(Math.min(30, tw2.store[selector].value));
+                tw2.store[selector].value = 0;
+
+                // redraw the line
+                svg.select(".line")
+                .attr("d", line)
+                .attr("transform", null);
+
+                // slide the x-axis left
+                axis.transition()
+                .duration(duration)
+                .ease("linear")
+                .call(x.axis);
+
+                // slide the line left
+                path.transition()
+                .duration(duration)
+                .ease("linear")
+                .attr("transform", "translate(" + x(now - (n - 1) * duration) + ")")
+                .each("end", tick);
+
+                // pop the old data point off the front
+                data.shift();
+            }
+        },
+    },
+});

tw2/d3/timeseries.css

+.x.axis line {
+  shape-rendering: auto;
+}
+
+.line {
+  fill: none;
+  stroke: #000;
+  stroke-width: 1.5px;
+}

tw2/d3/widgets.py

             self.padding,
             self.interval,
         ))
+
+
+class TimeSeriesChart(D3Widget):
+    resources = D3Widget.resources + [
+        twc.JSLink(modname=modname, filename="static/ext/timeseries.js"),
+        twc.CSSLink(modname=modname, filename="static/ext/timeseries.css"),
+    ]
+
+    data = twc.Param("An list of values in a timeseries.", default=None)
+    width = twc.Param("Width of the chart in pixels.", default=960)
+    height = twc.Param("Height of the chart in pixels.", default=120)
+    padding = twc.Param("A list of ints [top, right, bottom, left]",
+                        default=[6, 0, 20, 40])
+
+    interval = twc.Param("Redraw-interval in milliseconds.", default=0)
+    n = twc.Param('Number of buckets.', default=243)
+    duration = twc.Param(
+        'Number of seconds of data to display.', default=750)
+
+    def prepare(self):
+
+        # Check the types of everything
+        int(self.width)
+        int(self.height)
+        int(self.interval)
+        self.padding = [int(ele) for ele in self.padding]
+
+        if self.data == None:
+            raise ValueError("TimeSeriesChart must be provided a `data` dict")
+
+        if type(self.data) != list:
+            raise ValueError("TimeSeriesChart data must be of type `list`")
+
+        super(TimeSeriesChart, self).prepare()
+
+        self.add_call(twc.js_function('tw2.d3.timeseries.init')(
+            self.attrs['id'],
+            self.data,
+            self.width,
+            self.height,
+            self.padding,
+            self.interval,
+            self.n,
+            self.duration,
+        ))