plotjs 0.0.2__tar.gz → 0.0.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. {plotjs-0.0.2 → plotjs-0.0.3}/.gitignore +4 -0
  2. plotjs-0.0.3/Makefile +20 -0
  3. {plotjs-0.0.2/plotjs.egg-info → plotjs-0.0.3}/PKG-INFO +13 -6
  4. plotjs-0.0.3/README.md +32 -0
  5. {plotjs-0.0.2 → plotjs-0.0.3}/coverage-badge.svg +1 -1
  6. plotjs-0.0.3/docs/developers/contributing.md +68 -0
  7. plotjs-0.0.3/docs/developers/how-it-works.md +158 -0
  8. plotjs-0.0.3/docs/gallery/index.md +85 -0
  9. plotjs-0.0.3/docs/gallery/index.qmd +31 -0
  10. plotjs-0.0.3/docs/guides/css/CSS.py +56 -0
  11. plotjs-0.0.3/docs/guides/css/index.md +121 -0
  12. {plotjs-0.0.2 → plotjs-0.0.3}/docs/guides/javascript/index.md +42 -14
  13. {plotjs-0.0.2 → plotjs-0.0.3}/docs/guides/javascript/javascript.py +9 -6
  14. plotjs-0.0.3/docs/guides/troubleshooting/index.md +28 -0
  15. plotjs-0.0.3/docs/iframes/CSS-2.html +1514 -0
  16. plotjs-0.0.3/docs/iframes/CSS.html +1510 -0
  17. plotjs-0.0.3/docs/iframes/javascript.html +1197 -0
  18. plotjs-0.0.3/docs/iframes/javascript2.html +1286 -0
  19. plotjs-0.0.2/docs/iframes/quickstart3.html → plotjs-0.0.3/docs/iframes/quickstart.html +625 -612
  20. {plotjs-0.0.2 → plotjs-0.0.3}/docs/iframes/quickstart2.html +625 -612
  21. plotjs-0.0.3/docs/iframes/quickstart3.html +1193 -0
  22. plotjs-0.0.3/docs/iframes/quickstart4.html +1193 -0
  23. plotjs-0.0.3/docs/iframes/quickstart5.html +1419 -0
  24. plotjs-0.0.3/docs/iframes/quickstart6.html +974 -0
  25. plotjs-0.0.3/docs/iframes/quickstart7.html +1257 -0
  26. plotjs-0.0.3/docs/iframes/quickstart8.html +1717 -0
  27. plotjs-0.0.3/docs/iframes/quickstart9.html +1892 -0
  28. {plotjs-0.0.2 → plotjs-0.0.3}/docs/index.md +70 -14
  29. {plotjs-0.0.2 → plotjs-0.0.3}/docs/index.qmd +63 -11
  30. plotjs-0.0.3/docs/stylesheets/style.css +52 -0
  31. {plotjs-0.0.2 → plotjs-0.0.3}/mkdocs.yaml +6 -2
  32. plotjs-0.0.3/overrides/partials/footer.html +7 -0
  33. {plotjs-0.0.2 → plotjs-0.0.3}/plotjs/__init__.py +1 -1
  34. {plotjs-0.0.2 → plotjs-0.0.3}/plotjs/main.py +44 -41
  35. plotjs-0.0.3/plotjs/static/template.html +176 -0
  36. {plotjs-0.0.2 → plotjs-0.0.3/plotjs.egg-info}/PKG-INFO +13 -6
  37. {plotjs-0.0.2 → plotjs-0.0.3}/plotjs.egg-info/SOURCES.txt +10 -8
  38. {plotjs-0.0.2 → plotjs-0.0.3}/pyproject.toml +4 -3
  39. {plotjs-0.0.2 → plotjs-0.0.3}/tests/test_magic_plot.py +0 -6
  40. {plotjs-0.0.2 → plotjs-0.0.3}/tests/test_main.py +18 -18
  41. plotjs-0.0.2/README.md +0 -25
  42. plotjs-0.0.2/docs/guides/css/CSS-2.html +0 -1376
  43. plotjs-0.0.2/docs/guides/css/CSS.html +0 -1372
  44. plotjs-0.0.2/docs/guides/css/CSS.py +0 -47
  45. plotjs-0.0.2/docs/guides/css/index.md +0 -80
  46. plotjs-0.0.2/docs/guides/javascript/javascript.html +0 -1185
  47. plotjs-0.0.2/docs/guides/javascript/javascript2.html +0 -1274
  48. plotjs-0.0.2/docs/guides/troubleshooting/index.md +0 -28
  49. plotjs-0.0.2/docs/how-it-works.md +0 -158
  50. plotjs-0.0.2/docs/iframes/quickstart.html +0 -1180
  51. plotjs-0.0.2/docs/iframes/quickstart4.html +0 -1180
  52. plotjs-0.0.2/docs/iframes/quickstart5.html +0 -1406
  53. plotjs-0.0.2/docs/iframes/quickstart6.html +0 -961
  54. plotjs-0.0.2/docs/iframes/quickstart7.html +0 -1244
  55. plotjs-0.0.2/docs/iframes/quickstart8.html +0 -1704
  56. plotjs-0.0.2/docs/index_files/figure-commonmark/cell-2-output-1.png +0 -0
  57. plotjs-0.0.2/docs/stylesheets/style.css +0 -106
  58. plotjs-0.0.2/overrides/partials/footer.html +0 -20
  59. plotjs-0.0.2/plotjs/static/template.html +0 -163
  60. plotjs-0.0.2/scripts/coverage.sh +0 -5
  61. plotjs-0.0.2/scripts/release.sh +0 -36
  62. {plotjs-0.0.2 → plotjs-0.0.3}/.coverage +0 -0
  63. {plotjs-0.0.2 → plotjs-0.0.3}/.gitattributes +0 -0
  64. {plotjs-0.0.2 → plotjs-0.0.3}/.github/workflows/doc.yaml +0 -0
  65. {plotjs-0.0.2 → plotjs-0.0.3}/.github/workflows/lint.yaml +0 -0
  66. {plotjs-0.0.2 → plotjs-0.0.3}/.github/workflows/pypi.yaml +0 -0
  67. {plotjs-0.0.2 → plotjs-0.0.3}/.github/workflows/tests.yaml +0 -0
  68. {plotjs-0.0.2 → plotjs-0.0.3}/.github/workflows/type.yaml +0 -0
  69. {plotjs-0.0.2 → plotjs-0.0.3}/.pre-commit-config.yaml +0 -0
  70. {plotjs-0.0.2 → plotjs-0.0.3}/LICENSE +0 -0
  71. {plotjs-0.0.2 → plotjs-0.0.3}/docs/guides/advanced/advanced.py +0 -0
  72. {plotjs-0.0.2 → plotjs-0.0.3}/docs/guides/advanced/index.md +0 -0
  73. {plotjs-0.0.2 → plotjs-0.0.3}/docs/img/how-it-works-1.png +0 -0
  74. {plotjs-0.0.2 → plotjs-0.0.3}/docs/img/how-it-works-2.png +0 -0
  75. {plotjs-0.0.2 → plotjs-0.0.3}/docs/index_files/figure-commonmark/cell-3-output-1.png +0 -0
  76. {plotjs-0.0.2 → plotjs-0.0.3}/docs/reference/css.md +0 -0
  77. {plotjs-0.0.2 → plotjs-0.0.3}/docs/reference/datasets.md +0 -0
  78. {plotjs-0.0.2 → plotjs-0.0.3}/docs/reference/javascript.md +0 -0
  79. {plotjs-0.0.2 → plotjs-0.0.3}/docs/reference/magic-plot.md +0 -0
  80. {plotjs-0.0.2 → plotjs-0.0.3}/docs/static/style.css +0 -0
  81. {plotjs-0.0.2 → plotjs-0.0.3}/plotjs/css.py +0 -0
  82. {plotjs-0.0.2 → plotjs-0.0.3}/plotjs/data/__init__.py +0 -0
  83. {plotjs-0.0.2 → plotjs-0.0.3}/plotjs/data/datasets.py +0 -0
  84. {plotjs-0.0.2 → plotjs-0.0.3}/plotjs/data/iris.csv +0 -0
  85. {plotjs-0.0.2 → plotjs-0.0.3}/plotjs/data/mtcars.csv +0 -0
  86. {plotjs-0.0.2 → plotjs-0.0.3}/plotjs/data/titanic.csv +0 -0
  87. {plotjs-0.0.2 → plotjs-0.0.3}/plotjs/javascript.py +0 -0
  88. {plotjs-0.0.2 → plotjs-0.0.3}/plotjs/static/d3.min.js +0 -0
  89. {plotjs-0.0.2 → plotjs-0.0.3}/plotjs/static/default.css +0 -0
  90. {plotjs-0.0.2 → plotjs-0.0.3}/plotjs/static/main.js +0 -0
  91. {plotjs-0.0.2 → plotjs-0.0.3}/plotjs/utils.py +0 -0
  92. {plotjs-0.0.2 → plotjs-0.0.3}/plotjs.egg-info/dependency_links.txt +0 -0
  93. {plotjs-0.0.2 → plotjs-0.0.3}/plotjs.egg-info/requires.txt +0 -0
  94. {plotjs-0.0.2 → plotjs-0.0.3}/plotjs.egg-info/top_level.txt +0 -0
  95. {plotjs-0.0.2 → plotjs-0.0.3}/setup.cfg +0 -0
  96. {plotjs-0.0.2 → plotjs-0.0.3}/tests/__init__.py +0 -0
  97. {plotjs-0.0.2 → plotjs-0.0.3}/tests/static/script.js +0 -0
  98. {plotjs-0.0.2 → plotjs-0.0.3}/tests/static/style.css +0 -0
  99. {plotjs-0.0.2 → plotjs-0.0.3}/tests/test_css_and_js_utils.py +0 -0
  100. {plotjs-0.0.2 → plotjs-0.0.3}/tests/test_data.py +0 -0
