summaryrefslogtreecommitdiff
path: root/bin/pychart/area.py
diff options
context:
space:
mode:
authorFabien Pinckaers <fp@tinyerp.com>2009-03-15 20:07:01 +0100
committerFabien Pinckaers <fp@tinyerp.com>2009-03-15 20:07:01 +0100
commit81ed36a9f2e28ae426b07f4dbf4bf444728b2166 (patch)
treef68937f7873228898c56d2808de59594b8e057e1 /bin/pychart/area.py
parent4ab081becaa3822c8965933603924504694f5c11 (diff)
Adding Pychart due to bug in recent pychart versions
bzr revid: fp@tinyerp.com-20090315190701-996gxy21rdr47a8c
Diffstat (limited to 'bin/pychart/area.py')
-rw-r--r--bin/pychart/area.py249
1 files changed, 249 insertions, 0 deletions
diff --git a/bin/pychart/area.py b/bin/pychart/area.py
new file mode 100644
index 00000000000..a0e76876ec0
--- /dev/null
+++ b/bin/pychart/area.py
@@ -0,0 +1,249 @@
+#
+# Copyright (C) 2000-2005 by Yasushi Saito (yasushi.saito@gmail.com)
+#
+# Jockey is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2, or (at your option) any
+# later version.
+#
+# Jockey is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+import coord
+import line_style
+import legend
+import axis
+import pychart_util
+import chart_object
+import fill_style
+import canvas
+import area_doc
+import linear_coord
+import category_coord
+import theme
+from pychart_types import *
+from types import *
+
+_dummy_legend = legend.T()
+
+def range_doc(t):
+ u = t.upper()
+
+ return """Specifies the range of %s values that are displayed in the
+ chart. IF the value is None, both the values are computed
+ automatically from the samples. Otherwise, the value must be a
+ tuple of format (MIN, MAX). MIN and MAX must be either None or a
+ number. If None, the value is computed automatically from the
+ samples. For example, if %s_range = (None,5), then the minimum %s
+ value is computed automatically, but the maximum %s value is fixed
+ at 5.""" % (u, t, u, u)
+
+_keys = {
+ "loc" : (CoordType, (0,0),
+ """The location of the bottom-left corner of the chart.
+@cindex chart location
+@cindex location, chart
+"""),
+ "size" : (CoordType, (120,110),
+ """The size of the chart-drawing area, excluding axis labels,
+ legends, tick marks, etc.
+@cindex chart size
+@cindex size, chart
+ """),
+ "bg_style": (fill_style.T, None, "Background fill-pattern."),
+ "border_line_style": (line_style.T, None, "Line style of the outer frame of the chart."),
+ "x_coord":
+ (coord.T, linear_coord.T(),
+ """Set the X coordinate system.""",
+ """A linear coordinate system."""),
+ "y_coord": (coord.T, linear_coord.T(),
+ "Set the Y coordinate system.",
+ """A linear coordinate system."""),
+ "x_range": (CoordType, None, range_doc("x")),
+ "y_range": (CoordType, None, range_doc("y")),
+ "x_axis": (axis.X, None, "The X axis. <<axis>>."),
+ "x_axis2": (axis.X, None, """The second X axis. This axis should be non-None either when you want to display plots with two distinct domains or when
+ you just want to display two axes at the top and bottom of the chart.
+ <<axis>>"""),
+ "y_axis": (axis.Y, None, "The Y axis. <<axis>>."),
+ "y_axis2": (axis.Y, None,
+ """The second Y axis. This axis should be non-None either when you want to display plots with two distinct ranges or when
+ you just want to display two axes at the left and right of the chart. <<axis>>"""),
+ "x_grid_style" : (line_style.T, None,
+ """The style of horizontal grid lines.
+@cindex grid lines"""),
+ "y_grid_style" : (line_style.T, line_style.gray70_dash3,
+ "The style of vertical grid lines."),
+ "x_grid_interval": (IntervalType, None,
+ """The horizontal grid-line interval.
+ A numeric value
+ specifies the interval at which
+ lines are drawn. If value is a function, it
+ takes two arguments, (MIN, MAX), that tells
+ the minimum and maximum values found in the
+ sample data. The function should return a list
+ of values at which lines are drawn."""),
+ "y_grid_interval": (IntervalType, None,
+ "The vertical grid-line interval. See also x_grid_interval"),
+ "x_grid_over_plot": (IntType, False,
+ "If True, grid lines are drawn over plots. Otherwise, plots are drawn over grid lines."),
+ "y_grid_over_plot": (IntType, False, "See x_grid_over_plot."),
+ "plots": (ListType, pychart_util.new_list,
+ """Used only internally by pychart."""),
+ "legend": (legend.T, _dummy_legend, "The legend of the chart.",
+ """a legend is by default displayed
+ in the right-center of the chart."""),
+ }
+
+
+class T(chart_object.T):
+ keys = _keys
+ __doc__ = area_doc.doc
+##AUTOMATICALLY GENERATED
+
+##END AUTOMATICALLY GENERATED
+ def x_pos(self, xval):
+ "Return the x position (on the canvas) corresponding to XVAL."
+ off = self.x_coord.get_canvas_pos(self.size[0], xval,
+ self.x_range[0], self.x_range[1])
+ return self.loc[0] + off
+
+ def y_pos(self, yval):
+ "Return the y position (on the canvas) corresponding to YVAL."
+ off = self.y_coord.get_canvas_pos(self.size[1], yval,
+ self.y_range[0], self.y_range[1])
+ return self.loc[1] + off
+
+ def x_tic_points(self, interval):
+ "Return the list of X values for which tick marks and grid lines are drawn."
+ if type(interval) == FunctionType:
+ return apply(interval, self.x_range)
+
+ return self.x_coord.get_tics(self.x_range[0], self.x_range[1], interval)
+ def y_tic_points(self, interval):
+ "Return the list of Y values for which tick marks and grid lines are drawn."
+ if type(interval) == FunctionType:
+ return apply(interval, self.y_range)
+
+ return self.y_coord.get_tics(self.y_range[0], self.y_range[1], interval)
+ def __draw_x_grid_and_axis(self, can):
+ if self.x_grid_style:
+ for i in self.x_tic_points(self.x_grid_interval):
+ x = self.x_pos(i)
+ if x > self.loc[0]:
+ can.line(self.x_grid_style,
+ x, self.loc[1], x, self.loc[1]+self.size[1])
+ if self.x_axis:
+ self.x_axis.draw(self, can)
+ if self.x_axis2:
+ self.x_axis2.draw(self, can)
+ def __draw_y_grid_and_axis(self, can):
+ if self.y_grid_style:
+ for i in self.y_tic_points(self.y_grid_interval):
+ y = self.y_pos(i)
+ if y > self.loc[1]:
+ can.line(self.y_grid_style,
+ self.loc[0], y,
+ self.loc[0]+self.size[0], y)
+ if self.y_axis:
+ self.y_axis.draw(self, can)
+ if self.y_axis2:
+ self.y_axis2.draw(self, can)
+
+ def __get_data_range(self, r, which, coord, interval):
+ if isinstance(coord, category_coord.T):
+ # This info is unused for the category coord type.
+ # So I just return a random value.
+ return ((0,0), 1)
+
+ r = r or (None, None)
+
+ if len(self.plots) == 0:
+ raise ValueError, "No chart to draw, and no data range specified.\n";
+ dmin, dmax = 999999, -999999
+
+ for plot in self.plots:
+ this_min, this_max = plot.get_data_range(which)
+ dmin = min(this_min, dmin)
+ dmax = max(this_max, dmax)
+
+ if interval and type(interval) == FunctionType:
+ tics = apply(interval, (dmin, dmax))
+ dmin = tics[0]
+ dmax = tics[len(tics)-1]
+ else:
+ dmin, dmax, interval = coord.get_min_max(dmin, dmax, interval)
+
+ if r[0] != None:
+ dmin = r[0]
+ if r[1] != None:
+ dmax = r[1]
+ return ((dmin, dmax), interval)
+ def draw(self, can = None):
+ "Draw the charts."
+
+ if can == None:
+ can = canvas.default_canvas()
+
+ self.type_check()
+ for plot in self.plots:
+ plot.check_integrity()
+
+ self.x_range, self.x_grid_interval = \
+ self.__get_data_range(self.x_range, 'X',
+ self.x_coord,
+ self.x_grid_interval)
+
+ self.y_range, self.y_grid_interval = \
+ self.__get_data_range(self.y_range, 'Y',
+ self.y_coord,
+ self.y_grid_interval)
+
+ can.rectangle(self.border_line_style, self.bg_style,
+ self.loc[0], self.loc[1],
+ self.loc[0] + self.size[0], self.loc[1] + self.size[1])
+
+ if not self.x_grid_over_plot:
+ self.__draw_x_grid_and_axis(can)
+
+ if not self.y_grid_over_plot:
+ self.__draw_y_grid_and_axis(can)
+
+ clipbox = theme.adjust_bounding_box([self.loc[0], self.loc[1],
+ self.loc[0] + self.size[0],
+ self.loc[1] + self.size[1]])
+
+ can.clip(clipbox[0], clipbox[1],
+ clipbox[2], clipbox[3])
+
+ for plot in self.plots:
+ plot.draw(self, can)
+
+ can.endclip()
+
+ if self.x_grid_over_plot:
+ self.__draw_x_grid_and_axis(can)
+ if self.y_grid_over_plot:
+ self.__draw_y_grid_and_axis(can)
+
+ if self.legend == _dummy_legend:
+ self.legend = legend.T()
+
+ if self.legend:
+ legends = []
+ for plot in self.plots:
+ entry = plot.get_legend_entry()
+ if entry == None:
+ pass
+ elif type(entry) != ListType:
+ legends.append(entry)
+ else:
+ for e in entry:
+ legends.append(e)
+ self.legend.draw(self, legends, can)
+
+ def add_plot(self, *plots):
+ "Add PLOTS... to the area."
+ self.plots.extend(plots)