Browse Source

Going on doc + Fix y_labels behaviour for lines + Fix x_labels and y_labels behaviour for xy like + Improve gauge a bit + Finally allow call chains on add

pull/242/head
Florian Mounier 10 years ago
parent
commit
25313043f7
  1. 2
      demo/moulinrouge/tests.py
  2. 8
      docs/api/pygal.graph.rst
  3. 4
      docs/changelog.rst
  4. 3
      docs/documentation/builtin_styles.rst
  5. 2
      docs/documentation/parametric_styles.rst
  6. 6
      docs/documentation/sparks.rst
  7. 4
      docs/documentation/types/bar.rst
  8. 4
      docs/documentation/types/box.rst
  9. 16
      docs/documentation/types/dot.rst
  10. 4
      docs/documentation/types/funnel.rst
  11. 9
      docs/documentation/types/gauge.rst
  12. 14
      docs/documentation/types/histogram.rst
  13. 1
      docs/documentation/types/index.rst
  14. 4
      docs/documentation/types/line.rst
  15. 496
      docs/documentation/types/maps/index.rst
  16. 61
      docs/documentation/types/maps/pygal_maps_ch.rst
  17. 213
      docs/documentation/types/maps/pygal_maps_fr.rst
  18. 286
      docs/documentation/types/maps/pygal_maps_world.rst
  19. 28
      docs/documentation/types/pie.rst
  20. 4
      docs/documentation/types/pyramid.rst
  21. 6
      docs/documentation/types/radar.rst
  22. 4
      docs/documentation/types/xy.rst
  23. 18
      docs/ext/pygal_sphinx_directives.py
  24. 9
      docs/index.rst
  25. 6
      docs/installing.rst
  26. 3
      docs/requirements.txt
  27. 118
      pygal/graph/base.py
  28. 27
      pygal/graph/gauge.py
  29. 4
      pygal/graph/graph.py
  30. 7
      pygal/graph/histogram.py
  31. 145
      pygal/graph/public.py
  32. 12
      pygal/style.py
  33. 13
      pygal/view.py

2
demo/moulinrouge/tests.py

@ -158,6 +158,8 @@ def get_test_routes(app):
gauge.range = [-10, 10] gauge.range = [-10, 10]
gauge.add('Need l', [2.3, 5.12]) gauge.add('Need l', [2.3, 5.12])
gauge.add('Need m', [-4])
gauge.add('Need z', [-10, 10.5])
gauge.add('No', [99, -99]) gauge.add('No', [99, -99])
return gauge.render_response() return gauge.render_response()

8
docs/api/pygal.graph.rst

@ -116,6 +116,14 @@ pygal.graph.pie module
:undoc-members: :undoc-members:
:show-inheritance: :show-inheritance:
pygal.graph.public module
-------------------------
.. automodule:: pygal.graph.public
:members:
:undoc-members:
:show-inheritance:
pygal.graph.pyramid module pygal.graph.pyramid module
-------------------------- --------------------------

4
docs/changelog.rst