@@ -28,3 +28,7 @@ sandbox/
28
28
 
29
29
  *.svg
30
30
  !coverage-badge.svg
31
+
32
+ scripts/release.sh
33
+
34
+ /.quarto/
plotjs-0.0.3/Makefile ADDED
@@ -0,0 +1,20 @@
1
+ .PHONY: all examples coverage
2
+
3
+ examples:
4
+ quarto render docs/index.qmd
5
+ uv run docs/guides/advanced/advanced.py
6
+ uv run docs/guides/css/css.py
7
+ uv run docs/guides/javascript/javascript.py
8
+
9
+ gallery:
10
+ quarto render docs/gallery/index.qmd
11
+
12
+ coverage:
13
+ uv run coverage run --source=plotjs -m pytest
14
+ uv run coverage report -m
15
+ uv run coverage xml
16
+ uv run genbadge coverage -i coverage.xml
17
+ rm coverage.xml
18
+
19
+ test:
20
+ uv run pytest
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plotjs
3
- Version: 0.0.2
4
- Summary: Bridge between static matplotlib and interactive storytelling
3
+ Version: 0.0.3
4
+ Summary: Turn static matplotlib charts into interactive web visualizations
5
5
  Author-email: Joseph Barbier <joseph.barbierdarnal@mail.com>
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://y-sunflower.github.io/plotjs/
@@ -11,7 +11,7 @@ Project-URL: Repository, https://github.com/y-sunflower/plotjs
11
11
  Keywords: matplotlib,interactive,javascript,web,css,d3,mpld3,plotnine
