Changeset 109

Show
Ignore:
Timestamp:
10/27/08 16:49:58 (4 years ago)
Author:
lgs
Message:

Some refactoring and support for negative values in line and bar charts. Inspired by Nicolas patch

Location:
trunk
Files:
11 modified

Legend:

Unmodified
Added
Removed
  • trunk/chavier/app.py

    r108 r109  
    6565                tickCount=int, 
    6666                tickPrecision=int, 
     67                interval=int, 
    6768                range=list, 
    6869                rotate=float, 
     
    103104        shouldFill=bool, 
    104105        barWidthFillFraction=float, 
    105         xOriginIsZero=bool, 
    106         yOriginIsZero=bool, 
    107106        pieRadius=float, 
    108107        colorScheme=str, 
     
    127126        chart.addDataset(datasets) 
    128127        chart.render() 
    129         return surface 
     128        return chart 
    130129 
    131130if __name__ == '__main__': 
  • trunk/chavier/dialogs.py

    r108 r109  
    133133                                               0, 1, 5, 1, 1000, 10) 
    134134        self.min = self._create_spin_button('Minimum y value', 
    135                                             2, 0.5, 1, 0, 1000, 0) 
     135                                            2, 0.5, 1, -1000, 1000, 0) 
    136136        self.max = self._create_spin_button('Maximum y value', 
    137137                                            2, 0.5, 1, 0, 1000, 10) 
  • trunk/chavier/gui.py

    r108 r109  
    2222from chavier.dialogs import ( 
    2323    TextInputDialog, PointDialog, OptionDialog, RandomGeneratorDialog, 
    24     AboutDialog, 
     24    AboutDialog, warning 
    2525    ) 
    2626 
     
    2929        self.app = app 
    3030 
     31        self.chart = None 
    3132        self.surface = None 
    3233 
     
    123124                 '<ctrl>g', 'Generate random points', 
    124125                 self.generate_random_points), 
     126                ('dump-chart-state', gtk.STOCK_CONVERT, '_Dump chart state', 
     127                 '<ctrl>d', 'Dump internal chart variables', 
     128                 self.dump_chart_state), 
    125129                ('help', None, '_Help', None, 'Help', None), 
    126130                ('about', gtk.STOCK_ABOUT, None, None, 'About this program', 
     
    168172    <menu action="tools"> 
    169173      <menuitem action="random-points"/> 
     174      <menuitem action="dump-chart-state"/> 
    170175    </menu> 
    171176    <menu action="help"> 
     
    357362 
    358363    def drawing_area_expose_event(self, widget, event, data=None): 
    359         if self.surface is None: 
     364        if self.chart is None: 
    360365            return 
    361366 
     
    364369                     event.area.width, event.area.height) 
    365370        cr.clip() 
    366         cr.set_source_surface(self.surface, 0, 0) 
     371        cr.set_source_surface(self.chart.surface, 0, 0) 
    367372        cr.paint() 
    368373 
    369374    def drawing_area_size_allocate_event(self, widget, event, data=None): 
    370         if self.surface is not None: 
     375        if self.chart is not None: 
    371376            self.refresh() 
    372377 
    373378    def on_chart_type_change(self, action, current, data=None): 
    374         if self.surface is not None: 
     379        if self.chart is not None: 
    375380            self.refresh() 
    376381 
     
    478483            chart_type = self._get_chart_type() 
    479484            alloc = self.drawing_area.get_allocation() 
    480             self.surface = self.app.get_chart(datasets, options, chart_type, 
    481                                               alloc.width, alloc.height) 
     485            self.chart = self.app.get_chart(datasets, options, chart_type, 
     486                                            alloc.width, alloc.height) 
    482487            self.drawing_area.queue_draw() 
    483488        else: 
    484             self.surface = None 
     489            self.chart = None 
    485490 
    486491    def generate_random_points(self, action=None): 
     
    500505        dialog.destroy() 
    501506 
     507    def dump_chart_state(self, action=None): 
     508        if self.chart is None: 
     509            return 
     510 
     511        alloc = self.drawing_area.get_allocation() 
     512 
     513        print 'CHART STATE' 
     514        print '-' * 70 
     515        print 'surface: %d x %d' % (alloc.width, alloc.height) 
     516        print 'area   :', self.chart.area 
     517        print 
     518        print 'minxval:', self.chart.minxval 
     519        print 'maxxval:', self.chart.maxxval 
     520        print 'xrange :', self.chart.xrange 
     521        print 
     522        print 'minyval:', self.chart.minyval 
     523        print 'maxyval:', self.chart.maxyval 
     524        print 'yrange :', self.chart.yrange 
     525 
    502526    def about(self, action=None): 
    503527        dialog = AboutDialog(self.main_window) 
  • trunk/src/bar.py

    r97 r109  
    1717 
    1818from pycha.chart import Chart, uniqueIndices 
    19 from pycha.color import hex2rgb, clamp 
     19from pycha.color import hex2rgb 
    2020 
    2121class BarChart(Chart): 
     
    2828        self.barMargin = 0.0 
    2929 
     30    def _updateXY(self): 
     31        super(BarChart, self)._updateXY() 
     32        # each dataset is centered around a line segment. that's why we 
     33        # need n + 1 divisions on the x axis 
     34        self.xscale = 1 / (self.xrange + 1.0) 
     35 
    3036    def _updateChart(self): 
    3137        """Evaluates measures for vertical bars""" 
     
    3642        if len(uniqx) == 1: 
    3743            xdelta = 1.0 
    38             self.xscale = 1.0 
    39             self.minxval = uniqx[0] 
    4044            barWidth = 1.0 * self.options.barWidthFillFraction 
    4145            self.barWidthForSet = barWidth / len(stores) 
     
    4448            xdelta = min([abs(uniqx[j] - uniqx[j-1]) 
    4549                          for j in range(1, len(uniqx))]) 
    46             self.xscale = 1.0 / (self.xrange + 1) 
    4750            barWidth = xdelta * self.xscale * self.options.barWidthFillFraction 
    4851            self.barWidthForSet = barWidth / len(stores) 
     
    5760 
    5861        def drawBar(bar): 
    59             cx.set_line_width(self.options.stroke.width) 
     62            stroke_width = self.options.stroke.width 
     63            ux, uy = cx.device_to_user_distance(stroke_width, stroke_width) 
     64            if ux < uy: 
     65                ux = uy 
     66            cx.set_line_width(ux) 
    6067 
    6168            # gather bar proportions 
     
    7481                cx.fill() 
    7582 
    76             if self.options.shouldFill: 
     83            if self.options.shouldFill or (not self.options.stroke.hide): 
    7784                cx.rectangle(x, y, w, h) 
    78                 cx.set_source_rgb(*self.options.colorScheme[bar.name]) 
    79                 cx.fill_preserve() 
    8085 
    81             if not self.options.stroke.hide: 
    82                 cx.set_source_rgb(*hex2rgb(self.options.stroke.color)) 
    83                 cx.stroke() 
     86                if self.options.shouldFill: 
     87                    cx.set_source_rgb(*self.options.colorScheme[bar.name]) 
     88                    cx.fill_preserve() 
     89     
     90                if not self.options.stroke.hide: 
     91                    cx.set_source_rgb(*hex2rgb(self.options.stroke.color)) 
     92                    cx.stroke() 
    8493 
    8594        cx.save() 
     
    93102        """Evaluates measures for vertical bars""" 
    94103        super(VerticalBarChart, self)._updateChart() 
    95  
    96104        for i, (name, store) in enumerate(self.datasets): 
    97105            for item in store: 
    98106                xval, yval = item 
    99107                x = (((xval - self.minxval) * self.xscale) 
    100                     + (i * self.barWidthForSet) + self.barMargin) 
     108                    + self.barMargin + (i * self.barWidthForSet)) 
    101109                w = self.barWidthForSet 
    102                 h = (yval - self.minyval) * self.yscale 
    103                 y = 1.0 - h 
     110                h = abs(yval) * self.yscale 
     111                if yval > 0: 
     112                    y = (1.0 - h) - self.area.origin 
     113                else: 
     114                    y = 1 - self.area.origin 
    104115                rect = Rect(x, y, w, h, xval, yval, name) 
    105116 
     
    127138                xval, yval = item 
    128139                y = (((xval - self.minxval) * self.xscale) 
    129                      + (i * self.barWidthForSet) + self.barMargin) 
    130                 x = 0.0 
     140                     + self.barMargin  + (i * self.barWidthForSet)) 
    131141                h = self.barWidthForSet 
    132                 w = (yval - self.minyval) * self.yscale 
     142                w = abs(yval) * self.yscale 
     143                if yval > 0: 
     144                    x = self.area.origin 
     145                else: 
     146                    x = self.area.origin - w 
    133147                rect = Rect(x, y, w, h, xval, yval, name) 
    134148 
     
    153167        return (x, y-2, w+2, h+4) 
    154168 
     169    def _renderXAxis(self, cx): 
     170        """Draws the horizontal line representing the X axis""" 
     171        cx.new_path() 
     172        cx.move_to(self.area.x, self.area.y + self.area.h) 
     173        cx.line_to(self.area.x + self.area.w, self.area.y + self.area.h) 
     174        cx.close_path() 
     175        cx.stroke() 
     176 
     177    def _renderYAxis(self, cx): 
     178        # draws the vertical line representing the Y axis 
     179        cx.new_path() 
     180        cx.move_to(self.area.x + self.area.origin * self.area.w, 
     181                   self.area.y) 
     182        cx.line_to(self.area.x + self.area.origin * self.area.w, 
     183                   self.area.y + self.area.h) 
     184        cx.close_path() 
     185        cx.stroke() 
    155186 
    156187class Rect(object): 
     
    161192 
    162193    def __str__(self): 
    163         return "<pycha.bar.Rect@(%.2f, %.2f) %.2fx%.2f>" % (self.x, self.y, 
    164                                                             self.w, self.h) 
     194        return ("<pycha.bar.Rect@(%.2f, %.2f) %.2fx%.2f (%.2f, %.2f) %s>" 
     195                % (self.x, self.y, self.w, self.h, self.xval, self.yval, 
     196                   self.name)) 
  • trunk/src/chart.py

    r106 r109  
    139139    def _updateXY(self): 
    140140        """Calculates all kinds of metrics for the x and y axis""" 
    141         stores = self._getDatasetsValues() 
     141        x_range_is_defined = self.options.axis.x.range is not None 
     142        y_range_is_defined = self.options.axis.y.range is not None 
     143 
     144        if not x_range_is_defined or not y_range_is_defined: 
     145            stores = self._getDatasetsValues() 
     146 
     147        # gather data for the x axis 
     148        if x_range_is_defined: 
     149            self.minxval, self.maxxval = self.options.axis.x.range 
     150        else: 
     151            xdata = [pair[0] for pair in reduce(lambda a,b: a+b, stores)] 
     152            self.minxval = float(min(xdata)) 
     153            self.maxxval = float(max(xdata)) 
     154            if self.minxval * self.maxxval > 0 and self.minxval > 0: 
     155                self.minxval = 0.0 
     156 
     157        self.xrange = self.maxxval - self.minxval 
     158        if self.xrange == 0: 
     159            self.xscale = 1.0 
     160        else: 
     161            self.xscale = 1 / self.xrange 
     162 
     163        # gather data for the y axis 
     164        if y_range_is_defined: 
     165            self.minyval, self.maxyval = self.options.axis.y.range 
     166        else: 
     167            ydata = [pair[1] for pair in reduce(lambda a,b: a+b, stores)] 
     168            self.minyval = float(min(ydata)) 
     169            self.maxyval = float(max(ydata)) 
     170            if self.minyval * self.maxyval > 0 and self.minyval > 0: 
     171                self.minyval = 0.0 
     172 
     173        self.yrange = self.maxyval - self.minyval 
     174        if self.yrange == 0: 
     175            self.yscale = 1.0 
     176        else: 
     177            self.yscale = 1 / self.yrange 
    142178 
    143179        # calculate area data 
     
    146182        height = (self.surface.get_height() 
    147183                  - self.options.padding.top - self.options.padding.bottom) 
     184 
     185        if self.minyval * self.maxyval < 0: # different signs 
     186            origin = abs(self.minyval) * self.yscale 
     187        else: 
     188            origin = 0 
     189 
    148190        self.area = Area(self.options.padding.left, 
    149191                         self.options.padding.top, 
    150                          width, height) 
    151  
    152         # gather data for the x axis 
    153         if self.options.axis.x.range: 
    154             self.minxval, self.maxxval = self.options.axis.x.range 
    155             self.xscale = self.maxxval - self.minxval 
    156         else: 
    157             xdata = [pair[0] for pair in reduce(lambda a,b: a+b, stores)] 
    158             if self.options.xOriginIsZero: 
    159                 self.minxval = 0.0 
    160             else: 
    161                 self.minxval = float(min(xdata)) 
    162             self.maxxval = float(max(xdata)) 
    163  
    164         self.xrange = self.maxxval - self.minxval 
    165         if self.xrange == 0: 
    166             self.xscale = 1.0 
    167         else: 
    168             self.xscale = 1 / self.xrange 
    169  
    170         # gather data for the y axis 
    171         if self.options.axis.y.range: 
    172             self.minyval, self.maxyval = self.options.axis.y.range 
    173             self.yscale = self.maxyval - self.minyval 
    174         else: 
    175             ydata = [pair[1] for pair in reduce(lambda a,b: a+b, stores)] 
    176             if self.options.yOriginIsZero: 
    177                 self.minyval = 0.0 
    178             else: 
    179                 self.minyval = float(min(ydata)) 
    180             self.maxyval = float(max(ydata)) 
    181  
    182         self.yrange = self.maxyval - self.minyval 
    183         if self.yrange == 0: 
    184             self.yscale = 1.0 
    185         else: 
    186             self.yscale = 1 / self.yrange 
     192                         width, height, origin) 
    187193 
    188194    def _updateChart(self): 
     
    215221            uniqx = range(len(uniqueIndices(stores)) + 1) 
    216222            roughSeparation = self.xrange / self.options.axis.x.tickCount 
    217  
    218223            i = j = 0 
    219             while i + 1 < len(uniqx) and j < self.options.axis.x.tickCount: 
    220                 if (uniqx[i + 1] - self.minxval) >= (j * roughSeparation): 
     224            while i < len(uniqx) and j < self.options.axis.x.tickCount: 
     225                if (uniqx[i] - self.minxval) >= (j * roughSeparation): 
    221226                    pos = self.xscale * (uniqx[i] - self.minxval) 
    222227                    if 0.0 <= pos <= 1.0: 
    223                         self.xticks.append((pos, uniqx[i + 1])) 
     228                        self.xticks.append((pos, uniqx[i])) 
    224229                        j += 1 
    225230                i += 1 
     
    239244                    self.yticks.append((pos, label)) 
    240245 
     246        elif self.options.axis.y.interval > 0: 
     247            interval = self.options.axis.y.interval 
     248            label = (divmod(self.minyval, interval)[0] + 1) * interval 
     249            pos = 1.0 - (self.yscale * (label - self.minyval)) 
     250            while 0.0 <= pos <= 1.0: 
     251                self.yticks.append((pos, label)) 
     252                label += interval 
     253                pos = 1.0 - (self.yscale * (label - self.minyval)) 
     254 
    241255        elif self.options.axis.y.tickCount > 0: 
    242256            prec = self.options.axis.y.tickPrecision 
     
    263277        if self.options.background.baseColor: 
    264278            cx.set_source_rgb(*hex2rgb(self.options.background.baseColor)) 
    265             x, y, w, h = 0, 0, self.area.w, self.area.h 
    266             w += self.options.padding.left + self.options.padding.right 
    267             h += self.options.padding.top + self.options.padding.bottom 
    268             cx.rectangle(x, y, w, h) 
    269             cx.fill() 
     279            cx.paint() 
    270280 
    271281        if self.options.background.chartColor: 
     
    424434        """Draws the horizontal line representing the X axis""" 
    425435        cx.new_path() 
    426         cx.move_to(self.area.x, self.area.y + self.area.h) 
    427         cx.line_to(self.area.x + self.area.w, self.area.y + self.area.h) 
     436        cx.move_to(self.area.x, 
     437                   self.area.y + self.area.h * (1.0 - self.area.origin)) 
     438        cx.line_to(self.area.x + self.area.w, 
     439                   self.area.y + self.area.h * (1.0 - self.area.origin)) 
    428440        cx.close_path() 
    429441        cx.stroke() 
     
    567579class Area(object): 
    568580    """Simple rectangle to hold an area coordinates and dimensions""" 
    569     def __init__(self, x, y, w, h): 
     581    def __init__(self, x, y, w, h, origin=0.0): 
    570582        self.x, self.y, self.w, self.h = x, y, w, h 
     583        self.origin = origin 
    571584 
    572585    def __str__(self): 
    573         return "<pycha.chart.Area@(%.2f, %.2f) %.2fx%.2f" % (self.x, self.y, 
    574                                                              self.w, self.h) 
     586        msg = "<pycha.chart.Area@(%.2f, %.2f) %.2f x %.2f Origin: %.2f>" 
     587        return  msg % (self.x, self.y, self.w, self.h, self.origin) 
    575588 
    576589class Option(dict): 
     
    594607    axis=Option( 
    595608        lineWidth=1.0, 
    596         lineColor='#000000', 
     609        lineColor='#0f0000', 
    597610        tickSize=3.0, 
    598611        labelColor='#666666', 
     
    614627            tickCount=10, 
    615628            tickPrecision=1, 
     629            interval=0, 
    616630            range=None, 
    617631            rotate=None, 
     
    635649        left=30, 
    636650        right=30, 
    637         top=15, 
    638         bottom=15, 
     651        top=30, 
     652        bottom=30, 
    639653    ), 
    640654    stroke=Option( 
     
    647661    shouldFill=True, 
    648662    barWidthFillFraction=0.75, 
    649     xOriginIsZero=True, 
    650     yOriginIsZero=True, 
    651663    pieRadius=0.4, 
    652664    colorScheme=DEFAULT_COLOR, 
  • trunk/src/line.py

    r97 r109  
    1717 
    1818from pycha.chart import Chart 
    19 from pycha.color import hex2rgb, clamp 
     19from pycha.color import hex2rgb 
    2020 
    2121class LineChart(Chart): 
     
    4747            if self.options.shouldFill: 
    4848                # Go to the (0,0) coordinate to start drawing the area 
    49                 cx.move_to(self.area.x, self.area.y + self.area.h) 
     49                #cx.move_to(self.area.x, self.area.y + self.area.h) 
     50                cx.move_to(self.area.x, 
     51                           self.area.y + (1.0 - self.area.origin) * self.area.h) 
    5052 
    5153            for point in self.points: 
     
    6567            if self.options.shouldFill: 
    6668                # Close the path to the start point 
    67                 cx.line_to(lastX * self.area.w + self.area.x, 
    68                            self.area.h + self.area.y) 
    69                 cx.line_to(self.area.x, self.area.y + self.area.h) 
     69                y = (1.0 - self.area.origin) * self.area.h + self.area.y 
     70                cx.line_to(lastX * self.area.w + self.area.x, y) 
     71                cx.line_to(self.area.x, y) 
    7072                cx.close_path() 
    7173            else: 
  • trunk/src/pie.py

    r102 r109  
    6060        self.xticks = [] 
    6161        if self.options.axis.x.ticks: 
    62             lookup = dict([(slice.xval, slice) for slice in self.slices]) 
     62#            lookup = dict([(slice.xval, slice) for slice in self.slices]) 
     63            lookup = dict([(slice.name, slice) for slice in self.slices]) 
    6364            for tick in self.options.axis.x.ticks: 
    6465                if not isinstance(tick, Option): 
     
    7172        else: 
    7273            for slice in self.slices: 
    73                 label = '%s (%.1f%%)' % (slice.xval, slice.fraction * 100) 
    74                 self.xticks.append((slice.xval, label)) 
     74#                label = '%s (%.1f%%)' % (slice.xval, slice.fraction * 100) 
     75#                self.xticks.append((slice.xval, label)) 
     76                label = '%s (%.1f%%)' % (slice.name, slice.fraction * 100) 
     77                self.xticks.append((slice.name, label)) 
    7578 
    7679    def _renderBackground(self, cx): 
     
    130133 
    131134        self.xlabels = [] 
    132         lookup = dict([(slice.xval, slice) for slice in self.slices]) 
     135#        lookup = dict([(slice.xval, slice) for slice in self.slices]) 
     136        lookup = dict([(slice.name, slice) for slice in self.slices]) 
    133137 
    134138        cx.set_source_rgb(*hex2rgb(self.options.axis.labelColor)) 
  • trunk/src/scatter.py

    r96 r109  
    1616# along with PyCha.  If not, see <http://www.gnu.org/licenses/>. 
    1717 
    18 from pycha.chart import Chart 
    19 from pycha.color import hex2rgb, clamp 
    20 from pycha.line import Point, LineChart 
     18from pycha.line import LineChart 
    2119 
    2220class ScatterplotChart(LineChart): 
     
    2422    def _renderChart(self, cx): 
    2523        """Renders a scatterplot""" 
    26         def drawSymbol(point, size=2): 
    27             ox = point.x * self.area.w + self.area.x 
    28             oy = point.y * self.area.h + self.area.y 
    29             cx.move_to(ox-size, oy  ) 
    30             cx.line_to(ox+size, oy  ) 
    31             cx.move_to(ox     , oy-size) 
    32             cx.line_to(ox     , oy+size) 
     24        def drawSymbol(point, size=2): 
     25            ox = point.x * self.area.w + self.area.x 
     26            oy = point.y * self.area.h + self.area.y 
     27            cx.move_to(ox-size, oy  ) 
     28            cx.line_to(ox+size, oy  ) 
     29            cx.move_to(ox     , oy-size) 
     30            cx.line_to(ox     , oy+size) 
    3331 
    3432        def preparePath(storeName, size=2): 
     
    4240 
    4341        cx.set_line_width(self.options.stroke.width) 
    44         # TODO: self.options.stroke.shadow 
    45         for key in self._getDatasetsKeys(): 
    46             cx.set_source_rgb(*self.options.colorScheme[key]) 
    47             preparePath(key) 
    48             cx.stroke() 
     42        # TODO: self.options.stroke.shadow 
     43        for key in self._getDatasetsKeys(): 
     44            cx.set_source_rgb(*self.options.colorScheme[key]) 
     45            preparePath(key) 
     46            cx.stroke() 
    4947 
    5048        cx.restore() 
    5149 
    5250    def _renderLines(self, cx): 
    53         """We don't need lines in the background""" 
    54         pass 
     51        # We don't need lines in the background 
     52        pass 
  • trunk/tests/bar.py

    r85 r109  
    6262        surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 500, 500) 
    6363        dataset = ( 
    64             ('dataset1', ([0, 3], [1, 4], [2, 2], [3,5], [4,3.5])), 
    65             ('dataset2', ([0, 2], [1, 3], [2, 1], [3,5], [4,2.5])), 
     64            ('dataset1', ([0, 3], [1, 4], [2, 2], [3, 5], [4, 3.5])), 
     65            ('dataset2', ([0, 2], [1, 3], [2, 1], [3, 5], [4, 2.5])), 
    6666            ) 
    6767        ch = pycha.bar.VerticalBarChart(surface) 
     
    6969        ch._updateXY() 
    7070        ch._updateChart() 
     71        self.assertEqual(ch.minxval, 0) 
     72        self.assertEqual(ch.maxxval, 4) 
    7173        self.assertEqual(ch.xrange, 4) 
    72         self.assertAlmostEqual(ch.xscale, 0.2, 4) 
    73         self.assertAlmostEqual(ch.yscale, 0.2, 4) 
     74        self.assertAlmostEqual(ch.xscale, 0.20, 4) 
     75        self.assertEqual(ch.minyval, 0) 
     76        self.assertEqual(ch.maxyval, 5) 
     77        self.assertEqual(ch.yrange, 5) 
     78        self.assertAlmostEqual(ch.yscale, 0.20, 4) 
    7479        self.assertEqual(ch.minxdelta, 1) 
    7580        self.assertAlmostEqual(ch.barWidthForSet, 0.075, 4) 
    7681        self.assertAlmostEqual(ch.barMargin, 0.025, 4) 
    7782 
     83        R = pycha.bar.Rect 
    7884        bars = ( 
    79             pycha.bar.Rect(0.025, 0.4, 0.075, 0.6, 0, 3, 'dataset1'), 
    80             pycha.bar.Rect(0.225, 0.2, 0.075, 0.8, 1, 4, 'dataset1'), 
    81             pycha.bar.Rect(0.425, 0.6, 0.075, 0.4, 2, 2, 'dataset1'), 
    82             pycha.bar.Rect(0.625, 0.0, 0.075, 1.0, 3, 5, 'dataset1'), 
    83             pycha.bar.Rect(0.825, 0.3, 0.075, 0.7, 4, 3.5, 'dataset1'), 
    84  
    85             pycha.bar.Rect(0.100, 0.6, 0.075, 0.4, 0, 2, 'dataset2'), 
    86             pycha.bar.Rect(0.300, 0.4, 0.075, 0.6, 1, 3, 'dataset2'), 
    87             pycha.bar.Rect(0.500, 0.8, 0.075, 0.2, 2, 1, 'dataset2'), 
    88             pycha.bar.Rect(0.700, 0.0, 0.075, 1.0, 3, 5, 'dataset2'), 
    89             pycha.bar.Rect(0.900, 0.5, 0.075, 0.5, 4, 2.5, 'dataset2'), 
     85            R(0.025, 0.400, 0.075, 0.600, 0, 3, 'dataset1'), 
     86            R(0.225, 0.200, 0.075, 0.800, 1, 4, 'dataset1'), 
     87            R(0.425, 0.600, 0.075, 0.400, 2, 2, 'dataset1'), 
     88            R(0.625, 0.000, 0.075, 1.000, 3, 5, 'dataset1'), 
     89            R(0.825, 0.300, 0.075, 0.700, 4, 3.5, 'dataset1'), 
     90 
     91            R(0.100, 0.600, 0.075, 0.400, 0, 2, 'dataset2'), 
     92            R(0.300, 0.400, 0.075, 0.600, 1, 3, 'dataset2'), 
     93            R(0.500, 0.800, 0.075, 0.200, 2, 1, 'dataset2'), 
     94            R(0.700, 0.000, 0.075, 1.000, 3, 5, 'dataset2'), 
     95            R(0.900, 0.500, 0.075, 0.500, 4, 2.5, 'dataset2'), 
    9096            ) 
    9197 
     
    100106            self.assertEqual(b1.name, b2.name) 
    101107 
    102     def test_updateTicks(self): 
    103         surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 500, 500) 
    104         dataset = ( 
    105             ('dataset1', ([0, 1], [1, 1], [2, 3])), 
    106             ('dataset2', ([0, 2], [1, 0], [3, 4])), 
     108    def test_updateChartWithNegatives(self): 
     109        surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 500, 500) 
     110        dataset = ( 
     111            ('dataset1', ([0, -3], [1, -1], [2, 3], [3, 5])), 
    107112            ) 
    108113        ch = pycha.bar.VerticalBarChart(surface) 
     
    110115        ch._updateXY() 
    111116        ch._updateChart() 
    112         ch._updateTicks() 
    113         xticks = [(0.125, 1), (0.375, 2), (0.625, 3)] 
    114         for i in range(len(xticks)): 
    115             self.assertAlmostEqual(ch.xticks[i][0], xticks[i][0], 4) 
    116             self.assertAlmostEqual(ch.xticks[i][1], xticks[i][1], 4) 
    117  
    118     def test_shadowRectangle(self): 
    119         ch = pycha.bar.VerticalBarChart(None) 
    120         shadow = ch._getShadowRectangle(10, 20, 400, 300) 
    121         self.assertEqual(shadow, (8, 18, 404, 302)) 
    122  
    123 class HorizontalBarTests(unittest.TestCase): 
    124  
    125     def test_updateChart(self): 
    126         surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 500, 500) 
    127         dataset = ( 
    128             ('dataset1', ([0, 1], [1, 1], [2, 3])), 
    129             ('dataset2', ([0, 2], [1, 0], [3, 4])), 
    130             ) 
    131         ch = pycha.bar.HorizontalBarChart(surface) 
    132         ch.addDataset(dataset) 
    133         ch._updateXY() 
    134         ch._updateChart() 
     117        self.assertEqual(ch.minxval, 0) 
     118        self.assertEqual(ch.maxxval, 3) 
    135119        self.assertEqual(ch.xrange, 3) 
    136120        self.assertAlmostEqual(ch.xscale, 0.25, 4) 
    137         self.assertAlmostEqual(ch.yscale, 0.25, 4) 
     121        self.assertEqual(ch.minyval, -3) 
     122        self.assertEqual(ch.maxyval, 5) 
     123        self.assertEqual(ch.yrange, 8) 
     124        self.assertAlmostEqual(ch.yscale, 0.125, 4) 
     125        self.assertAlmostEqual(ch.area.origin, 0.375) 
    138126        self.assertEqual(ch.minxdelta, 1) 
    139         self.assertAlmostEqual(ch.barWidthForSet, 0.09375, 4) 
     127        self.assertAlmostEqual(ch.barWidthForSet, 0.1875, 4) 
    140128        self.assertAlmostEqual(ch.barMargin, 0.03125, 4) 
    141129 
     130        R = pycha.bar.Rect 
    142131        bars = ( 
    143             pycha.bar.Rect(0, 0.03125, 0.25, 0.09375, 0, 1, 'dataset1'), 
    144             pycha.bar.Rect(0, 0.28125, 0.25, 0.09375, 1, 1, 'dataset1'), 
    145             pycha.bar.Rect(0, 0.53125, 0.75, 0.09375, 2, 3, 'dataset1'), 
    146  
    147             pycha.bar.Rect(0, 0.125, 0.5, 0.09375, 0, 2, 'dataset2'), 
    148             pycha.bar.Rect(0, 0.375, 0.0, 0.09375, 1, 0, 'dataset2'), 
    149             pycha.bar.Rect(0, 0.875, 1.0, 0.09375, 3, 4, 'dataset2'), 
     132            R(0.03125, 0.625, 0.1875, 0.375, 0, -3, 'dataset1'), 
     133            R(0.28125, 0.625, 0.1875, 0.125, 1, -1, 'dataset1'), 
     134            R(0.53125, 0.250, 0.1875, 0.375, 2,  3, 'dataset1'), 
     135            R(0.78125, 0.000, 0.1875, 0.625, 3,  5, 'dataset1'), 
    150136            ) 
    151137 
     
    166152            ('dataset2', ([0, 2], [1, 0], [3, 4])), 
    167153            ) 
     154        ch = pycha.bar.VerticalBarChart(surface) 
     155        ch.addDataset(dataset) 
     156        ch._updateXY() 
     157        ch._updateChart() 
     158        ch._updateTicks() 
     159        xticks = [(0.125, 0), (0.375, 1), (0.625, 2)] 
     160        for i in range(len(xticks)): 
     161            self.assertAlmostEqual(ch.xticks[i][0], xticks[i][0], 4) 
     162            self.assertAlmostEqual(ch.xticks[i][1], xticks[i][1], 4) 
     163 
     164        yticks = [ 
     165            (1.0, 0.0), (0.9, 0.4), (0.8, 0.8), (0.7, 1.2), (0.6, 1.6), 
     166            (0.5, 2.0), (0.4, 2.4), (0.3, 2.8), (0.2, 3.2), (0.1, 3.6), 
     167            (0.0, 4.0) 
     168            ] 
     169        for i in range(len(yticks)): 
     170            self.assertAlmostEqual(ch.yticks[i][0], yticks[i][0], 4) 
     171            self.assertAlmostEqual(ch.yticks[i][1], yticks[i][1], 4) 
     172             
     173    def test_udpateTicksWithNegatives(self): 
     174        surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 500, 500) 
     175        dataset = ( 
     176            ('dataset1', ([0, -2], [1, 1], [2, 3])), 
     177            ) 
     178        ch = pycha.bar.VerticalBarChart(surface) 
     179        ch.addDataset(dataset) 
     180        ch._updateXY() 
     181        ch._updateChart() 
     182        ch._updateTicks() 
     183        xticks = [(0.1667, 0), (0.5000, 1), (0.8333, 2)] 
     184        for i in range(len(xticks)): 
     185            self.assertAlmostEqual(ch.xticks[i][0], xticks[i][0], 4) 
     186            self.assertAlmostEqual(ch.xticks[i][1], xticks[i][1], 4) 
     187 
     188        yticks = [ 
     189            (1.0, -2.0), (0.9, -1.5), (0.8, -1.0), (0.7, -0.5), (0.6, 0.0), 
     190            (0.5, 0.5), (0.4, 1.0), (0.3, 1.5), (0.2, 2.0), (0.1, 2.5), 
     191            (0.0, 3.0) 
     192            ] 
     193        for i in range(len(yticks)): 
     194            self.assertAlmostEqual(ch.yticks[i][0], yticks[i][0], 4) 
     195            self.assertAlmostEqual(ch.yticks[i][1], yticks[i][1], 4) 
     196 
     197    def test_shadowRectangle(self): 
     198        ch = pycha.bar.VerticalBarChart(None) 
     199        shadow = ch._getShadowRectangle(10, 20, 400, 300) 
     200        self.assertEqual(shadow, (8, 18, 404, 302)) 
     201 
     202class HorizontalBarTests(unittest.TestCase): 
     203 
     204    def test_updateChart(self): 
     205        surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 500, 500) 
     206        dataset = ( 
     207            ('dataset1', ([0, 1], [1, 1], [2, 3])), 
     208            ('dataset2', ([0, 2], [1, 0], [3, 4])), 
     209            ) 
    168210        ch = pycha.bar.HorizontalBarChart(surface) 
    169211        ch.addDataset(dataset) 
    170212        ch._updateXY() 
    171213        ch._updateChart() 
     214        self.assertEqual(ch.xrange, 3) 
     215        self.assertAlmostEqual(ch.xscale, 0.25, 4) 
     216        self.assertAlmostEqual(ch.yscale, 0.25, 4) 
     217        self.assertEqual(ch.minxdelta, 1) 
     218        self.assertAlmostEqual(ch.barWidthForSet, 0.09375, 4) 
     219        self.assertAlmostEqual(ch.barMargin, 0.03125, 4) 
     220 
     221        bars = ( 
     222            pycha.bar.Rect(0, 0.03125, 0.25, 0.09375, 0, 1, 'dataset1'), 
     223            pycha.bar.Rect(0, 0.28125, 0.25, 0.09375, 1, 1, 'dataset1'), 
     224            pycha.bar.Rect(0, 0.53125, 0.75, 0.09375, 2, 3, 'dataset1'), 
     225 
     226            pycha.bar.Rect(0, 0.125, 0.5, 0.09375, 0, 2, 'dataset2'), 
     227            pycha.bar.Rect(0, 0.375, 0.0, 0.09375, 1, 0, 'dataset2'), 
     228            pycha.bar.Rect(0, 0.875, 1.0, 0.09375, 3, 4, 'dataset2'), 
     229            ) 
     230 
     231        for i, bar in enumerate(bars): 
     232            b1, b2 = ch.bars[i], bar 
     233            self.assertAlmostEqual(b1.x, b2.x, 4) 
     234            self.assertAlmostEqual(b1.y, b2.y, 4) 
     235            self.assertAlmostEqual(b1.w, b2.w, 4) 
     236            self.assertAlmostEqual(b1.h, b2.h, 4) 
     237            self.assertEqual(b1.xval, b2.xval) 
     238            self.assertEqual(b1.yval, b2.yval) 
     239            self.assertEqual(b1.name, b2.name) 
     240 
     241    def test_updateChartWithNegatives(self): 
     242        surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 500, 500) 
     243        dataset = ( 
     244            ('dataset1', ([0, -3], [1, -1], [2, 3], [3, 5])), 
     245            ) 
     246  
     247        ch = pycha.bar.HorizontalBarChart(surface) 
     248        ch.addDataset(dataset) 
     249        ch._updateXY() 
     250        ch._updateChart() 
     251        self.assertEqual(ch.minxval, 0) 
     252        self.assertEqual(ch.maxxval, 3) 
     253        self.assertEqual(ch.xrange, 3) 
     254        self.assertAlmostEqual(ch.xscale, 0.25, 4) 
     255        self.assertEqual(ch.minyval, -3) 
     256        self.assertEqual(ch.maxyval, 5) 
     257        self.assertEqual(ch.yrange, 8) 
     258        self.assertAlmostEqual(ch.yscale, 0.125, 4) 
     259        self.assertAlmostEqual(ch.area.origin, 0.375) 
     260        self.assertEqual(ch.minxdelta, 1) 
     261        self.assertAlmostEqual(ch.barWidthForSet, 0.1875, 4) 
     262        self.assertAlmostEqual(ch.barMargin, 0.03125, 4) 
     263 
     264        R = pycha.bar.Rect 
     265        bars = ( 
     266            R(0.000, 0.03125, 0.375, 0.1875, 0, -3, 'dataset1'), 
     267            R(0.250, 0.28125, 0.125, 0.1875, 1, -1, 'dataset1'), 
     268            R(0.375, 0.53125, 0.375, 0.1875, 2, 3, 'dataset1'), 
     269            R(0.375, 0.78125, 0.625, 0.1875, 3, 5, 'dataset1'), 
     270            ) 
     271 
     272        for i, bar in enumerate(bars): 
     273            b1, b2 = ch.bars[i], bar 
     274            self.assertAlmostEqual(b1.x, b2.x, 4) 
     275            self.assertAlmostEqual(b1.y, b2.y, 4) 
     276            self.assertAlmostEqual(b1.w, b2.w, 4) 
     277            self.assertAlmostEqual(b1.h, b2.h, 4) 
     278            self.assertEqual(b1.xval, b2.xval) 
     279            self.assertEqual(b1.yval, b2.yval) 
     280            self.assertEqual(b1.name, b2.name) 
     281 
     282    def test_updateTicks(self): 
     283        surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 500, 500) 
     284        dataset = ( 
     285            ('dataset1', ([0, 1], [1, 1], [2, 3])), 
     286            ('dataset2', ([0, 2], [1, 0], [3, 4])), 
     287            ) 
     288        ch = pycha.bar.HorizontalBarChart(surface) 
     289        ch.addDataset(dataset) 
     290        ch._updateXY() 
     291        ch._updateChart() 
    172292        ch._updateTicks() 
    173         yticks = [(0.125, 1), (0.375, 2), (0.625, 3)] 
     293 
     294        xticks = [ 
     295            (0.0, 0.0), (0.1, 0.4), (0.2, 0.8), (0.3, 1.2), (0.4, 1.6), 
     296            (0.5, 2.0), (0.6, 2.4), (0.7, 2.8), (0.8, 3.2), (0.9, 3.6), 
     297            (1.0, 4.0) 
     298            ] 
     299        for i in range(len(xticks)): 
     300            self.assertAlmostEqual(ch.xticks[i][0], xticks[i][0], 4) 
     301            self.assertAlmostEqual(ch.xticks[i][1], xticks[i][1], 4) 
     302 
     303        yticks = [(0.125, 0), (0.375, 1), (0.625, 2)] 
     304        for i in range(len(yticks)): 
     305            self.assertAlmostEqual(ch.yticks[i][0], yticks[i][0], 4) 
     306            self.assertAlmostEqual(ch.yticks[i][1], yticks[i][1], 4) 
     307 
     308    def test_udpateTicksWithNegatives(self): 
     309        surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 500, 500) 
     310        dataset = ( 
     311            ('dataset1', ([0, -2], [1, 1], [2, 3])), 
     312            ) 
     313        ch = pycha.bar.HorizontalBarChart(surface) 
     314        ch.addDataset(dataset) 
     315        ch._updateXY() 
     316        ch._updateChart() 
     317        ch._updateTicks() 
     318        xticks = [ 
     319            (0.0, -2.0), (0.1, -1.5), (0.2, -1.0), (0.3, -0.5), (0.4, 0.0), 
     320            (0.5, 0.5), (0.6, 1.0), (0.7, 1.5), (0.8, 2.0), (0.9, 2.5), 
     321            (1.0, 3.0) 
     322            ] 
     323        for i in range(len(xticks)): 
     324            self.assertAlmostEqual(ch.xticks[i][0], xticks[i][0], 4) 
     325            self.assertAlmostEqual(ch.xticks[i][1], xticks[i][1], 4) 
     326 
     327        yticks = [(0.1667, 0), (0.5000, 1), (0.8333, 2)] 
    174328        for i in range(len(yticks)): 
    175329            self.assertAlmostEqual(ch.yticks[i][0], yticks[i][0], 4) 
  • trunk/tests/chart.py

    r85 r109  
    4545        self.assertEqual(area.w, 100) 
    4646        self.assertEqual(area.h, 300) 
     47        self.assertEqual(area.origin, 0.0) 
     48        msg = "<pycha.chart.Area@(10.00, 20.00) 100.00 x 300.00 Origin: 0.00>" 
     49        self.assertEqual(str(area), msg) 
    4750 
    4851class OptionTests(unittest.TestCase): 
     
    164167        self.assertEqual(ch.yscale, 1/4.0) 
    165168         
    166         # TODO: test with different options (xOriginIsZero, axis.range, ...) 
     169        # TODO: test with different options (axis.range, ...) 
    167170 
    168171    def test_updateTicks(self): 
     
    177180        ch._updateXY() 
    178181        ch._updateTicks() 
    179         xticks = [(0.0, 1), (1/3.0, 2), (2/3.0, 3)] 
     182        xticks = [(0.0, 0), (1/3.0, 1), (2/3.0, 2)] 
    180183        for i in range(len(xticks)): 
    181184            self.assertAlmostEqual(ch.xticks[i][0], xticks[i][0], 4) 
     
    189192            self.assertAlmostEqual(ch.yticks[i][1], yticks[i][1], 4) 
    190193 
    191     def test_updateExplicitTicks(self): 
     194    def _test_updateExplicitTicks(self): 
    192195        """Test for bug #7""" 
    193196        surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 500, 500) 
  • trunk/tests/line.py

    r85 r109  
    4848        ch._updateXY() 
    4949        ch._updateChart() 
    50                  
     50 
     51        self.assertEqual(ch.minxval, 0) 
     52        self.assertEqual(ch.maxxval, 3) 
     53        self.assertEqual(ch.xrange, 3) 
     54        self.assertAlmostEqual(ch.xscale, 1/3.0, 4) 
     55        self.assertEqual(ch.minyval, 0) 
     56        self.assertEqual(ch.maxyval, 4) 
     57        self.assertEqual(ch.yrange, 4) 
     58        self.assertAlmostEqual(ch.yscale, 0.25, 4) 
     59 
    5160        points = ( 
    5261            pycha.line.Point(0, 0.75, 0, 1, 'dataset1'), 
     
    6574            self.assertEqual(p1.name, p2.name) 
    6675 
     76    def test_updateChartWithNegatives(self): 
     77        surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 500, 500) 
     78        dataset = ( 
     79            ('dataset1', ([0, 1], [1, -2], [2, 3])), 
     80            ('dataset2', ([0, 2], [1, 0], [3, -4])), 
     81            ) 
     82        ch = pycha.line.LineChart(surface) 
     83        ch.addDataset(dataset) 
     84        ch._updateXY() 
     85        ch._updateChart() 
     86        self.assertEqual(ch.minxval, 0) 
     87        self.assertEqual(ch.maxxval, 3) 
     88        self.assertEqual(ch.xrange, 3) 
     89        self.assertAlmostEqual(ch.xscale, 1/3.0, 4) 
     90        self.assertEqual(ch.minyval, -4) 
     91        self.assertEqual(ch.maxyval, 3) 
     92        self.assertEqual(ch.yrange, 7) 
     93        self.assertAlmostEqual(ch.yscale, 1/7.0, 4) 
     94 
     95        points = ( 
     96            pycha.line.Point(0, 0.2857, 0, 1, 'dataset1'), 
     97            pycha.line.Point(1/3.0, 0.7143, 1, -2, 'dataset1'), 
     98            pycha.line.Point(2/3.0, 0.0, 2, 3, 'dataset1'), 
     99            pycha.line.Point(0, 0.1429, 0, 2, 'dataset2'), 
     100            pycha.line.Point(1/3.0, 0.4286, 1, 0, 'dataset2'), 
     101            pycha.line.Point(1, 1.0, 3, -4, 'dataset2'), 
     102        ) 
     103        for i, point in enumerate(points): 
     104            p1, p2 = ch.points[i], point 
     105            self.assertAlmostEqual(p1.x, p2.x, 4) 
     106            self.assertAlmostEqual(p1.y, p2.y, 4) 
     107            self.assertAlmostEqual(p1.xval, p2.xval, 4) 
     108            self.assertAlmostEqual(p1.yval, p2.yval, 4) 
     109            self.assertEqual(p1.name, p2.name) 
     110 
    67111def test_suite(): 
    68112    return unittest.TestSuite((