@ -17,6 +17,10 @@ Changelog
* Maps are now plugins, they are removed from pygal core and moved to packages (pygal_maps_world, pygal_maps_fr, pygal_maps_ch, ...) (#225) * Maps are now plugins, they are removed from pygal core and moved to packages (pygal_maps_world, pygal_maps_fr, pygal_maps_ch, ...) (#225)
* Dot now supports negative values * Dot now supports negative values
* Fix dot with log scale (#201) * Fix dot with log scale (#201)
* Fix y_labels behaviour for lines
* Fix x_labels and y_labels behaviour for xy like
* Improve gauge a bit
* Finally allow call chains on add
1.7.0 1.7.0
===== =====

3
docs/documentation/builtin_styles.rst

@ -9,7 +9,8 @@ Default
.. pygal-code:: .. pygal-code::
chart = pygal.StackedLine(fill=True, interpolate='cubic') from pygal.style import DefaultStyle
chart = pygal.StackedLine(fill=True, interpolate='cubic', style=DefaultStyle) # Setting style here is not necessary
chart.add('A', [1, 3, 5, 16, 13, 3, 7]) chart.add('A', [1, 3, 5, 16, 13, 3, 7])
chart.add('B', [5, 2, 3, 2, 5, 7, 17]) chart.add('B', [5, 2, 3, 2, 5, 7, 17])
chart.add('C', [6, 10, 9, 7, 3, 1, 0]) chart.add('C', [6, 10, 9, 7, 3, 1, 0])

2
docs/documentation/parametric_styles.rst

@ -158,7 +158,7 @@ Desaturate
.. pygal-code:: .. pygal-code::
from pygal.style import DesaturateStyle from pygal.style import DesaturateStyle
desaturate_style = DesaturateStyle('#8322dd') desaturate_style = DesaturateStyle('#8322dd', step=8)
chart = pygal.StackedLine(fill=True, interpolate='cubic', style=desaturate_style) chart = pygal.StackedLine(fill=True, interpolate='cubic', style=desaturate_style)
chart.add('A', [1, 3, 5, 16, 13, 3, 7]) chart.add('A', [1, 3, 5, 16, 13, 3, 7])
chart.add('B', [5, 2, 3, 2, 5, 7, 17]) chart.add('B', [5, 2, 3, 2, 5, 7, 17])

6
docs/documentation/sparks.rst

@ -21,14 +21,14 @@ Sparklines support the same options as normal charts but for those that are over
.. pygal-code:: sparkline .. pygal-code:: sparkline
from pygal.style import RTDStyle chart = pygal.Line(interpolate='cubic')
chart = pygal.Line(style=RTDStyle, interpolate='cubic')
chart.add('', [1, 3, 5, 16, 13, 3, 7]) chart.add('', [1, 3, 5, 16, 13, 3, 7])
chart.render_sparkline() chart.render_sparkline()
.. pygal-code:: sparkline .. pygal-code:: sparkline
chart = pygal.Line() from pygal.style import LightSolarizedStyle
chart = pygal.Line(style=LightSolarizedStyle)
chart.add('', [1, 3, 5, 16, 13, 3, 7, 9, 2, 1, 4, 9, 12, 10, 12, 16, 14, 12, 7, 2]) chart.add('', [1, 3, 5, 16, 13, 3, 7, 9, 2, 1, 4, 9, 12, 10, 12, 16, 14, 12, 7, 2])
chart.render_sparkline(width=500, height=25, show_dots=True) chart.render_sparkline(width=500, height=25, show_dots=True)

4
docs/documentation/types/bar.rst

@ -1,5 +1,5 @@
Bar charts / Histograms Bar
----------------------- ---
Basic Basic
~~~~~ ~~~~~

4
docs/documentation/types/box.rst

@ -1,5 +1,5 @@
Box plot Box
-------- ---
Basic Basic
~~~~~ ~~~~~

16
docs/documentation/types/dot.rst

@ -1,5 +1,5 @@
Dot charts Dot
---------- ---
Basic Basic
~~~~~ ~~~~~
@ -15,3 +15,15 @@ Punch card like chart:
dot_chart.add('Firefox', [7473, 8099, 11700, 2651, 6361, 1044, 3797, 9450]) dot_chart.add('Firefox', [7473, 8099, 11700, 2651, 6361, 1044, 3797, 9450])
dot_chart.add('Opera', [3472, 2933, 4203, 5229, 5810, 1828, 9013, 4669]) dot_chart.add('Opera', [3472, 2933, 4203, 5229, 5810, 1828, 9013, 4669])
dot_chart.add('IE', [43, 41, 59, 79, 144, 136, 34, 102]) dot_chart.add('IE', [43, 41, 59, 79, 144, 136, 34, 102])
Negative
~~~~~~~~
Negative values are also supported, drawing the dot empty:
.. pygal-code:: 600 300
dot_chart = pygal.Dot(x_label_rotation=30)
dot_chart.add('Normal', [10, 50, 76, 80, 25])
dot_chart.add('With negatives', [0, -34, -29, 39, -75])

4
docs/documentation/types/funnel.rst

@ -1,5 +1,5 @@
Funnel charts Funnel
------------- ------
Basic Basic
~~~~~ ~~~~~

9
docs/documentation/types/gauge.rst

@ -1,16 +1,15 @@
Gauge charts Gauge
------------ -----
Basic Basic
~~~~~ ~~~~~
Simple gauge chart: Gauge chart:
.. pygal-code:: .. pygal-code:: 600 550
gauge_chart = pygal.Gauge(human_readable=True) gauge_chart = pygal.Gauge(human_readable=True)
gauge_chart.title = 'DeltaBlue V8 benchmark results' gauge_chart.title = 'DeltaBlue V8 benchmark results'
gauge_chart.x_labels = ['Richards', 'DeltaBlue', 'Crypto', 'RayTrace', 'EarleyBoyer', 'RegExp', 'Splay', 'NavierStokes']
gauge_chart.range = [0, 10000] gauge_chart.range = [0, 10000]
gauge_chart.add('Chrome', 8212) gauge_chart.add('Chrome', 8212)
gauge_chart.add('Firefox', 8099) gauge_chart.add('Firefox', 8099)

14
docs/documentation/types/histogram.rst

@ -0,0 +1,14 @@
Histogram
---------
Basic
~~~~~
Histogram are special bars that take 3 values for a bar: the ordinate height, the abscissa start and the abscissa end.
.. pygal-code::
hist = pygal.Histogram()
hist.add('Wide bars', [(5, 0, 10), (4, 5, 13), (2, 0, 15)])
hist.add('Narrow bars', [(10, 1, 2), (12, 4, 4.5), (8, 11, 13)])

1
docs/documentation/types/index.rst

@ -8,6 +8,7 @@ pygal provides various kinds of charts:
line line
bar bar
histogram
xy xy
pie pie
radar radar

4
docs/documentation/types/line.rst

@ -1,5 +1,5 @@
Line charts Line
----------- ----
Basic Basic
~~~~~ ~~~~~

496
docs/documentation/types/maps/index.rst

@ -1,491 +1,13 @@
Worldmap charts Maps
--------------- ----
Basic Maps are now packaged separately to keep pygal a reasonable sized package.
~~~~~
Highlight some countries: There are currently 3 available packages:
.. pygal-code:: .. toctree::
:maxdepth: 2
worldmap_chart = pygal.Worldmap()
worldmap_chart.title = 'Some countries'
worldmap_chart.add('F countries', ['fr', 'fi'])
worldmap_chart.add('M countries', ['ma', 'mc', 'md', 'me', 'mg',
'mk', 'ml', 'mm', 'mn', 'mo',
'mr', 'mt', 'mu', 'mv', 'mw',
'mx', 'my', 'mz'])
worldmap_chart.add('U countries', ['ua', 'ug', 'us', 'uy', 'uz'])
You can also specify an number for a country:
.. pygal-code::
worldmap_chart = pygal.Worldmap()
worldmap_chart.title = 'Minimum deaths by capital punishement (source: Amnesty International)'
worldmap_chart.add('In 2012', {
'af': 14,
'bd': 1,
'by': 3,
'cn': 1000,
'gm': 9,
'in': 1,
'ir': 314,
'iq': 129,
'jp': 7,
'kp': 6,
'pk': 1,
'ps': 6,
'sa': 79,
'so': 6,
'sd': 5,
'tw': 6,
'ae': 1,
'us': 43,
'ye': 28
})
The following countries are supported:
- `ad`: Andorra
- `ae`: United Arab Emirates
- `af`: Afghanistan
- `al`: Albania
- `am`: Armenia
- `ao`: Angola
- `aq`: Antarctica
- `ar`: Argentina
- `at`: Austria
- `au`: Australia
- `az`: Azerbaijan
- `ba`: Bosnia and Herzegovina
- `bd`: Bangladesh
- `be`: Belgium
- `bf`: Burkina Faso
- `bg`: Bulgaria
- `bh`: Bahrain
- `bi`: Burundi
- `bj`: Benin
- `bn`: Brunei Darussalam
- `bo`: Bolivia, Plurinational State of
- `br`: Brazil
- `bt`: Bhutan
- `bw`: Botswana
- `by`: Belarus
- `bz`: Belize
- `ca`: Canada
- `cd`: Congo, the Democratic Republic of the
- `cf`: Central African Republic
- `cg`: Congo
- `ch`: Switzerland
- `ci`: Cote d'Ivoire
- `cl`: Chile
- `cm`: Cameroon
- `cn`: China
- `co`: Colombia
- `cr`: Costa Rica
- `cu`: Cuba
- `cv`: Cape Verde
- `cy`: Cyprus
- `cz`: Czech Republic
- `de`: Germany
- `dj`: Djibouti
- `dk`: Denmark
- `do`: Dominican Republic
- `dz`: Algeria
- `ec`: Ecuador
- `ee`: Estonia
- `eg`: Egypt
- `eh`: Western Sahara
- `er`: Eritrea
- `es`: Spain
- `et`: Ethiopia
- `fi`: Finland
- `fr`: France
- `ga`: Gabon
- `gb`: United Kingdom
- `ge`: Georgia
- `gf`: French Guiana
- `gh`: Ghana
- `gl`: Greenland
- `gm`: Gambia
- `gn`: Guinea
- `gq`: Equatorial Guinea
- `gr`: Greece
- `gt`: Guatemala
- `gu`: Guam
- `gw`: Guinea-Bissau
- `gy`: Guyana
- `hk`: Hong Kong
- `hn`: Honduras
- `hr`: Croatia
- `ht`: Haiti
- `hu`: Hungary
- `id`: Indonesia
- `ie`: Ireland
- `il`: Israel
- `in`: India
- `iq`: Iraq
- `ir`: Iran, Islamic Republic of
- `is`: Iceland
- `it`: Italy
- `jm`: Jamaica
- `jo`: Jordan
- `jp`: Japan
- `ke`: Kenya
- `kg`: Kyrgyzstan
- `kh`: Cambodia
- `kp`: Korea, Democratic People's Republic of
- `kr`: Korea, Republic of
- `kw`: Kuwait
- `kz`: Kazakhstan
- `la`: Lao People's Democratic Republic
- `lb`: Lebanon
- `li`: Liechtenstein
- `lk`: Sri Lanka
- `lr`: Liberia
- `ls`: Lesotho
- `lt`: Lithuania
- `lu`: Luxembourg
- `lv`: Latvia
- `ly`: Libyan Arab Jamahiriya
- `ma`: Morocco
- `mc`: Monaco
- `md`: Moldova, Republic of
- `me`: Montenegro
- `mg`: Madagascar
- `mk`: Macedonia, the former Yugoslav Republic of
- `ml`: Mali
- `mm`: Myanmar
- `mn`: Mongolia
- `mo`: Macao
- `mr`: Mauritania
- `mt`: Malta
- `mu`: Mauritius
- `mv`: Maldives
- `mw`: Malawi
- `mx`: Mexico
- `my`: Malaysia
- `mz`: Mozambique
- `na`: Namibia
- `ne`: Niger
- `ng`: Nigeria
- `ni`: Nicaragua
- `nl`: Netherlands
- `no`: Norway
- `np`: Nepal
- `nz`: New Zealand
- `om`: Oman
- `pa`: Panama
- `pe`: Peru
- `pg`: Papua New Guinea
- `ph`: Philippines
- `pk`: Pakistan
- `pl`: Poland
- `pr`: Puerto Rico
- `ps`: Palestine, State of
- `pt`: Portugal
- `py`: Paraguay
- `re`: Reunion
- `ro`: Romania
- `rs`: Serbia
- `ru`: Russian Federation
- `rw`: Rwanda
- `sa`: Saudi Arabia
- `sc`: Seychelles
- `sd`: Sudan
- `se`: Sweden
- `sg`: Singapore
- `sh`: Saint Helena, Ascension and Tristan da Cunha
- `si`: Slovenia
- `sk`: Slovakia
- `sl`: Sierra Leone
- `sm`: San Marino
- `sn`: Senegal
- `so`: Somalia
- `sr`: Suriname
- `st`: Sao Tome and Principe
- `sv`: El Salvador
- `sy`: Syrian Arab Republic
- `sz`: Swaziland
- `td`: Chad
- `tg`: Togo
- `th`: Thailand
- `tj`: Tajikistan
- `tl`: Timor-Leste
- `tm`: Turkmenistan
- `tn`: Tunisia
- `tr`: Turkey
- `tw`: Taiwan, Province of China
- `tz`: Tanzania, United Republic of
- `ua`: Ukraine
- `ug`: Uganda
- `us`: United States
- `uy`: Uruguay
- `uz`: Uzbekistan
- `va`: Holy See (Vatican City State)
- `ve`: Venezuela, Bolivarian Republic of
- `vn`: Viet Nam
- `ye`: Yemen
- `yt`: Mayotte
- `za`: South Africa
- `zm`: Zambia
- `zw`: Zimbabwe
Country charts
--------------
As of now, only France is available. As other country are implemented, this will be externalized in other packages.
(Please submit pull requests :))
French map
~~~~~~~~~~
Highlight some departments:
.. pygal-code::
fr_chart = pygal.FrenchMap_Departments()
fr_chart.title = 'Some departments'
fr_chart.add('Métropole', ['69', '92', '13'])
fr_chart.add('Corse', ['2A', '2B'])
fr_chart.add('DOM COM', ['971', '972', '973', '974'])
You can also specify an number for a department:
.. pygal-code::
fr_chart = pygal.FrenchMap_Departments(human_readable=True)
fr_chart.title = 'Population by department'
fr_chart.add('In 2011', {
'01': 603827,
'02': 541302,
'03': 342729,
'04': 160959,
'05': 138605,
'06': 1081244,
'07': 317277,
'08': 283110,
'09': 152286,
'10': 303997,
'11': 359967,
'12': 275813,
'13': 1975896,
'14': 685262,
'15': 147577,
'16': 352705,
'17': 625682,
'18': 311694,
'19': 242454,
'2A': 145846,
'2B': 168640,
'21': 525931,
'22': 594375,
'23': 122560,
'24': 415168,
'25': 529103,
'26': 487993,
'27': 588111,
'28': 430416,
'29': 899870,
'30': 718357,
'31': 1260226,
'32': 188893,
'33': 1463662,
'34': 1062036,
'35': 996439,
'36': 230175,
'37': 593683,
'38': 1215212,
'39': 261294,
'40': 387929,
'41': 331280,
'42': 749053,
'43': 224907,
'44': 1296364,
'45': 659587,
'46': 174754,
'47': 330866,
'48': 77156,
'49': 790343,
'50': 499531,
'51': 566571,
'52': 182375,
'53': 307031,
'54': 733124,
'55': 193557,
'56': 727083,
'57': 1045146,
'58': 218341,
'59': 2579208,
'60': 805642,
'61': 290891,
'62': 1462807,
'63': 635469,
'64': 656608,
'65': 229228,
'66': 452530,
'67': 1099269,
'68': 753056,
'69': 1744236,
'70': 239695,
'71': 555999,
'72': 565718,
'73': 418949,
'74': 746994,
'75': 2249975,
'76': 1251282,
'77': 1338427,
'78': 1413635,
'79': 370939,
'80': 571211,
'81': 377675,
'82': 244545,
'83': 1012735,
'84': 546630,
'85': 641657,
'86': 428447,
'87': 376058,
'88': 378830,
'89': 342463,
'90': 143348,
'91': 1225191,
'92': 1581628,
'93': 1529928,
'94': 1333702,
'95': 1180365,
'971': 404635,
'972': 392291,
'973': 237549,
'974': 828581,
'976': 212645
})
You can do the same with regions:
.. pygal-code::
fr_chart = pygal.FrenchMap_Regions()
fr_chart.title = 'Some regions'
fr_chart.add('Métropole', ['82', '11', '93'])
fr_chart.add('Corse', ['94'])
fr_chart.add('DOM COM', ['01', '02', '03', '04'])
You can also specify a number for a region and use a department to region aggregation:
.. pygal-code::
from pygal.graph.frenchmap import aggregate_regions
fr_chart = pygal.FrenchMap_Regions(human_readable=True)
fr_chart.title = 'Population by region'
fr_chart.add('In 2011', aggregate_regions({
'01': 603827,
'02': 541302,
'03': 342729,
'04': 160959,
'05': 138605,
'06': 1081244,
'07': 317277,
'08': 283110,
'09': 152286,
'10': 303997,
'11': 359967,
'12': 275813,
'13': 1975896,
'14': 685262,
'15': 147577,
'16': 352705,
'17': 625682,
'18': 311694,
'19': 242454,
'2A': 145846,
'2B': 168640,
'21': 525931,
'22': 594375,
'23': 122560,
'24': 415168,
'25': 529103,
'26': 487993,
'27': 588111,
'28': 430416,
'29': 899870,
'30': 718357,
'31': 1260226,
'32': 188893,
'33': 1463662,
'34': 1062036,
'35': 996439,
'36': 230175,
'37': 593683,
'38': 1215212,
'39': 261294,
'40': 387929,
'41': 331280,
'42': 749053,
'43': 224907,
'44': 1296364,
'45': 659587,
'46': 174754,
'47': 330866,
'48': 77156,
'49': 790343,
'50': 499531,
'51': 566571,
'52': 182375,
'53': 307031,
'54': 733124,
'55': 193557,
'56': 727083,
'57': 1045146,
'58': 218341,
'59': 2579208,
'60': 805642,
'61': 290891,
'62': 1462807,
'63': 635469,
'64': 656608,
'65': 229228,
'66': 452530,
'67': 1099269,
'68': 753056,
'69': 1744236,
'70': 239695,
'71': 555999,
'72': 565718,
'73': 418949,
'74': 746994,
'75': 2249975,
'76': 1251282,
'77': 1338427,
'78': 1413635,
'79': 370939,
'80': 571211,
'81': 377675,
'82': 244545,
'83': 1012735,
'84': 546630,
'85': 641657,
'86': 428447,
'87': 376058,
'88': 378830,
'89': 342463,
'90': 143348,
'91': 1225191,
'92': 1581628,
'93': 1529928,
'94': 1333702,
'95': 1180365,
'971': 404635,
'972': 392291,
'973': 237549,
'974': 828581,
'976': 212645
}))
pygal_maps_world
pygal_maps_fr
pygal_maps_ch

61
docs/documentation/types/maps/pygal_maps_ch.rst

@ -0,0 +1,61 @@
Swiss map
~~~~~~~~~
Installing
~~~~~~~~~~
The swiss map plugin can be installed by doing a:
.. code-block:: bash
pip install pygal_maps_ch
Canton
~~~~~~
Then you will have access to the ``pygal.maps.ch`` module.
You can now plot cantons (see below for the list):
.. pygal-code::
ch_chart = pygal.maps.ch.Cantons()
ch_chart.title = 'Some cantons'
ch_chart.add('Cantons 1', ['kt-zh', 'kt-be', 'kt-nw'])
ch_chart.add('Cantons 2', ['kt-ow', 'kt-bs', 'kt-ne'])
Canton list
~~~~~~~~~~~
===== ======
code Canton
===== ======
kt-zh ZH
kt-be BE
kt-lu LU
kt-ju JH
kt-ur UR
kt-sz SZ
kt-ow OW
kt-nw NW
kt-gl GL
kt-zg ZG
kt-fr FR
kt-so SO
kt-bl BL
kt-bs BS
kt-sh SH
kt-ar AR
kt-ai AI
kt-sg SG
kt-gr GR
kt-ag AG
kt-tg TG
kt-ti TI
kt-vd VD
kt-vs VS
kt-ne NE
kt-ge GE
===== ======

213
docs/documentation/types/maps/pygal_maps_fr.rst

@ -0,0 +1,213 @@
French map
~~~~~~~~~~
Installing
~~~~~~~~~~
The french map plugin can be installed by doing a:
.. code-block:: bash
pip install pygal_maps_fr
Department
~~~~~~~~~~
Then you will have access to the ``pygal.maps.fr`` module.
You can now plot departments (see below for the list):
.. pygal-code::
fr_chart = pygal.maps.fr.Departments()
fr_chart.title = 'Some departments'
fr_chart.add('Métropole', ['69', '92', '13'])
fr_chart.add('Corse', ['2A', '2B'])
fr_chart.add('DOM COM', ['971', '972', '973', '974'])
Or specify an number for a department:
.. pygal-code::
fr_chart = pygal.maps.fr.Departments(human_readable=True)
fr_chart.title = 'Population by department'
fr_chart.add('In 2011', {
'01': 603827, '02': 541302, '03': 342729, '04': 160959, '05': 138605, '06': 1081244, '07': 317277, '08': 283110, '09': 152286, '10': 303997, '11': 359967, '12': 275813, '13': 1975896, '14': 685262, '15': 147577, '16': 352705, '17': 625682, '18': 311694, '19': 242454, '2A': 145846, '2B': 168640, '21': 525931, '22': 594375, '23': 122560, '24': 415168, '25': 529103, '26': 487993, '27': 588111, '28': 430416, '29': 899870, '30': 718357, '31': 1260226, '32': 188893, '33': 1463662, '34': 1062036, '35': 996439, '36': 230175, '37': 593683, '38': 1215212, '39': 261294, '40': 387929, '41': 331280, '42': 749053, '43': 224907, '44': 1296364, '45': 659587, '46': 174754, '47': 330866, '48': 77156, '49': 790343, '50': 499531, '51': 566571, '52': 182375, '53': 307031, '54': 733124, '55': 193557, '56': 727083, '57': 1045146, '58': 218341, '59': 2579208, '60': 805642, '61': 290891, '62': 1462807, '63': 635469, '64': 656608, '65': 229228, '66': 452530, '67': 1099269, '68': 753056, '69': 1744236, '70': 239695, '71': 555999, '72': 565718, '73': 418949, '74': 746994, '75': 2249975, '76': 1251282, '77': 1338427, '78': 1413635, '79': 370939, '80': 571211, '81': 377675, '82': 244545, '83': 1012735, '84': 546630, '85': 641657, '86': 428447, '87': 376058, '88': 378830, '89': 342463, '90': 143348, '91': 1225191, '92': 1581628, '93': 1529928, '94': 1333702, '95': 1180365, '971': 404635, '972': 392291, '973': 237549, '974': 828581, '976': 212645
})
Regions
~~~~~~~
You can do the same with regions:
.. pygal-code::
fr_chart = pygal.maps.fr.Regions()
fr_chart.title = 'Some regions'
fr_chart.add('Métropole', ['82', '11', '93'])
fr_chart.add('Corse', ['94'])
fr_chart.add('DOM COM', ['01', '02', '03', '04'])
You can also specify a number for a region and use a department to region aggregation:
.. pygal-code::
from pygal.maps.fr import aggregate_regions
fr_chart = pygal.maps.fr.Regions(human_readable=True)
fr_chart.title = 'Population by region'
fr_chart.add('In 2011', aggregate_regions({
'01': 603827, '02': 541302, '03': 342729, '04': 160959, '05': 138605, '06': 1081244, '07': 317277, '08': 283110, '09': 152286, '10': 303997, '11': 359967, '12': 275813, '13': 1975896, '14': 685262, '15': 147577, '16': 352705, '17': 625682, '18': 311694, '19': 242454, '2A': 145846, '2B': 168640, '21': 525931, '22': 594375, '23': 122560, '24': 415168, '25': 529103, '26': 487993, '27': 588111, '28': 430416, '29': 899870, '30': 718357, '31': 1260226, '32': 188893, '33': 1463662, '34': 1062036, '35': 996439, '36': 230175, '37': 593683, '38': 1215212, '39': 261294, '40': 387929, '41': 331280, '42': 749053, '43': 224907, '44': 1296364, '45': 659587, '46': 174754, '47': 330866, '48': 77156, '49': 790343, '50': 499531, '51': 566571, '52': 182375, '53': 307031, '54': 733124, '55': 193557, '56': 727083, '57': 1045146, '58': 218341, '59': 2579208, '60': 805642, '61': 290891, '62': 1462807, '63': 635469, '64': 656608, '65': 229228, '66': 452530, '67': 1099269, '68': 753056, '69': 1744236, '70': 239695, '71': 555999, '72': 565718, '73': 418949, '74': 746994, '75': 2249975, '76': 1251282, '77': 1338427, '78': 1413635, '79': 370939, '80': 571211, '81': 377675, '82': 244545, '83': 1012735, '84': 546630, '85': 641657, '86': 428447, '87': 376058, '88': 378830, '89': 342463, '90': 143348, '91': 1225191, '92': 1581628, '93': 1529928, '94': 1333702, '95': 1180365, '971': 404635, '972': 392291, '973': 237549, '974': 828581, '976': 212645
}))
Department list
~~~~~~~~~~~~~~~
==== ========================
code Department
==== ========================
01 Ain
02 Aisne
03 Allier
04 Alpes-de-Haute-Provence
05 Hautes-Alpes
06 Alpes-Maritimes
07 Ardèche
08 Ardennes
09 Ariège
10 Aube
11 Aude
12 Aveyron
13 Bouches-du-Rhône
14 Calvados
15 Cantal
16 Charente
17 Charente-Maritime
18 Cher
19 Corrèze
2A Corse-du-Sud
2B Haute-Corse
21 Côte-d'Or
22 Côtes-d'Armor
23 Creuse
24 Dordogne
25 Doubs
26 Drôme
27 Eure
28 Eure-et-Loir
29 Finistère
30 Gard
31 Haute-Garonne
32 Gers
33 Gironde
34 Hérault
35 Ille-et-Vilaine
36 Indre
37 Indre-et-Loire
38 Isère
39 Jura
40 Landes
41 Loir-et-Cher
42 Loire
43 Haute-Loire
44 Loire-Atlantique
45 Loiret
46 Lot
47 Lot-et-Garonne
48 Lozère
49 Maine-et-Loire
50 Manche
51 Marne
52 Haute-Marne
53 Mayenne
54 Meurthe-et-Moselle
55 Meuse
56 Morbihan
57 Moselle
58 Nièvre
59 Nord
60 Oise
61 Orne
62 Pas-de-Calais
63 Puy-de-Dôme
64 Pyrénées-Atlantiques
65 Hautes-Pyrénées
66 Pyrénées-Orientales
67 Bas-Rhin
68 Haut-Rhin
69 Rhône
70 Haute-Saône
71 Saône-et-Loire
72 Sarthe
73 Savoie
74 Haute-Savoie
75 Paris
76 Seine-Maritime
77 Seine-et-Marne
78 Yvelines
79 Deux-Sèvres
80 Somme
81 Tarn
82 Tarn-et-Garonne
83 Var
84 Vaucluse
85 Vendée
86 Vienne
87 Haute-Vienne
88 Vosges
89 Yonne
90 Territoire de Belfort
91 Essonne
92 Hauts-de-Seine
93 Seine-Saint-Denis
94 Val-de-Marne
95 Val-d'Oise
971 Guadeloupe
972 Martinique
973 Guyane
974 Réunion
975 Saint Pierre et Miquelon
976 Mayotte
==== ========================
Region list
~~~~~~~~~~~
==== ===================
code Region
==== ===================
11 Île-de-France
21 Champagne-Ardenne
22 Picardie
23 Haute-Normandie
24 Centre
25 Basse-Normandie
26 Bourgogne
31 Nord-Pas-de-Calais
41 Lorraine
42 Alsace
43 Franche-Comté
52 Pays-de-la-Loire
53 Bretagne
54 Poitou-Charentes
72 Aquitaine
73 Midi-Pyrénées
74 Limousin
82 Rhône-Alpes
83 Auvergne
91 Languedoc-Roussillon
93 Provence-Alpes-Côte d'Azur
94 Corse
01 Guadeloupe
02 Martinique
03 Guyane
04 Réunion
05 Saint Pierre et Miquelon
06 Mayotte
==== ===================

286
docs/documentation/types/maps/pygal_maps_world.rst

@ -0,0 +1,286 @@
World map
---------
Installing
~~~~~~~~~~
The world map plugin can be installed by doing a:
.. code-block:: bash
pip install pygal_maps_world
Countries
~~~~~~~~~
Then you will have acces to the ``pygal.maps.world`` module.
Now you can plot countries by specifying their code (see below for the big list of supported country codes)
.. pygal-code::
worldmap_chart = pygal.maps.world.World()
worldmap_chart.title = 'Some countries'
worldmap_chart.add('F countries', ['fr', 'fi'])
worldmap_chart.add('M countries', ['ma', 'mc', 'md', 'me', 'mg',
'mk', 'ml', 'mm', 'mn', 'mo',
'mr', 'mt', 'mu', 'mv', 'mw',
'mx', 'my', 'mz'])
worldmap_chart.add('U countries', ['ua', 'ug', 'us', 'uy', 'uz'])
You can also specify a value for a country:
.. pygal-code::
worldmap_chart = pygal.maps.world.World()
worldmap_chart.title = 'Minimum deaths by capital punishement (source: Amnesty International)'
worldmap_chart.add('In 2012', {
'af': 14,
'bd': 1,
'by': 3,
'cn': 1000,
'gm': 9,
'in': 1,
'ir': 314,
'iq': 129,
'jp': 7,
'kp': 6,
'pk': 1,
'ps': 6,
'sa': 79,
'so': 6,
'sd': 5,
'tw': 6,
'ae': 1,
'us': 43,
'ye': 28
})
Continents
~~~~~~~~~~
You have also access to continents:
.. pygal-code::
supra = pygal.maps.world.SupranationalWorld()
supra.add('Asia', [('asia', 1)])
supra.add('Europe', [('europe', 1)])
supra.add('Africa', [('africa', 1)])
supra.add('North america', [('north_america', 1)])
supra.add('South america', [('south_america', 1)])
supra.add('Oceania', [('oceania', 1)])
supra.add('Antartica', [('antartica', 1)])
Coutry code list
~~~~~~~~~~~~~~~~
The following countries are supported:
==== ============================================
code Country
==== ============================================
ad Andorra
ae United Arab Emirates
af Afghanistan
al Albania
am Armenia
ao Angola
aq Antarctica
ar Argentina
at Austria
au Australia
az Azerbaijan
ba Bosnia and Herzegovina
bd Bangladesh
be Belgium
bf Burkina Faso
bg Bulgaria
bh Bahrain
bi Burundi
bj Benin
bn Brunei Darussalam
bo Bolivia, Plurinational State of
br Brazil
bt Bhutan
bw Botswana
by Belarus
bz Belize
ca Canada
cd Congo, the Democratic Republic of the
cf Central African Republic
cg Congo
ch Switzerland
ci Cote d'Ivoire
cl Chile
cm Cameroon
cn China
co Colombia
cr Costa Rica
cu Cuba
cv Cape Verde
cy Cyprus
cz Czech Republic
de Germany
dj Djibouti
dk Denmark
do Dominican Republic
dz Algeria
ec Ecuador
ee Estonia
eg Egypt
eh Western Sahara
er Eritrea
es Spain
et Ethiopia
fi Finland
fr France
ga Gabon
gb United Kingdom
ge Georgia
gf French Guiana
gh Ghana
gl Greenland
gm Gambia
gn Guinea
gq Equatorial Guinea
gr Greece
gt Guatemala
gu Guam
gw Guinea-Bissau
gy Guyana
hk Hong Kong
hn Honduras
hr Croatia
ht Haiti
hu Hungary
id Indonesia
ie Ireland
il Israel
in India
iq Iraq
ir Iran, Islamic Republic of
is Iceland
it Italy
jm Jamaica
jo Jordan
jp Japan
ke Kenya
kg Kyrgyzstan
kh Cambodia
kp Korea, Democratic People's Republic of
kr Korea, Republic of
kw Kuwait
kz Kazakhstan
la Lao People's Democratic Republic
lb Lebanon
li Liechtenstein
lk Sri Lanka
lr Liberia
ls Lesotho
lt Lithuania
lu Luxembourg
lv Latvia
ly Libyan Arab Jamahiriya
ma Morocco
mc Monaco
md Moldova, Republic of
me Montenegro
mg Madagascar
mk Macedonia, the former Yugoslav Republic of
ml Mali
mm Myanmar
mn Mongolia
mo Macao
mr Mauritania
mt Malta
mu Mauritius
mv Maldives
mw Malawi
mx Mexico
my Malaysia
mz Mozambique
na Namibia
ne Niger
ng Nigeria
ni Nicaragua
nl Netherlands
no Norway
np Nepal
nz New Zealand
om Oman
pa Panama
pe Peru
pg Papua New Guinea
ph Philippines
pk Pakistan
pl Poland
pr Puerto Rico
ps Palestine, State of
pt Portugal
py Paraguay
re Reunion
ro Romania
rs Serbia
ru Russian Federation
rw Rwanda
sa Saudi Arabia
sc Seychelles
sd Sudan
se Sweden
sg Singapore
sh Saint Helena, Ascension and Tristan da Cunha
si Slovenia
sk Slovakia
sl Sierra Leone
sm San Marino
sn Senegal
so Somalia
sr Suriname
st Sao Tome and Principe
sv El Salvador
sy Syrian Arab Republic
sz Swaziland
td Chad
tg Togo
th Thailand
tj Tajikistan
tl Timor-Leste
tm Turkmenistan
tn Tunisia
tr Turkey
tw Taiwan, Province of China
tz Tanzania, United Republic of
ua Ukraine
ug Uganda
us United States
uy Uruguay
uz Uzbekistan
va Holy See (Vatican City State)
ve Venezuela, Bolivarian Republic of
vn Viet Nam
ye Yemen
yt Mayotte
za South Africa
zm Zambia
zw Zimbabwe
==== ============================================
Continent list
~~~~~~~~~~~~~~
============= =============
code name
============= =============
asia Asia
europe Europe
africa Africa
north_america North America
south_america South America
oceania Oceania
antartica Antartica
============= =============

28
docs/documentation/types/pie.rst

@ -32,3 +32,31 @@ Same pie but divided in sub category:
pie_chart.add('Chrome', [.3, .9, 17.1, 15.3, .6, .5, 1.6]) pie_chart.add('Chrome', [.3, .9, 17.1, 15.3, .6, .5, 1.6])
pie_chart.add('Safari', [4.4, .1]) pie_chart.add('Safari', [4.4, .1])
pie_chart.add('Opera', [.1, 1.6, .1, .5]) pie_chart.add('Opera', [.1, 1.6, .1, .5])
Donut
~~~~~
It is possible to specify an inner radius to get a donut:
.. pygal-code::
pie_chart = pygal.Pie(inner_radius=.4)
pie_chart.title = 'Browser usage in February 2012 (in %)'
pie_chart.add('IE', 19.5)
pie_chart.add('Firefox', 36.6)
pie_chart.add('Chrome', 36.3)
pie_chart.add('Safari', 4.5)
pie_chart.add('Opera', 2.3)
or a ring:
.. pygal-code::
pie_chart = pygal.Pie(inner_radius=.75)
pie_chart.title = 'Browser usage in February 2012 (in %)'
pie_chart.add('IE', 19.5)
pie_chart.add('Firefox', 36.6)
pie_chart.add('Chrome', 36.3)
pie_chart.add('Safari', 4.5)
pie_chart.add('Opera', 2.3)

4
docs/documentation/types/pyramid.rst

@ -1,5 +1,5 @@
Pyramid charts Pyramid
-------------- -------
Basic Basic
~~~~~ ~~~~~

6
docs/documentation/types/radar.rst

@ -1,12 +1,12 @@
Radar charts Radar
------------ -----
Basic Basic
~~~~~ ~~~~~
Simple Kiviat diagram: Simple Kiviat diagram:
.. pygal-code:: .. pygal-code:: 600 500
radar_chart = pygal.Radar() radar_chart = pygal.Radar()
radar_chart.title = 'V8 benchmark results' radar_chart.title = 'V8 benchmark results'

4
docs/documentation/types/xy.rst

@ -1,5 +1,5 @@
XY charts XY
--------- --
Basic Basic
~~~~~ ~~~~~

18
docs/ext/pygal_sphinx_directives.py

@ -26,6 +26,19 @@ import base64
import docutils.core import docutils.core
import pygal import pygal
# Patch default style
pygal.config.Config.style.value = pygal.style.RotateStyle(
'#2980b9',
background='#fcfcfc',
plot_background='#ffffff',
foreground='#707070',
foreground_light='#404040',
foreground_dark='#a0a0a0',
opacity='.8',
opacity_hover='.9',
transition='400ms ease-in')
class PygalDirective(Directive): class PygalDirective(Directive):
"""Execute the given python file and puts its result in the document.""" """Execute the given python file and puts its result in the document."""
@ -68,9 +81,8 @@ class PygalDirective(Directive):
break break
if chart is None: if chart is None:
return [docutils.nodes.system_message( return [docutils.nodes.system_message(
'No instance of graph found', level=3)] 'No instance of graph found', level=3,
if not hasattr(chart, 'style'): type='ERROR', source='/')]
chart.style = pygal.style.RTDStyle
chart.config.width = width chart.config.width = width
chart.config.height = height chart.config.height = height
chart.explicit_size = True chart.explicit_size = True

9
docs/index.rst

@ -15,7 +15,7 @@ Sexy python charting
.. pygal:: 300 200 .. pygal:: 300 200
chart = pygal.Line(x_label_rotation=25, fill=True, style=pygal.style.NeonStyle, interpolate='cubic') chart = pygal.Line(x_label_rotation=25, fill=True, interpolate='cubic')
chart.x_labels = 'one', 'two', 'three', 'four', 'five' chart.x_labels = 'one', 'two', 'three', 'four', 'five'
chart.add('alpha', [1, 2, 3, 1, 2]) chart.add('alpha', [1, 2, 3, 1, 2])
chart.add('beta', [4, 3, 0, 1, 2]) chart.add('beta', [4, 3, 0, 1, 2])
@ -29,7 +29,7 @@ Sexy python charting
.. pygal:: 300 200 .. pygal:: 300 200
chart = pygal.Radar(fill=True, style=pygal.style.NeonStyle) chart = pygal.Radar(fill=True)
chart.x_labels = 'one', 'two', 'three', 'four', 'five' chart.x_labels = 'one', 'two', 'three', 'four', 'five'
chart.add('alpha', [1, 2, 3, 1, 2]) chart.add('alpha', [1, 2, 3, 1, 2])
chart.add('beta', [4, 3, 0, 1, 2]) chart.add('beta', [4, 3, 0, 1, 2])
@ -38,10 +38,9 @@ Sexy python charting
Simple python charting Simple python charting
====================== ======================
.. pygal-code:: .. pygal-code:: inline
chart = pygal.Bar() pygal.Bar().add('1', [1, 3, 3, 7]).add('2', [1, 6, 6, 4]).render()
chart.add('First', [1, 3, 3, 7])
Index Index

6
docs/installing.rst

@ -8,6 +8,8 @@ PyPI
pygal is `available on PyPI <http://pypi.python.org/pypi/pygal/>`_. pygal is `available on PyPI <http://pypi.python.org/pypi/pygal/>`_.
To install, just type as superuser:: To install, just type as superuser::
.. code-block:: bash
pip install pygal pip install pygal
@ -15,7 +17,9 @@ Git Repository
============== ==============
If you want the development version of pygal, take a look at the If you want the development version of pygal, take a look at the
:codelink:`git repository on GitHub`, or clone it with:: `git repository on GitHub <https://github.com/Kozea/pygal>`_, or clone it with::
.. code-block:: bash
git clone git://github.com/Kozea/pygal.git git clone git://github.com/Kozea/pygal.git

3
docs/requirements.txt

@ -0,0 +1,3 @@
pygal_maps_world
pygal_maps_ch
pygal_maps_fr

118
pygal/graph/base.py

@ -22,7 +22,7 @@ Base for pygal charts
""" """
from __future__ import division from __future__ import division
from pygal._compat import u, is_list_like, to_unicode from pygal._compat import is_list_like
from pygal.view import Margin, Box from pygal.view import Margin, Box
from pygal.config import Config from pygal.config import Config
from pygal.state import State from pygal.state import State
@ -34,12 +34,11 @@ from pygal.adapters import (
not_zero, positive, decimal_to_float) not_zero, positive, decimal_to_float)
from functools import reduce from functools import reduce
from uuid import uuid4 from uuid import uuid4
import io
import os import os
class BaseGraph(object): class BaseGraph(object):
"""Graphs commons""" """Chart internal behaviour related functions"""
_adapters = [] _adapters = []
@ -73,18 +72,6 @@ class BaseGraph(object):
return super(BaseGraph, self).__getattribute__(name) return super(BaseGraph, self).__getattribute__(name)
return getattr(self.state, name) return getattr(self.state, name)
def add(self, title, values, **kwargs):
"""Add a serie to this graph"""
if not is_list_like(values) and not isinstance(values, dict):
values = [values]
if kwargs.get('secondary', False):
self.raw_series2.append((title, values, kwargs))
else:
self.raw_series.append((title, values, kwargs))
def add_xml_filter(self, callback):
self.xml_filters.append(callback)
def prepare_values(self, raw, offset=0): def prepare_values(self, raw, offset=0):
"""Prepare the values to start with sane values""" """Prepare the values to start with sane values"""
from pygal.graph.map import BaseMap from pygal.graph.map import BaseMap
@ -231,107 +218,6 @@ class BaseGraph(object):
del self.state del self.state
self.state = None self.state = None
def render(self, is_unicode=False, **kwargs):
"""Render the graph, and return the svg string"""
self.setup(**kwargs)
svg = self.svg.render(
is_unicode=is_unicode, pretty_print=self.pretty_print)
self.teardown()
return svg
def render_tree(self):
"""Render the graph, and return (l)xml etree"""
self.setup()
svg = self.svg.root
for f in self.xml_filters:
svg = f(svg)
self.teardown()
return svg
def render_table(self, **kwargs):
# Import here to avoid lxml import
try:
from pygal.table import Table
except ImportError:
raise ImportError('You must install lxml to use render table')
return Table(self).render(**kwargs)
def render_pyquery(self):
"""Render the graph, and return a pyquery wrapped tree"""
from pyquery import PyQuery as pq
return pq(self.render(), parser='html')
def render_in_browser(self, **kwargs):
"""Render the graph, open it in your browser with black magic"""
try:
from lxml.html import open_in_browser
except ImportError:
raise ImportError('You must install lxml to use render in browser')
open_in_browser(self.render_tree(**kwargs), encoding='utf-8')
def render_response(self, **kwargs):
"""Render the graph, and return a Flask response"""
from flask import Response
return Response(self.render(**kwargs), mimetype='image/svg+xml')
def render_django_response(self, **kwargs):
"""Render the graph, and return a Django response"""
from django.http import HttpResponse
return HttpResponse(
self.render(**kwargs), content_type='image/svg+xml')
def render_to_file(self, filename, **kwargs):
"""Render the graph, and write it to filename"""
with io.open(filename, 'w', encoding='utf-8') as f:
f.write(self.render(is_unicode=True, **kwargs))
def render_to_png(self, filename=None, dpi=72, **kwargs):
"""Render the graph, convert it to png and write it to filename"""
import cairosvg
return cairosvg.svg2png(
bytestring=self.render(**kwargs), write_to=filename, dpi=dpi)
def render_sparktext(self, relative_to=None):
"""Make a mini text sparkline from chart"""
bars = u('▁▂▃▄▅▆▇█')
if len(self.raw_series) == 0:
return u('')
values = list(self.raw_series[0][1])
if len(values) == 0:
return u('')
chart = u('')
values = list(map(lambda x: max(x, 0), values))
vmax = max(values)
if relative_to is None:
relative_to = min(values)
if (vmax - relative_to) == 0:
chart = bars[0] * len(values)
return chart
divisions = len(bars) - 1
for value in values:
chart += bars[int(divisions *
(value - relative_to) / (vmax - relative_to))]
return chart
def render_sparkline(self, **kwargs):
spark_options = dict(
width=200,
height=50,
show_dots=False,
show_legend=False,
show_x_labels=False,
show_y_labels=False,
spacing=0,
margin=5,
explicit_size=True
)
spark_options.update(kwargs)
return self.render(**spark_options)
def _repr_svg_(self): def _repr_svg_(self):
"""Display svg in IPython notebook""" """Display svg in IPython notebook"""
return self.render(disable_xml_declaration=True) return self.render(disable_xml_declaration=True)