12
12
  Classifier: Programming Language :: Python :: 3
13
13
  Classifier: Operating System :: OS Independent
14
- Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Development Status :: 4 - Beta
15
15
  Requires-Python: >=3.10
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE
@@ -20,11 +20,18 @@ Requires-Dist: matplotlib>=3.10.0
20
20
  Requires-Dist: narwhals>=2.0.0
21
21
  Dynamic: license-file
22
22
 
23
- # `plotjs`: bridge between static matplotlib and interactive storytelling
23
+ ![](./coverage-badge.svg)
24
24
 
25
- `plotjs` is a proof of concept, inspired by [mpld3](https://github.com/mpld3/mpld3), to make matplotlib plots interactive (for the browser) with minimum user inputs.
25
+ # `plotjs`: Turn static matplotlib charts into interactive web visualizations
26
26
 
27
- The goal is also to give users a large customization power.
27
+ <img src="https://github.com/JosephBARBIERDARNAL/static/blob/main/python-libs/plotjs/image.png?raw=true" alt="plotjs logo" align="right" width="150px"/>
28
+
29
+ `plotjs` is a Python package that transform matplotlib plots into interactive charts with minimum user inputs. You can:
30
+
31
+ - control tooltip labels and grouping
32
+ - add CSS
33
+ - add JavaScript
34
+ - and many more
28
35
 
29
36
  > Consider that the project is still **very unstable**.
30
37
 
plotjs-0.0.3/README.md ADDED
@@ -0,0 +1,32 @@
1
+ ![](./coverage-badge.svg)
2
+
3
+ # `plotjs`: Turn static matplotlib charts into interactive web visualizations
4
+
5
+ <img src="https://github.com/JosephBARBIERDARNAL/static/blob/main/python-libs/plotjs/image.png?raw=true" alt="plotjs logo" align="right" width="150px"/>
6
+
7
+ `plotjs` is a Python package that transform matplotlib plots into interactive charts with minimum user inputs. You can:
8
+
9
+ - control tooltip labels and grouping
10
+ - add CSS
11
+ - add JavaScript
12
+ - and many more
13
+
14
+ > Consider that the project is still **very unstable**.
15
+
16
+ [Online demo](https://y-sunflower.github.io/plotjs/)
17
+
18
+ <br><br>
19
+
20
+ ## Installation
21
+
22
+ From PyPI:
23
+
24
+ ```
25
+ pip install plotjs
26
+ ```
27
+
28
+ Latest dev version:
29
+
30
+ ```
31
+ pip install git+https://github.com/y-sunflower/plotjs.git
32
+ ```
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="114" height="20" role="img" aria-label="coverage: 92.48%"><title>coverage: 92.48%</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="114" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="61" height="20" fill="#555"/><rect x="61" width="53" height="20" fill="#4c1"/><rect width="114" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="315" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="510">coverage</text><text x="315" y="140" transform="scale(.1)" fill="#fff" textLength="510">coverage</text><text aria-hidden="true" x="865" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">92.48%</text><text x="865" y="140" transform="scale(.1)" fill="#fff" textLength="430">92.48%</text></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="114" height="20" role="img" aria-label="coverage: 92.19%"><title>coverage: 92.19%</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="114" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="61" height="20" fill="#555"/><rect x="61" width="53" height="20" fill="#4c1"/><rect width="114" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="315" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="510">coverage</text><text x="315" y="140" transform="scale(.1)" fill="#fff" textLength="510">coverage</text><text aria-hidden="true" x="865" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">92.19%</text><text x="865" y="140" transform="scale(.1)" fill="#fff" textLength="430">92.19%</text></g></svg>
@@ -0,0 +1,68 @@
1
+ `plotjs` is still in a very early stage, but contributions is still welcomed!
2
+
3
+ The best way to help the development is to:
4
+
5
+ - list the bugs you found by opening [GitHub issues](https://github.com/y-sunflower/plotjs/issues){target="_blank"}
6
+ - list the features you'd like to see by opening [GitHub issues](https://github.com/y-sunflower/plotjs/issues){target="_blank"}
7
+
8
+ ## Setting up your environment
9
+
10
+ ### Install for development
11
+
12
+ - Fork the repository to your own GitHub account.
13
+
14
+ - Clone your forked repository to your local machine (ensure you have [Git installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)):
15
+
16
+ ```bash
17
+ git clone https://github.com/github_user_name/plotjs.git
18
+ cd plotjs
19
+ git remote add upstream https://github.com/y-sunflower/plotjs.git
20
+ ```
21
+
22
+ - Create a new branch:
23
+
24
+ ```bash
25
+ git checkout -b my-feature
26
+ ```
27
+
28
+ - Set up your Python environment (ensure you have [uv installed](https://docs.astral.sh/uv/getting-started/installation/)):
29
+
30
+ ```bash
31
+ uv sync --all-groups --dev
32
+ uv run pre-commit install
33
+ uv pip install -e .
34
+ ```
35
+
36
+ ## Code
37
+
38
+ You can now make changes to the package and start coding!
39
+
40
+ ### Run the test
41
+
42
+ - Test that everything works correctly by running:
43
+
44
+ ```bash
45
+ uv run pytest
46
+ ```
47
+
48
+ ### Preview documentation locally
49
+
50
+ ```bash
51
+ uv run mkdocs serve
52
+ ```
53
+
54
+ ## Push changes
55
+
56
+ - Commit and push your changes:
57
+
58
+ ```bash
59
+ git add -A
60
+ git commit -m "description of what you did"
61
+ git push
62
+ ```
63
+
64
+ - Navigate to your fork on GitHub and click the "Compare & pull request" button to open a new pull request.
65
+
66
+ Congrats! Once your PR is merged, it will be part of `plotjs`.
67
+
68
+ <br>
@@ -0,0 +1,158 @@
1
+ Transforming a static plot into something interactive can't be done (unfortunately) just by saying "make this interactive."
2
+
3
+ But that doesn't mean we have to do mystic things to make it work, because **yes**, that's perfectly possible without weird hacking stuff.
4
+
5
+ ## Overview
6
+
7
+ There are two ways to tackle this problem:
8
+
9
+ - Take a matplotlib Figure (an instance containing all plot elements) and convert it to a more common format such as JSON. We call this **serialization**. Then, with this JSON file, we recreate the figure with an interactive tool such as D3.js (that's what [mpld3](https://github.com/mpld3/mpld3) does, by the way!).
10
+ - Use the native matplotlib figure output format (especially SVG) and parse this instead (that's what `plotjs` does).
11
+
12
+ The second option is **much simpler** (well, it depends), because we don't have to:
13
+
14
+ - translate the figure to JSON (which can be painfully complex if you want to handle all edge cases and make it robust),
15
+ - recreate the chart (browsers can display SVG perfectly).
16
+
17
+ But it means we don't have full control over how the plot is structured (from the browser's point of view). We need to find a way to parse this SVG.
18
+
19
+ ## Parsing SVG
20
+
21
+ For the moment, we just take the user's matplotlib figure and save it as SVG. This is just:
22
+
23
+ ```python
24
+ fig.savefig("plot.svg")
25
+ ```
26
+
27
+ Now, let's say the figure contains a scatter plot and we want to add a tooltip: when someone hovers their mouse over a point, it displays a label.
28
+
29
+ The **core problem to solve** is: "how do I know what elements from the SVG are points?"
30
+
31
+ If we're able to find a solution to this, then we're able to do pretty much **anything we want**.
32
+
33
+ The thing is, there's nothing in the SVG output file that tells us "this element is a point from the scatter plot." **Even worse**, we don't even know if it's a scatter plot or something completely unrelated, like a choropleth map.
34
+
35
+ For example, here is a polygon of a choropleth map:
36
+
37
+ ```svg
38
+ <path d="M -5.94098 449.279178
39
+ L -4.961244 447.127034
40
+ L -4.623284 444.786333
41
+ L -3.951831 442.584471
42
+ L -5.459995 439.551953
43
+ L -5.763812 436.050818
44
+ L -3.764043 431.650797
45
+ L -2.459548 432.213824
46
+ L 0.368893 433.425033
47
+ L 4.436122 437.74466
48
+ L 5.072043 439.840475
49
+ L 2.799722 444.523728
50
+ L 1.620088 448.292228
51
+ L 0.147953 450.236113
52
+ L -1.691322 450.601856
53
+ L -2.215184 449.161231
54
+ L -3.074426 448.947286
55
+ L -4.263489 450.334887
56
+ z
57
+ " clip-path="url(#pf43ab1627f)" style="fill: #424186"/>
58
+ ```
59
+
60
+ Here is a point from a scatter plot:
61
+
62
+ ```svg
63
+ <use
64
+ xlink:href="#m81e2893e84"
65
+ x="145.978182"
66
+ y="144.288"
67
+ style="fill: #1f77b4;
68
+ stroke: #1f77b4"
69
+ />
70
+ ```
71
+
72
+ Here is a line from a line chart:
73
+
74
+ ```svg
75
+ <g id="line2d_19">
76
+ <path d="M 73.832727 295.488
77
+ L 154.996364 235.008
78
+ L 236.16 174.528
79
+ L 317.323636 114.048
80
+ L 398.487273 53.568
81
+ "
82
+ clip-path="url(#pd511a61f39)"
83
+ style="fill: none;
84
+ stroke: #1f77b4;
85
+ stroke-width: 1.5;
86
+ stroke-linecap: square" />
87
+ </g>
88
+ ```
89
+
90
+ If you pay close attention, you'll see potential patterns in the structure of certain elements.
91
+
92
+ That's exactly what we'll use to determine what kind of plot elements we have.
93
+
94
+ > Note: determining the kind of plot elements could have been done (partially) from the Python side, but this felt easier to me to do from the JavaScript side.
95
+
96
+ The next step is to understand matplotlib's underlying objects (called [artists](https://matplotlib.org/stable/users/explain/artists/artist_intro.html){target="\_blank"}) and how that translates to SVG.
97
+
98
+ ## TL;DR: Artists in matplotlib
99
+
100
+ In matplotlib, artists are all the visual elements you see on a plot. There is the `Artist` base class, and all other artists inherit from this class.
101
+
102
+ For example:
103
+
104
+ - the `scatter()` function returns a `PathCollection` object, a subclass of `Artist`,
105
+ - the `plot()` function returns a `Line2D` object, a subclass of `Artist`,
106
+ - and so on.
107
+
108
+ ## Selecting artists from SVG
109
+
110
+ In the SVG output of `savefig("plot.svg")`, we can find some info about what object was used.
111
+
112
+ For example, all `PathCollection` objects look like `<g id="PathCollection_1">`, `<g id="PathCollection_2">`. And since `PathCollection` is just one or multiple points, we can easily know how many scatter plots there are.
113
+
114
+ For lines, they are represented by `Line2D`. In the SVG, they look like `<g id="line2d_1">`, `<g id="line2d_2">`, etc. With this, we can easily detect that there are lines in the chart.
115
+
116
+ But there's a major issue here: not all `PathCollection` elements are relevant, same for `Line2D`, and so on.
117
+
118
+ By "relevant," I mean those we want to add interactivity to. For example, what elements here are considered to be a `Line2D`?
119
+
120
+ ![](../img/how-it-works-1.png)
121
+
122
+ At first, I thought there were three: one for each main line. But in practice, it's much more:
123
+
124
+ ![](../img/how-it-works-2.png)
125
+
126
+ What that means is that we can't just select all `Line2D` elements and give them a hover effect, for instance. We need to find a way to discriminate relevant lines (the three big ones) from the other ones.
127
+
128
+ ## Filtering artists from SVG
129
+
130
+ This section might not be up to date with the latest version, but it'll give you an idea of how `plotjs` detects what is a "core" plot element and what is not.
131
+
132
+ It mostly consists of handling edge cases here, and is very different depending on the plot element (`Line2D`, `PathCollection`, etc.).
133
+
134
+ For example, in order to select only "core" `Line2D` elements (the three colored ones in the previous image), we do:
135
+
136
+ ```javascript
137
+ const lines = svg.selectAll('g[id^="line2d"] path').filter(function () {
138
+ const clip = d3.select(this).attr("clip-path");
139
+
140
+ return (
141
+ // keep only <path> with clip-path attribute
142
+ clip &&
143
+ // that starts with "url("
144
+ clip.startsWith("url(") &&
145
+ // and are not child of a #matplotlib.axis
146
+ !this.closest('g[id^="matplotlib.axis"]')
147
+ );
148
+ });
149
+ ```
150
+
151
+ The idea is basically:
152
+
153
+ - select all `Line2D` elements with the first line,
154
+ - filter to remove non-wanted `Line2D` elements.
155
+
156
+ This gives us a `lines` variable that only contains the lines of interest!
157
+
158
+ The logic is the same for other plot elements: bars, points, polygons, etc.
@@ -0,0 +1,85 @@
1
+
2
+
3
+ This page contains all the `plotjs` examples from this website.
4
+
5
+ **TODO: make this page less ugly and add more diverse examples.**
6
+
7
+ <div style="display: flex;">
8
+
9
+ <iframe width="100%" height="300" src="../iframes/quickstart8.html" style="flex: 1; border: none;">
10
+
11
+ </iframe>
12
+
13
+ <iframe width="100%" height="300" src="../iframes/quickstart.html" style="flex: 1; border: none;">
14
+
15
+ </iframe>
16
+
17
+ </div>
18
+
19
+ <div style="display: flex;">
20
+
21
+ <iframe width="100%" height="300" src="../iframes/quickstart4.html" style="flex: 1; border: none;">
22
+
23
+ </iframe>
24
+
25
+ <iframe width="100%" height="300" src="../iframes/quickstart5.html" style="flex: 1; border: none;">
26
+
27
+ </iframe>
28
+
29
+ </div>
30
+
31
+ <div style="display: flex;">
32
+
33
+ <iframe width="100%" height="300" src="../iframes/quickstart9.html" style="flex: 1; border: none;">
34
+
35
+ </iframe>
36
+
37
+ <iframe width="100%" height="300" src="../iframes/CSS.html" style="flex: 1; border: none;">
38
+
39
+ </iframe>
40
+
41
+ </div>
42
+
43
+ <div style="display: flex;">
44
+
45
+ <iframe width="100%" height="300" src="../iframes/quickstart2.html" style="flex: 1; border: none;">
46
+
47
+ </iframe>
48
+
49
+ <iframe width="100%" height="300" src="../iframes/javascript.html" style="flex: 1; border: none;">
50
+
51
+ </iframe>
52
+
53
+ </div>
54
+
55
+ <div style="display: flex;">
56
+
57
+ <iframe width="100%" height="300" src="../iframes/quickstart3.html" style="flex: 1; border: none;">
58
+
59
+ </iframe>
60
+
61
+ <iframe width="100%" height="300" src="../iframes/javascript2.html" style="flex: 1; border: none;">
62
+
63
+ </iframe>
64
+
65
+ </div>
66
+
67
+ <div style="display: flex;">
68
+
69
+ <iframe width="100%" height="300" src="../iframes/CSS-2.html" style="flex: 1; border: none;">
70
+
71
+ </iframe>
72
+
73
+ <iframe width="100%" height="300" src="../iframes/quickstart6.html" style="flex: 1; border: none;">
74
+
75
+ </iframe>
76
+
77
+ </div>
78
+
79
+ <div style="display: flex;">
80
+
81
+ <iframe width="100%" height="300" src="../iframes/quickstart7.html" style="flex: 1; border: none;">
82
+
83
+ </iframe>
84
+
85
+ </div>
@@ -0,0 +1,31 @@
1
+ ---
2
+ format: gfm
3
+ from: markdown-smart
4
+ ---
5
+
6
+ This page contains all the `plotjs` examples from this website.
7
+
8
+ **TODO: make this page less ugly and add more diverse examples.**
9
+
10
+ ```{python}
11
+ # | echo: false
12
+ # | output: asis
13
+ from pathlib import Path
14
+
15
+ html_files = list(Path("../iframes").rglob("*.html"))
16
+ html_paths = [str(path) for path in html_files]
17
+
18
+
19
+ # Split the list into chunks of 2
20
+ def chunks(lst, n):
21
+ for i in range(0, len(lst), n):
22
+ yield lst[i : i + n]
23
+
24
+
25
+ for pair in chunks(html_paths, 2):
26
+ print('<div style="display: flex;">')
27
+ for html_path in pair:
28
+ iframe = f'<iframe width="100%" height="300" src="{html_path}" style="flex: 1; border: none;"></iframe>'
29
+ print(iframe)
30
+ print("</div>")
31
+ ```
@@ -0,0 +1,56 @@
1
+ import matplotlib.pyplot as plt
2
+ from plotjs import MagicPlot, data, css
3
+
4
+ df = data.load_iris()
5
+
6
+ fig, ax = plt.subplots()
7
+ ax.scatter(
8
+ df["sepal_length"],
9
+ df["sepal_width"],
10
+ c=df["species"].astype("category").cat.codes,
11
+ s=300,
12
+ alpha=0.5,
13
+ ec="black",
14
+ )
15
+ ax.set_xlabel("Sepal Length")
16
+ ax.set_ylabel("Sepal Width")
17
+
18
+ df["tooltip"] = (
19
+ "Sepal length = "
20
+ + df["sepal_length"].astype(str)
21
+ + "<br>"
22
+ + "Sepal width = "
23
+ + df["sepal_width"].astype(str)
24
+ + "<br>"
25
+ + df["species"].str.upper()
26
+ )
27
+
28
+ (
29
+ MagicPlot()
30
+ .add_tooltip(labels=df["tooltip"])
31
+ .add_css(".tooltip {background: red; color: blue;}")
32
+ .save("docs/iframes/CSS.html")
33
+ )
34
+
35
+
36
+ (
37
+ MagicPlot()
38
+ .add_tooltip(labels=df["tooltip"])
39
+ .add_css(css.from_dict({".tooltip": {"background": "red", "color": "blue"}}))
40
+ )
41
+
42
+ (
43
+ MagicPlot()
44
+ .add_tooltip(
45
+ labels=df["tooltip"],
46
+ )
47
+ .add_css(css.from_dict({".tooltip": {"color": "blue"}}))
48
+ .add_css(css.from_dict({".tooltip": {"background": "red"}}))
49
+ )
50
+
51
+ (
52
+ MagicPlot()
53
+ .add_tooltip(labels=df["tooltip"])
54
+ .add_css(css.from_file("docs/static/style.css"))
55
+ .save("docs/iframes/CSS-2.html")
56
+ )
@@ -0,0 +1,121 @@
1
+ With `plotjs`, you can add your own CSS for advanced plot customization. Here's how it works.
2
+
3
+ ## What is CSS?
4
+
5
+ CSS has two main components:
6
+
7
+ - **Selectors**: which elements the style applies to
8
+ - **Rules**: a set of `key: value` pairs defining the style
9
+
10
+ A basic CSS rule looks like this:
11
+
12
+ ```css
13
+ .tooltip {
14
+ background: red;
15
+ color: blue;
16
+ }
17
+ ```
18
+
19
+ This means: _"For all elements with class `tooltip`, set the background to red and the text color to blue."_
20
+
21
+ ## Applying CSS to a plot
22
+
23
+ You can directly apply a CSS string to your plot:
24
+
25
+ ```python
26
+ (
27
+ MagicPlot()
28
+ .add_tooltip(labels=df["tooltip"])
29
+ .add_css(".tooltip { background: red; color: blue; }")
30
+ )
31
+ ```
32
+
33
+ <iframe width="800" height="600" src="../../iframes/css.html" style="border:none;"></iframe>
34
+
35
+ > CSS doesn’t require indentation: one-liners work fine.
36
+
37
+ ## Using a Python dictionary
38
+
39
+ For better readability and reusability, you can define CSS as a dictionary using `plotjs.css.from_dict()`:
40
+
41
+ ```python
42
+ from plotjs import css
43
+
44
+ (
45
+ MagicPlot()
46
+ .add_tooltip(labels=df["tooltip"])
47
+ .add_css(css.from_dict({
48
+ ".tooltip": {
49
+ "background": "red",
50
+ "color": "blue"
51
+ }
52
+ }))
53
+ )
54
+ ```
55
+
56
+ Method chaining also works if you want to split styles:
57
+
58
+ ```python
59
+ (
60
+ MagicPlot()
61
+ .add_tooltip(labels=df["tooltip"])
62
+ .add_css(css.from_dict({".tooltip": {"color": "blue"}}))
63
+ .add_css(css.from_dict({".tooltip": {"background": "red"}}))
64
+ )
65
+ ```
66
+
67
+ ## Loading CSS from a file
68
+
69
+ If your CSS is stored in a `.css` file like:
70
+
71
+ ```css
72
+ .tooltip {
73
+ background: pink;
74
+ color: yellow;
75
+ }
76
+ ```
77
+
78
+ You can load it with:
79
+
80
+ ```python
81
+ from plotjs import css
82
+
83
+ (
84
+ MagicPlot()
85
+ .add_tooltip(labels=df["tooltip"])
86
+ .add_css(css.from_file("docs/static/style.css"))
87
+ )
88
+ ```
89
+
90
+ <iframe width="800" height="600" src="../../iframes/css-2.html" style="border:none;"></iframe>
91
+
92
+ ## Selectable elements
93
+
94
+ To style or add interactivity, you need to select elements using the DOM[^1]. These are the most common selectors:
95
+
96
+ ### Plot elements
97
+
98
+ - `.point`: scatter plot points
99
+ - `.line`: line chart lines
100
+ - `.area`: area chart fills
101
+ - `.bar`: bar chart bars
102
+ - `.plot-element`: all of the above
103
+
104
+ You can combine with `.hovered` or `.not-hovered`, e.g., `.point.hovered`.
105
+
106
+ ### Misc
107
+
108
+ - `.tooltip`: tooltip shown on hover
109
+ - `svg`: the entire SVG element
110
+
111
+ ???+ question
112
+
113
+ Something missing? Please [open an issue](https://github.com/y-sunflower/plotjs/issues)!
114
+
115
+ ## Default CSS
116
+
117
+ You can find the default CSS applied by plotjs [here](https://github.com/y-sunflower/plotjs/blob/main/plotjs/static/default.css)
118
+
119
+ ## Appendix
120
+
121
+ [^1]: The DOM (Document Object Model) is like a tree structure representing your webpage. JavaScript and CSS use it to select, modify, and interact with elements dynamically.