Changeset 169 for trunk

Show
Ignore:
Timestamp:
03/17/09 05:27:31 (3 years ago)
Author:
lgs
Message:

Big refactor about how the colors scheme are created and used. See #29

Location:
trunk
Files:
17 modified

Legend:

Unmodified
Added
Removed
  • trunk/examples/barchart.py

    r136 r169  
    4949            'lineColor': '#444444' 
    5050        }, 
    51         'colorScheme': 'red', 
     51        'colorScheme': { 
     52            'name': 'gradient', 
     53            'args': { 
     54                'initialColor': 'red', 
     55            }, 
     56        }, 
    5257        'legend': { 
    5358            'hide': True, 
  • trunk/examples/linechart.py

    r136 r169  
    4444            'lineColor': '#444444' 
    4545        }, 
    46         'colorScheme': 'blue', 
     46        'colorScheme': { 
     47            'name': 'gradient', 
     48            'args': { 
     49                'initialColor': 'blue', 
     50            }, 
     51        }, 
    4752        'legend': { 
    4853            'hide': True, 
  • trunk/examples/scatterchart.py

    r136 r169  
    3636            'lineColor': '#444444' 
    3737        }, 
    38         'colorScheme': 'blue', 
     38        'colorScheme': { 
     39            'name': 'gradient', 
     40            'args': { 
     41                'initialColor': 'blue', 
     42            }, 
     43        }, 
    3944        'legend': { 
    4045            'hide': True, 
  • trunk/examples/stackedbarchart.py

    r165 r169  
    3737            'lineColor': '#444444', 
    3838        }, 
    39         'colorScheme': 'red', 
     39        'colorScheme': { 
     40            'name': 'gradient', 
     41            'args': { 
     42                'initialColor': 'red', 
     43            }, 
     44        }, 
    4045        'legend': { 
    4146            'hide': True, 
  • trunk/pycha/bar.py

    r149 r169  
    8686 
    8787                if self.options.shouldFill: 
    88                     cx.set_source_rgb(*self.options.colorScheme[bar.name]) 
     88                    cx.set_source_rgb(*self.colorScheme[bar.name]) 
    8989                    cx.fill_preserve() 
    9090 
  • trunk/pycha/chart.py

    r149 r169  
    2121import cairo 
    2222 
    23 from pycha.color import (defaultColorscheme, getColorscheme, hex2rgb, 
    24                          DEFAULT_COLOR) 
     23from pycha.color import ColorScheme, hex2rgb, DEFAULT_COLOR 
    2524 
    2625 
     
    5756        self._initSurface(surface) 
    5857 
     58        self.colorScheme = None 
     59 
    5960    def addDataset(self, dataset): 
    6061        """Adds an object containing chart data to the storage hash""" 
     
    113114 
    114115    def _setColorscheme(self): 
    115         """Sets the colorScheme used for the chart using the color in the 
     116        """Sets the colorScheme used for the chart using the 
    116117        options.colorScheme option 
    117118        """ 
    118         scheme = self.options.colorScheme 
     119        name = self.options.colorScheme.name 
    119120        keys = self._getDatasetsKeys() 
    120         if isinstance(scheme, dict): 
    121             if not scheme: 
    122                 self.options.colorScheme = defaultColorscheme(keys) 
    123         elif isinstance(scheme, basestring): 
    124             self.options.colorScheme = getColorscheme(scheme, keys) 
    125         else: 
    126             raise TypeError("Color scheme is invalid!") 
     121        colorSchemeClass = ColorScheme.getColorScheme(name, None) 
     122        if colorSchemeClass is None: 
     123            raise ValueError('Color scheme is invalid!') 
     124 
     125        kwargs = dict(self.options.colorScheme.args) 
     126        self.colorScheme = colorSchemeClass(keys, **kwargs) 
    127127 
    128128    def _initSurface(self, surface): 
     
    552552        def drawKey(key, x, y, text_height): 
    553553            cx.rectangle(x, y, bullet, bullet) 
    554             cx.set_source_rgb(*self.options.colorScheme[key]) 
     554            cx.set_source_rgb(*self.colorScheme[key]) 
    555555            cx.fill_preserve() 
    556556            cx.set_source_rgb(0, 0, 0) 
     
    670670    barWidthFillFraction=0.75, 
    671671    pieRadius=0.4, 
    672     colorScheme=DEFAULT_COLOR, 
     672    colorScheme=Option( 
     673        name='gradient', 
     674        args=Option(initialColor=DEFAULT_COLOR), 
     675    ), 
    673676    title=None, 
    674677    titleFont='Tahoma', 
  • trunk/pycha/color.py

    r140 r169  
    11# Copyright(c) 2007-2009 by Lorenzo Gil Sanchez <lorenzo.gil.sanchez@gmail.com> 
     2#              2009 by Yaco S.L. <lgs@yaco.es> 
    23# 
    34# This file is part of PyCha. 
     
    5354 
    5455 
    55 def generateColorscheme(masterColor, keys): 
    56     """Generates a dictionary where the keys match the keys argument and 
    57     the values are colors derivated from the masterColor. 
    58  
    59     Each color is a lighter version of masterColor. This difference is 
    60     computed based on the number of keys. 
    61  
    62     The masterColor is given in a hex string format. 
    63     """ 
    64     r, g, b = hex2rgb(masterColor) 
    65     light = 1.0 / (len(keys)*2) 
    66     return dict([(key, lighten(r, g, b, light * i)) 
    67                  for i, key in enumerate(keys)]) 
    68  
    69  
    70 def defaultColorscheme(keys): 
    71     """Return the default color scheme (derived from a dark green)""" 
    72     return generateColorscheme(DEFAULT_COLOR, keys) 
    73  
    74  
    75 def getColorscheme(color, keys): 
    76     """Get a color scheme from the six predefined ones or makes another 
    77     one if the color is not found 
    78     """ 
    79     return generateColorscheme(colorSchemes.get(color, color), keys) 
    80  
    81  
    82 # default colors for color schemes 
    83 colorSchemes = dict( 
     56basicColors = dict( 
    8457    red='#6d1d1d', 
    8558    green=DEFAULT_COLOR, 
     
    8962    darkcyan='#305755', 
    9063    ) 
     64 
     65 
     66class ColorSchemeMetaclass(type): 
     67    """This metaclass is used to autoregister all ColorScheme classes""" 
     68 
     69    def __new__(mcs, name, bases, dict): 
     70        klass = type.__new__(mcs, name, bases, dict) 
     71        klass.registerColorScheme() 
     72        return klass 
     73 
     74 
     75class ColorScheme(dict): 
     76    """A color scheme is a dictionary where the keys match the keys 
     77    constructor argument and the values are colors""" 
     78 
     79    __metaclass__ = ColorSchemeMetaclass 
     80    __registry__ = {} 
     81 
     82    def __init__(self, keys): 
     83        super(ColorScheme, self).__init__() 
     84 
     85    @classmethod 
     86    def registerColorScheme(cls): 
     87        key = cls.__name__.replace('ColorScheme', '').lower() 
     88        if key: 
     89            cls.__registry__[key] = cls 
     90 
     91    @classmethod 
     92    def getColorScheme(cls, name, default=None): 
     93        return cls.__registry__.get(name, default) 
     94 
     95 
     96class GradientColorScheme(ColorScheme): 
     97    """In this color scheme each color is a lighter version of initialColor. 
     98 
     99    This difference is computed based on the number of keys. 
     100 
     101    The initialColor is given in a hex string format. 
     102    """ 
     103 
     104    def __init__(self, keys, initialColor=DEFAULT_COLOR): 
     105        super(GradientColorScheme, self).__init__(keys) 
     106        if initialColor in basicColors: 
     107            initialColor = basicColors[initialColor] 
     108 
     109        r, g, b = hex2rgb(initialColor) 
     110        light = 1.0 / (len(keys) * 2) 
     111 
     112        for i, key in enumerate(keys): 
     113            self[key] = lighten(r, g, b, light * i) 
  • trunk/pycha/line.py

    r140 r169  
    7474                cx.close_path() 
    7575            else: 
    76                 cx.set_source_rgb(*self.options.colorScheme[storeName]) 
     76                cx.set_source_rgb(*self.colorScheme[storeName]) 
    7777                cx.stroke() 
    7878 
     
    9393 
    9494                # fill the line 
    95                 cx.set_source_rgb(*self.options.colorScheme[storeName]) 
     95                cx.set_source_rgb(*self.colorScheme[storeName]) 
    9696                preparePath(storeName) 
    9797                cx.fill() 
  • trunk/pycha/pie.py

    r140 r169  
    112112        for slice in self.slices: 
    113113            if slice.isBigEnough(): 
    114                 cx.set_source_rgb(*self.options.colorScheme[slice.name]) 
     114                cx.set_source_rgb(*self.colorScheme[slice.name]) 
    115115                if self.options.shouldFill: 
    116116                    slice.draw(cx, self.centerx, self.centery, self.radius) 
  • trunk/pycha/scatter.py

    r140 r169  
    4444        # TODO: self.options.stroke.shadow 
    4545        for key in self._getDatasetsKeys(): 
    46             cx.set_source_rgb(*self.options.colorScheme[key]) 
     46            cx.set_source_rgb(*self.colorScheme[key]) 
    4747            preparePath(key) 
    4848            cx.stroke() 
  • trunk/tests/chart.py

    r143 r169  
    133133 
    134134    def test_colorscheme(self): 
    135         ch = pycha.chart.Chart(None, {'colorScheme': '#000000'}) 
     135        options = {'colorScheme': {'name': 'gradient', 
     136                                   'args': {'initialColor': '#000000'}}} 
     137        ch = pycha.chart.Chart(None, options) 
    136138        dataset = (('dataset1', ([0, 1], [1, 1])), ) 
    137139        ch.addDataset(dataset) 
    138140        ch._setColorscheme() 
    139         self.assert_(isinstance(ch.options.colorScheme, dict)) 
    140         self.assertEqual(ch.options.colorScheme, {'dataset1': (0.0, 0.0, 0.0)}) 
    141  
    142         ch = pycha.chart.Chart(None, {'colorScheme': {}}) 
    143         ch.addDataset(dataset) 
    144         ch._setColorscheme() 
    145         self.assert_(isinstance(ch.options.colorScheme, dict)) 
    146         self.assertEqual(ch.options.colorScheme.keys(), ['dataset1']) 
    147  
    148         ch = pycha.chart.Chart(None, {'colorScheme': (0.0, 0.0, 0.0)}) 
    149         self.assertRaises(TypeError, ch._setColorscheme) 
     141        self.assert_(isinstance(ch.colorScheme, dict)) 
     142        self.assertEqual(ch.colorScheme, {'dataset1': (0.0, 0.0, 0.0)}) 
     143 
     144        options = {'colorScheme': {'name': 'foo'}} 
     145        ch = pycha.chart.Chart(None, options) 
     146        ch.addDataset(dataset) 
     147        self.assertRaises(ValueError, ch._setColorscheme) 
    150148 
    151149    def test_updateXY(self): 
  • trunk/tests/color.py

    r112 r169  
    1 # Copyright (c) 2007-2008 by Lorenzo Gil Sanchez <lorenzo.gil.sanchez@gmail.com> 
     1# Copyright(c) 2007-2008 by Lorenzo Gil Sanchez <lorenzo.gil.sanchez@gmail.com> 
     2#              2009 by Yaco S.L. <lgs@yaco.es> 
    23# 
    34# This file is part of PyCha. 
     
    1920 
    2021import pycha.color 
     22 
     23 
     24class SimpleColorScheme(pycha.color.ColorScheme): 
     25    pass 
     26 
    2127 
    2228class ColorTests(unittest.TestCase): 
     
    6369            self.assertAlmostEqual(c1[i], c2[i], precission) 
    6470 
    65     def test_generateColorscheme(self): 
    66         keys = ('k1', 'k2', 'k3', 'k4') 
    67         color = '#ff0000' 
    68         scheme = pycha.color.generateColorscheme(color, keys) 
    69  
    70         self._assertColors(scheme['k1'], (1, 0, 0), 3) 
    71         self._assertColors(scheme['k2'], (1, 0.125, 0.125), 3) 
    72         self._assertColors(scheme['k3'], (1, 0.250, 0.250), 3) 
    73         self._assertColors(scheme['k4'], (1, 0.375, 0.375), 3) 
    74  
    75     def test_defaultColorScheme(self): 
    76         keys = ('k1', 'k2', 'k3', 'k4') 
    77         scheme1 = pycha.color.defaultColorscheme(keys) 
    78         color = pycha.color.DEFAULT_COLOR 
    79         scheme2 = pycha.color.generateColorscheme(color, keys) 
    80         self.assertEqual(scheme1, scheme2) 
    81  
    82     def test_colorScheme(self): 
     71    def test_basicColors(self): 
    8372        colors = ('red', 'green', 'blue', 'grey', 'black', 'darkcyan') 
    8473        for color in colors: 
    85             self.assert_(pycha.color.colorSchemes.has_key(color)) 
     74            self.assert_(color in pycha.color.basicColors) 
     75 
     76    def test_ColorSchemeRegistry(self): 
     77        self.assertEquals(SimpleColorScheme, 
     78                          pycha.color.ColorScheme.getColorScheme('simple')) 
     79        self.assertEquals(None, 
     80                          pycha.color.ColorScheme.getColorScheme('foo')) 
     81 
     82    def test_GradientColorScheme(self): 
     83        keys = range(5) 
     84        scheme = pycha.color.GradientColorScheme(keys, "000000") 
     85        self._assertColors(scheme[0], (0.0, 0.0, 0.0), 3) 
     86        self._assertColors(scheme[1], (0.1, 0.1, 0.1), 3) 
     87        self._assertColors(scheme[2], (0.2, 0.2, 0.2), 3) 
     88        self._assertColors(scheme[3], (0.3, 0.3, 0.3), 3) 
     89        self._assertColors(scheme[4], (0.4, 0.4, 0.4), 3) 
    8690 
    8791    def test_autoLighting(self): 
     
    9498        keys = range(n) 
    9599        color = '#ff0000' 
    96         scheme = pycha.color.generateColorscheme(color, keys) 
     100        scheme = pycha.color.GradientColorScheme(keys, color) 
    97101 
    98102        # ensure that the last color is not completely white 
    99103        color = scheme[n-1] 
    100         self.assertAlmostEqual(color[0], 1.0, 4) # the red component was already 1 
     104 
     105        # the red component was already 1 
     106        self.assertAlmostEqual(color[0], 1.0, 4) 
    101107        self.assertNotAlmostEqual(color[1], 1.0, 4) 
    102108        self.assertNotAlmostEqual(color[2], 1.0, 4) 
     109 
    103110 
    104111def test_suite(): 
     
    107114    )) 
    108115 
     116 
    109117if __name__ == '__main__': 
    110118    unittest.main(defaultTest='test_suite')