27
pygal/graph/gauge.py

@ -29,6 +29,7 @@ from pygal.graph.graph import Graph
class Gauge(Graph): class Gauge(Graph):
"""Gauge graph""" """Gauge graph"""
needle_width = 1 / 20
def _set_view(self): def _set_view(self):
if self.logarithmic: if self.logarithmic:
@ -46,7 +47,10 @@ class Gauge(Graph):
for i, theta in enumerate(serie.values): for i, theta in enumerate(serie.values):
if theta is None: if theta is None:
continue continue
fmt = lambda x: '%f %f' % x
def point(x, y):
return '%f %f' % self.view((x, y))
value = self._format(serie.values[i]) value = self._format(serie.values[i])
metadata = serie.metadata.get(i) metadata = serie.metadata.get(i)
gauges = decorate( gauges = decorate(
@ -54,12 +58,18 @@ class Gauge(Graph):
self.svg.node(serie_node['plot'], class_="dots"), self.svg.node(serie_node['plot'], class_="dots"),
metadata) metadata)
alter(self.svg.node( tolerance = 1.15
gauges, 'polygon', points=' '.join([ theta = min(
fmt(self.view((0, 0))), max(self._min * tolerance, theta), self._max * tolerance)
fmt(self.view((.75, theta))), w = (self._box._tmax - self._box._tmin + self.view.aperture) / 4
fmt(self.view((.8, theta))), alter(
fmt(self.view((.75, theta)))]), self.svg.node(
gauges, 'path', d='M %s L %s A %s 1 0 1 %s Z' % (
point(.85, theta),
point(self.needle_width, theta - w),
'%f %f' % (self.needle_width, self.needle_width),
point(self.needle_width, theta + w),
),
class_='line reactive tooltip-trigger'), class_='line reactive tooltip-trigger'),
metadata) metadata)
@ -110,7 +120,8 @@ class Gauge(Graph):
self.max_) self.max_)
x_pos = compute_scale( x_pos = compute_scale(
self.min_, self.max_, self.logarithmic, self.order_min self.min_, self.max_, self.logarithmic, self.order_min
) ) if not self.x_labels else list(map(float, self.x_labels))
self._x_labels = list(zip(map(self._format, x_pos), x_pos)) self._x_labels = list(zip(map(self._format, x_pos), x_pos))
def _plot(self): def _plot(self):

4
pygal/graph/graph.py

@ -24,7 +24,7 @@ Commmon graphing functions
from __future__ import division from __future__ import division
from pygal._compat import is_list_like from pygal._compat import is_list_like
from pygal.interpolate import INTERPOLATIONS from pygal.interpolate import INTERPOLATIONS
from pygal.graph.base import BaseGraph from pygal.graph.public import PublicApi
from pygal.view import View, LogView, XYLogView, ReverseView from pygal.view import View, LogView, XYLogView, ReverseView
from pygal.util import ( from pygal.util import (
cached_property, majorize, humanize, split_title, cached_property, majorize, humanize, split_title,
@ -34,7 +34,7 @@ from math import sqrt, ceil, cos, sin
from itertools import repeat, chain from itertools import repeat, chain
class Graph(BaseGraph): class Graph(PublicApi):
"""Graph super class containing generic common functions""" """Graph super class containing generic common functions"""
_dual = False _dual = False

7
pygal/graph/histogram.py

@ -33,7 +33,6 @@ class Histogram(Graph):
_dual = True _dual = True
_series_margin = 0 _series_margin = 0
@cached_property @cached_property
def _secondary_values(self): def _secondary_values(self):
"""Getter for secondary series values (flattened)""" """Getter for secondary series values (flattened)"""
@ -128,9 +127,11 @@ class Histogram(Graph):
self._box.ymin, self._box.ymax = ymin, ymax self._box.ymin, self._box.ymax = ymin, ymax
x_pos = compute_scale( x_pos = compute_scale(
self._box.xmin, self._box.xmax, self.logarithmic, self.order_min) self._box.xmin, self._box.xmax, self.logarithmic, self.order_min
) if not self.x_labels else list(map(float, self.x_labels))
y_pos = compute_scale( y_pos = compute_scale(
self._box.ymin, self._box.ymax, self.logarithmic, self.order_min) self._box.ymin, self._box.ymax, self.logarithmic, self.order_min
) if not self.y_labels else list(map(float, self.y_labels))
self._x_labels = list(zip(map(self._format, x_pos), x_pos)) self._x_labels = list(zip(map(self._format, x_pos), x_pos))
self._y_labels = list(zip(map(self._format, y_pos), y_pos)) self._y_labels = list(zip(map(self._format, y_pos), y_pos))

145
pygal/graph/public.py

@ -0,0 +1,145 @@
# -*- coding: utf-8 -*-
# This file is part of pygal
#
# A python svg graph plotting library
# Copyright © 2012-2015 Kozea
#
# This library is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
# This library 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 Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with pygal. If not, see <http://www.gnu.org/licenses/>.
"""
pygal public api functions
"""
import io
from pygal._compat import u, is_list_like
from pygal.graph.base import BaseGraph
class PublicApi(BaseGraph):
"""Chart public functions"""
def add(self, title, values, **kwargs):
"""Add a serie to this graph"""
if not is_list_like(values) and not isinstance(values, dict):
values = [values]
if kwargs.get('secondary', False):
self.raw_series2.append((title, values, kwargs))
else:
self.raw_series.append((title, values, kwargs))
return self
def add_xml_filter(self, callback):
self.xml_filters.append(callback)
return self
def render(self, is_unicode=False, **kwargs):
"""Render the graph, and return the svg string"""
self.setup(**kwargs)
svg = self.svg.render(
is_unicode=is_unicode, pretty_print=self.pretty_print)
self.teardown()
return svg
def render_tree(self):
"""Render the graph, and return (l)xml etree"""
self.setup()
svg = self.svg.root
for f in self.xml_filters:
svg = f(svg)
self.teardown()
return svg
def render_table(self, **kwargs):
# Import here to avoid lxml import
try:
from pygal.table import Table
except ImportError:
raise ImportError('You must install lxml to use render table')
return Table(self).render(**kwargs)
def render_pyquery(self):
"""Render the graph, and return a pyquery wrapped tree"""
from pyquery import PyQuery as pq
return pq(self.render(), parser='html')
def render_in_browser(self, **kwargs):
"""Render the graph, open it in your browser with black magic"""
try:
from lxml.html import open_in_browser
except ImportError:
raise ImportError('You must install lxml to use render in browser')
open_in_browser(self.render_tree(**kwargs), encoding='utf-8')
def render_response(self, **kwargs):
"""Render the graph, and return a Flask response"""
from flask import Response
return Response(self.render(**kwargs), mimetype='image/svg+xml')
def render_django_response(self, **kwargs):
"""Render the graph, and return a Django response"""
from django.http import HttpResponse
return HttpResponse(
self.render(**kwargs), content_type='image/svg+xml')
def render_to_file(self, filename, **kwargs):
"""Render the graph, and write it to filename"""
with io.open(filename, 'w', encoding='utf-8') as f:
f.write(self.render(is_unicode=True, **kwargs))
def render_to_png(self, filename=None, dpi=72, **kwargs):
"""Render the graph, convert it to png and write it to filename"""
import cairosvg
return cairosvg.svg2png(
bytestring=self.render(**kwargs), write_to=filename, dpi=dpi)
def render_sparktext(self, relative_to=None):
"""Make a mini text sparkline from chart"""
bars = u('▁▂▃▄▅▆▇█')
if len(self.raw_series) == 0:
return u('')
values = list(self.raw_series[0][1])
if len(values) == 0:
return u('')
chart = u('')
values = list(map(lambda x: max(x, 0), values))
vmax = max(values)
if relative_to is None:
relative_to = min(values)
if (vmax - relative_to) == 0:
chart = bars[0] * len(values)
return chart
divisions = len(bars) - 1
for value in values:
chart += bars[int(divisions *
(value - relative_to) / (vmax - relative_to))]
return chart
def render_sparkline(self, **kwargs):
spark_options = dict(
width=200,
height=50,
show_dots=False,
show_legend=False,
show_x_labels=False,
show_y_labels=False,
spacing=0,
margin=5,
explicit_size=True
)
spark_options.update(kwargs)
return self.render(**spark_options)

12
pygal/style.py

@ -301,18 +301,6 @@ SolidColorStyle = Style(
'#0099C6', '#DD4477', '#74B217', '#B82E2E', '#316395', '#994499')) '#0099C6', '#DD4477', '#74B217', '#B82E2E', '#316395', '#994499'))
RTDStyle = Style(
background='#fcfcfc',
plot_background='#ffffff',
foreground='#707070',
foreground_light='#404040',
foreground_dark='#a0a0a0',
opacity='.8',
opacity_hover='.9',
transition='400ms ease-in',
colors=[colors.rotate('#2980b9', i * 50) for i in range(16)])
styles = {'default': DefaultStyle, styles = {'default': DefaultStyle,
'light': LightStyle, 'light': LightStyle,
'neon': NeonStyle, 'neon': NeonStyle,

13
pygal/view.py

@ -232,8 +232,9 @@ class PolarLogView(View):
class PolarThetaView(View): class PolarThetaView(View):
"""Logarithmic polar projection""" """Logarithmic polar projection"""
def __init__(self, width, height, box): def __init__(self, width, height, box, aperture=pi / 3):
super(PolarThetaView, self).__init__(width, height, box) super(PolarThetaView, self).__init__(width, height, box)
self.aperture = aperture
if not hasattr(box, '_tmin') or not hasattr(box, '_tmax'): if not hasattr(box, '_tmin') or not hasattr(box, '_tmax'):
raise Exception( raise Exception(
'Box must be set with set_polar_box for polar charts') 'Box must be set with set_polar_box for polar charts')
@ -243,14 +244,8 @@ class PolarThetaView(View):
if None in rhotheta: if None in rhotheta:
return None, None return None, None
rho, theta = rhotheta rho, theta = rhotheta
aperture = pi / 3 start = 3 * pi / 2 + self.aperture / 2
if theta > self.box._tmax: theta = start + (2 * pi - self.aperture) * (
theta = (3 * pi - aperture / 2) / 2
elif theta < self.box._tmin:
theta = (3 * pi + aperture / 2) / 2
else:
start = 3 * pi / 2 + aperture / 2
theta = start + (2 * pi - aperture) * (
theta - self.box._tmin) / ( theta - self.box._tmin) / (
self.box._tmax - self.box._tmin) self.box._tmax - self.box._tmin)
return super(PolarThetaView, self).__call__( return super(PolarThetaView, self).__call__(

Loading…
Cancel
Save