echartsy 0.1.0__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.
echartsy-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jigar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,636 @@
1
+ Metadata-Version: 2.2
2
+ Name: echartsy
3
+ Version: 0.1.0
4
+ Summary: A matplotlib-style fluent builder API for Apache ECharts in Python, Jupyter Notebooks, and Streamlit.
5
+ Author-email: Jigar <astrojigs24@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/astrojigs/echartsy
8
+ Project-URL: Documentation, https://github.com/astrojigs/echartsy#readme
9
+ Project-URL: Repository, https://github.com/astrojigs/echartsy
10
+ Keywords: echarts,visualization,charts,jupyter,plotting
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Scientific/Engineering :: Visualization
21
+ Classifier: Framework :: Jupyter
22
+ Requires-Python: >=3.9
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: pandas>=1.5.0
26
+ Requires-Dist: numpy>=1.23.0
27
+ Provides-Extra: jupyter
28
+ Requires-Dist: ipython>=7.0; extra == "jupyter"
29
+ Provides-Extra: streamlit
30
+ Requires-Dist: streamlit-echarts>=0.4.0; extra == "streamlit"
31
+ Provides-Extra: scipy
32
+ Requires-Dist: scipy>=1.9.0; extra == "scipy"
33
+ Provides-Extra: all
34
+ Requires-Dist: ipython>=7.0; extra == "all"
35
+ Requires-Dist: streamlit-echarts>=0.4.0; extra == "all"
36
+ Requires-Dist: scipy>=1.9.0; extra == "all"
37
+ Provides-Extra: dev
38
+ Requires-Dist: pytest>=7.0; extra == "dev"
39
+ Requires-Dist: ipython>=7.0; extra == "dev"
40
+
41
+ <p align="center">
42
+ <img src="https://echarts.apache.org/en/images/logo.png" alt="Apache ECharts" width="70" />
43
+ </p>
44
+
45
+ <h1 align="center">echartsy</h1>
46
+
47
+ <p align="center">
48
+ <em>Interactive charts in Python — the matplotlib workflow, the ECharts experience.</em>
49
+ </p>
50
+
51
+ <p align="center">
52
+ <a href="https://pypi.org/project/echartsy/"><img src="https://img.shields.io/pypi/v/echartsy?color=%2334d058&label=PyPI" alt="PyPI version" /></a>
53
+ <a href="https://pypi.org/project/echartsy/"><img src="https://img.shields.io/pypi/pyversions/echartsy?color=%2334d058" alt="Python versions" /></a>
54
+ <a href="https://github.com/astrojigs/echartsy/blob/main/LICENSE"><img src="https://img.shields.io/github/license/astrojigs/echartsy?color=blue" alt="License" /></a>
55
+ <a href="https://github.com/astrojigs/echartsy/stargazers"><img src="https://img.shields.io/github/stars/astrojigs/echartsy?style=flat&color=yellow" alt="Stars" /></a>
56
+ <a href="https://github.com/astrojigs/echartsy/issues"><img src="https://img.shields.io/github/issues/astrojigs/echartsy?color=orange" alt="Issues" /></a>
57
+ </p>
58
+
59
+ <p align="center">
60
+ Build publication-quality interactive charts with a familiar<br/>
61
+ <code>fig = figure()</code> &rarr; <code>fig.bar()</code> &rarr; <code>fig.show()</code> workflow.<br/>
62
+ Works everywhere: <b>Jupyter</b> &middot; <b>Streamlit</b> &middot; <b>standalone scripts</b>
63
+ </p>
64
+
65
+ ---
66
+
67
+ ## Why echartsy?
68
+
69
+ - **Feels like matplotlib** — If you know `plt.figure()` / `plt.show()`, you already know 90% of the API. No JSON wrangling, no JavaScript.
70
+ - **Interactive out of the box** — Every chart ships with tooltips, legend toggling, zoom, and a built-in export toolbox. Zero configuration needed.
71
+ - **One library, three engines** — Write once, render in Jupyter notebooks, Streamlit apps, or plain Python scripts that pop open a browser.
72
+ - **Composable & animated** — Layer pies on bar charts, build dual-axis dashboards, or animate any chart across time with `TimelineFigure`.
73
+
74
+ ---
75
+
76
+ ## Installation
77
+
78
+ ```bash
79
+ pip install echartsy
80
+ ```
81
+
82
+ Need extras?
83
+
84
+ ```bash
85
+ pip install echartsy[jupyter] # Jupyter Notebook / JupyterLab
86
+ pip install echartsy[streamlit] # Streamlit apps
87
+ pip install echartsy[scipy] # KDE density plots
88
+ pip install echartsy[all] # Everything
89
+ ```
90
+
91
+ > **Requirements:** Python 3.9+ &middot; pandas &ge; 1.5 &middot; numpy &ge; 1.23
92
+
93
+ ---
94
+
95
+ ## Quick Start
96
+
97
+ ```python
98
+ import pandas as pd
99
+ import echartsy as ec
100
+
101
+ ec.config(engine="jupyter") # or "python" / "streamlit"
102
+
103
+ df = pd.DataFrame({
104
+ "Fruit": ["Apples", "Bananas", "Cherries", "Dates", "Elderberries"],
105
+ "Sales": [120, 95, 78, 42, 63],
106
+ })
107
+
108
+ fig = ec.figure()
109
+ fig.bar(df, x="Fruit", y="Sales", gradient=True, labels=True)
110
+ fig.title("Fruit Sales")
111
+ fig.show()
112
+ ```
113
+
114
+ Three lines from DataFrame to interactive chart.
115
+
116
+ <p align="center">
117
+ <img src="assets/demo_bar.png" alt="Quick Start — Bar Chart" width="600" />
118
+ </p>
119
+
120
+ ---
121
+
122
+ ## Gallery
123
+
124
+ ### Cartesian Charts
125
+
126
+ <table>
127
+ <tr>
128
+ <td width="50%">
129
+ <p align="center"><strong>Bar + Pie Overlay</strong></p>
130
+ <p align="center"><img src="assets/demo_bar_pie.png" alt="Bar + Pie Overlay" width="100%" /></p>
131
+
132
+ ```python
133
+ fig = ec.figure(height="500px")
134
+ fig.bar(df, x="Dept", y="Budget",
135
+ gradient=True, labels=True)
136
+ fig.pie(df, names="Dept", values="Budget",
137
+ center=["82%","25%"],
138
+ radius=["18%","28%"])
139
+ fig.show()
140
+ ```
141
+ </td>
142
+ <td width="50%">
143
+ <p align="center"><strong>Smooth Line</strong></p>
144
+ <p align="center"><img src="assets/demo_line.png" alt="Line Chart" width="100%" /></p>
145
+
146
+ ```python
147
+ fig = ec.figure()
148
+ fig.plot(df, x="Month", y="Sales",
149
+ smooth=True, area=True)
150
+ fig.show()
151
+ ```
152
+ </td>
153
+ </tr>
154
+ <tr>
155
+ <td width="50%">
156
+ <p align="center"><strong>Scatter Plot</strong></p>
157
+ <p align="center"><img src="assets/demo_scatter.png" alt="Scatter Plot" width="100%" /></p>
158
+
159
+ ```python
160
+ fig = ec.figure()
161
+ fig.scatter(df, x="Height", y="Weight",
162
+ color="Gender", size="Age")
163
+ fig.show()
164
+ ```
165
+ </td>
166
+ <td width="50%">
167
+ <p align="center"><strong>Grouped Bar</strong></p>
168
+ <p align="center"><img src="assets/demo_grouped.png" alt="Grouped Bar" width="100%" /></p>
169
+
170
+ ```python
171
+ fig = ec.figure()
172
+ fig.bar(df, x="Quarter", y="Revenue",
173
+ hue="Region")
174
+ fig.show()
175
+ ```
176
+ </td>
177
+ </tr>
178
+ <tr>
179
+ <td width="50%">
180
+ <p align="center"><strong>Stacked Bar</strong></p>
181
+ <p align="center"><img src="assets/demo_stacked.png" alt="Stacked Bar" width="100%" /></p>
182
+
183
+ ```python
184
+ fig = ec.figure()
185
+ fig.bar(df, x="Month", y="Revenue",
186
+ hue="Product", stack=True)
187
+ fig.show()
188
+ ```
189
+ </td>
190
+ <td width="50%">
191
+ <p align="center"><strong>Histogram</strong></p>
192
+ <p align="center"><img src="assets/demo_histogram.png" alt="Histogram" width="100%" /></p>
193
+
194
+ ```python
195
+ fig = ec.figure()
196
+ fig.hist(df, column="Score", bins=20)
197
+ fig.show()
198
+ ```
199
+ </td>
200
+ </tr>
201
+ <tr>
202
+ <td width="50%">
203
+ <p align="center"><strong>Area Chart</strong></p>
204
+ <p align="center"><img src="assets/demo_area.png" alt="Area Chart" width="100%" /></p>
205
+
206
+ ```python
207
+ fig = ec.figure()
208
+ fig.plot(df, x="Month", y="Users",
209
+ area=True, smooth=True)
210
+ fig.show()
211
+ ```
212
+ </td>
213
+ <td width="50%">
214
+ <p align="center"><strong>Horizontal Bar</strong></p>
215
+ <p align="center"><img src="assets/demo_horizontal.png" alt="Horizontal Bar" width="100%" /></p>
216
+
217
+ ```python
218
+ fig = ec.figure()
219
+ fig.bar(df, x="Country", y="Population",
220
+ orient="h")
221
+ fig.show()
222
+ ```
223
+ </td>
224
+ </tr>
225
+ <tr>
226
+ <td width="50%">
227
+ <p align="center"><strong>Dual Axis: Bar + Line</strong></p>
228
+ <p align="center"><img src="assets/demo_dual.png" alt="Dual Axis" width="100%" /></p>
229
+
230
+ ```python
231
+ fig = ec.figure()
232
+ fig.bar(df, x="Month", y="Revenue")
233
+ fig.plot(df, x="Month", y="Growth",
234
+ smooth=True, axis=1)
235
+ fig.ylabel("Revenue ($K)")
236
+ fig.ylabel_right("Growth %")
237
+ fig.show()
238
+ ```
239
+ </td>
240
+ <td width="50%">
241
+ <p align="center"><strong>Gradient Bars</strong></p>
242
+ <p align="center"><img src="assets/demo_gradient.png" alt="Gradient Bars" width="100%" /></p>
243
+
244
+ ```python
245
+ fig = ec.figure()
246
+ fig.bar(df, x="City", y="Temp",
247
+ gradient=True,
248
+ gradient_colors=["#83bff6","#188df0"])
249
+ fig.show()
250
+ ```
251
+ </td>
252
+ </tr>
253
+ <tr>
254
+ <td width="50%">
255
+ <p align="center"><strong>Multi-Line with Area</strong></p>
256
+ <p align="center"><img src="assets/demo_multiline.png" alt="Multi-Line" width="100%" /></p>
257
+
258
+ ```python
259
+ fig = ec.figure()
260
+ fig.plot(df, x="Month", y="Value",
261
+ hue="Series", smooth=True,
262
+ area=True)
263
+ fig.show()
264
+ ```
265
+ </td>
266
+ <td width="50%">
267
+ <p align="center"><strong>Boxplot</strong></p>
268
+ <p align="center"><img src="assets/demo_boxplot.png" alt="Boxplot" width="100%" /></p>
269
+
270
+ ```python
271
+ fig = ec.figure()
272
+ fig.boxplot(df, x="Department", y="Salary")
273
+ fig.show()
274
+ ```
275
+ </td>
276
+ </tr>
277
+ </table>
278
+
279
+ ### Standalone Charts
280
+
281
+ <table>
282
+ <tr>
283
+ <td width="50%">
284
+ <p align="center"><strong>Donut / Pie</strong></p>
285
+ <p align="center"><img src="assets/demo_pie.png" alt="Donut Chart" width="100%" /></p>
286
+
287
+ ```python
288
+ fig = ec.figure()
289
+ fig.pie(df, names="Browser", values="Share",
290
+ inner_radius="40%")
291
+ fig.show()
292
+ ```
293
+ </td>
294
+ <td width="50%">
295
+ <p align="center"><strong>Radar</strong></p>
296
+ <p align="center"><img src="assets/demo_radar.png" alt="Radar Chart" width="100%" /></p>
297
+
298
+ ```python
299
+ fig = ec.figure()
300
+ fig.radar(indicators, data,
301
+ series_names=["Warrior","Mage"])
302
+ fig.show()
303
+ ```
304
+ </td>
305
+ </tr>
306
+ <tr>
307
+ <td width="50%">
308
+ <p align="center"><strong>Heatmap</strong></p>
309
+ <p align="center"><img src="assets/demo_heatmap.png" alt="Heatmap" width="100%" /></p>
310
+
311
+ ```python
312
+ fig = ec.figure()
313
+ fig.heatmap(df, x="Day", y="Hour",
314
+ value="Count")
315
+ fig.show()
316
+ ```
317
+ </td>
318
+ <td width="50%">
319
+ <p align="center"><strong>Funnel</strong></p>
320
+ <p align="center"><img src="assets/demo_funnel.png" alt="Funnel" width="100%" /></p>
321
+
322
+ ```python
323
+ fig = ec.figure()
324
+ fig.funnel(df, names="Stage", values="Count")
325
+ fig.show()
326
+ ```
327
+ </td>
328
+ </tr>
329
+ <tr>
330
+ <td width="50%">
331
+ <p align="center"><strong>Treemap</strong></p>
332
+ <p align="center"><img src="assets/demo_treemap.png" alt="Treemap" width="100%" /></p>
333
+
334
+ ```python
335
+ fig = ec.figure()
336
+ fig.treemap(df,
337
+ path=["Category","SubCat"],
338
+ value="Sales")
339
+ fig.show()
340
+ ```
341
+ </td>
342
+ <td width="50%">
343
+ <p align="center"><strong>Sankey Diagram</strong></p>
344
+ <p align="center"><img src="assets/demo_sankey.png" alt="Sankey Diagram" width="100%" /></p>
345
+
346
+ ```python
347
+ fig = ec.figure()
348
+ fig.sankey(df,
349
+ levels=["Source","Channel","Outcome"],
350
+ value="Users")
351
+ fig.show()
352
+ ```
353
+ </td>
354
+ </tr>
355
+ </table>
356
+
357
+ ### Composite & Dashboard Charts
358
+
359
+ <table>
360
+ <tr>
361
+ <td width="50%">
362
+ <p align="center"><strong>Bar + Pie (Dark)</strong></p>
363
+ <p align="center"><img src="assets/demo_composite_dark.png" alt="Composite Dark" width="100%" /></p>
364
+ </td>
365
+ <td width="50%">
366
+ <p align="center"><strong>Triple Composite</strong></p>
367
+ <p align="center"><img src="assets/demo_composite.png" alt="Triple Composite" width="100%" /></p>
368
+ </td>
369
+ </tr>
370
+ <tr>
371
+ <td width="50%">
372
+ <p align="center"><strong>KPI Dashboard</strong></p>
373
+ <p align="center"><img src="assets/demo_dashboard.png" alt="KPI Dashboard" width="100%" /></p>
374
+ </td>
375
+ <td width="50%">
376
+ <p align="center"><strong>Stacked + Trend + Pie</strong></p>
377
+ <p align="center"><img src="assets/demo_full_dashboard.png" alt="Full Dashboard" width="100%" /></p>
378
+ </td>
379
+ </tr>
380
+ <tr>
381
+ <td width="50%">
382
+ <p align="center"><strong>Side-by-Side Pies</strong></p>
383
+ <p align="center"><img src="assets/demo_sidebyside_pies.png" alt="Side-by-Side Pies" width="100%" /></p>
384
+ </td>
385
+ <td width="50%">
386
+ <p align="center"><strong>Dark Theme</strong></p>
387
+ <p align="center"><img src="assets/demo_dark.png" alt="Dark Theme" width="100%" /></p>
388
+ </td>
389
+ </tr>
390
+ </table>
391
+
392
+ > Every chart is fully interactive — hover for tooltips, click legend items to toggle series, use the toolbox to export.
393
+ > Open the [HTML demos in `assets/`](assets/) for the live experience, or run `python generate_demos.py` yourself.
394
+
395
+ ---
396
+
397
+ ## Supported Chart Types
398
+
399
+ | Category | Method | Description |
400
+ |:---|:---|:---|
401
+ | **Line** | `fig.plot()` | Smooth/straight lines, multi-series via `hue`, optional filled area |
402
+ | **Bar** | `fig.bar()` | Vertical/horizontal, grouped (`hue`), stacked, gradient fills |
403
+ | **Scatter** | `fig.scatter()` | Color and size encoding, numeric axes |
404
+ | **Histogram** | `fig.hist()` | Auto-binned frequency distribution |
405
+ | **Boxplot** | `fig.boxplot()` | Five-number statistical summary |
406
+ | **KDE** | `fig.kde()` | Kernel density estimation (requires `scipy`) |
407
+ | **Pie / Donut** | `fig.pie()` | Pie, donut (`inner_radius`), rose charts, side-by-side multiples |
408
+ | **Radar** | `fig.radar()` | Multi-indicator polygon charts |
409
+ | **Heatmap** | `fig.heatmap()` | Matrix visualisation with colour mapping |
410
+ | **Sankey** | `fig.sankey()` | Multi-level flow diagrams |
411
+ | **Treemap** | `fig.treemap()` | Hierarchical area charts |
412
+ | **Funnel** | `fig.funnel()` | Stage-based conversion funnels |
413
+
414
+ ---
415
+
416
+ ## Rendering Engines
417
+
418
+ echartsy writes your chart once; `ec.config()` controls where it renders.
419
+
420
+ | Engine | Use case | Install |
421
+ |:---|:---|:---|
422
+ | `"python"` | Standalone scripts — opens the default browser | No extra deps |
423
+ | `"jupyter"` | Jupyter Notebook / JupyterLab inline widgets | `pip install echartsy[jupyter]` |
424
+ | `"streamlit"` | Streamlit applications | `pip install echartsy[streamlit]` |
425
+
426
+ ```python
427
+ ec.config(engine="jupyter")
428
+ ```
429
+
430
+ ---
431
+
432
+ ## Adaptive Dark Mode
433
+
434
+ Charts automatically respond to the user's OS or browser `prefers-color-scheme` setting.
435
+
436
+ ```python
437
+ ec.config(engine="jupyter", adaptive="auto") # auto-detect (default)
438
+ ec.config(engine="jupyter", adaptive="dark") # force dark
439
+ ec.config(engine="jupyter", adaptive="light") # force light
440
+ ```
441
+
442
+ ---
443
+
444
+ ## Style Presets & Palettes
445
+
446
+ Apply a pre-built visual theme in one argument:
447
+
448
+ ```python
449
+ fig = ec.figure(style=ec.StylePreset.CLINICAL) # Clean defaults
450
+ fig = ec.figure(style=ec.StylePreset.DASHBOARD_DARK) # Dark background
451
+ fig = ec.figure(style=ec.StylePreset.KPI_REPORT) # Warm rusty tones
452
+ fig = ec.figure(style=ec.StylePreset.MINIMAL) # Minimal & light
453
+ ```
454
+
455
+ Or set a custom palette at any time:
456
+
457
+ ```python
458
+ fig.palette(["#667eea", "#764ba2", "#f093fb", "#f5576c", "#4facfe"])
459
+ fig.palette(ec.PALETTE_RUSTY)
460
+ fig.palette(ec.PALETTE_CLINICAL)
461
+ ```
462
+
463
+ Build your own `StylePreset` for full control over fonts, grid lines, tooltip style, and more:
464
+
465
+ ```python
466
+ my_style = ec.StylePreset(
467
+ palette=("#264653", "#2a9d8f", "#e9c46a", "#f4a261", "#e76f51"),
468
+ bg="#fefae0",
469
+ font_family="Georgia",
470
+ title_font_size=20,
471
+ )
472
+ fig = ec.figure(style=my_style)
473
+ ```
474
+
475
+ ---
476
+
477
+ ## Composite Charts
478
+
479
+ Overlay pies on cartesian charts, or combine bar + line on dual axes — all on one figure.
480
+
481
+ ```python
482
+ fig = ec.figure(height="550px")
483
+
484
+ # Primary axis: bars
485
+ fig.bar(df, x="Month", y="Revenue", labels=True, border_radius=4)
486
+
487
+ # Secondary axis: trend line
488
+ fig.plot(df, x="Month", y="Growth", smooth=True, axis=1, line_width=3)
489
+
490
+ # Inset pie
491
+ fig.pie(df_mix, names="Plan", values="Share",
492
+ center=["25%", "32%"], radius=["15%", "25%"])
493
+
494
+ fig.ylabel("Revenue ($K)")
495
+ fig.ylabel_right("Growth %")
496
+ fig.legend(top=40, left=350)
497
+ fig.show()
498
+ ```
499
+
500
+ ---
501
+
502
+ ## Timeline Animations
503
+
504
+ Animate any chart across a time dimension with `TimelineFigure`. It mirrors the `Figure` API; every series method gains one extra parameter: `time_col`.
505
+
506
+ ```python
507
+ fig = ec.TimelineFigure(height="500px", interval=1.5)
508
+ fig.bar(df, x="Country", y="GDP", time_col="Year", labels=True)
509
+ fig.title("GDP by Country")
510
+ fig.ylabel("GDP (Trillion USD)")
511
+ fig.legend(top=30)
512
+ fig.show()
513
+ ```
514
+
515
+ <p align="center">
516
+ <img src="assets/demo_timeline.png" alt="Timeline Animation" width="700" />
517
+ </p>
518
+
519
+ **TimelineFigure features:**
520
+
521
+ | Feature | API |
522
+ |:---|:---|
523
+ | Playback control | `TimelineFigure(interval=2.0, autoplay=True, loop=True)` |
524
+ | Adjust after creation | `fig.playback(interval=1.0, rewind=True)` |
525
+ | Smart frame sorting | Parses years, quarters (`Q1 2024`), months (`Jan 2024`), ISO dates, fiscal years |
526
+ | Supported series | `bar()`, `plot()`, `scatter()`, `pie()` |
527
+ | Diagnose format | `ec.detect_time_format(df["Year"])` |
528
+
529
+ ---
530
+
531
+ ## Chart Configuration Reference
532
+
533
+ Every `Figure` and `TimelineFigure` supports these configuration methods:
534
+
535
+ ```python
536
+ # Titles
537
+ fig.title("Main Title", subtitle="Sub-title")
538
+
539
+ # Axes
540
+ fig.xlabel("X Label", rotate=30)
541
+ fig.ylabel("Y Label")
542
+ fig.ylabel_right("Secondary Y")
543
+ fig.xlim(0, 100)
544
+ fig.ylim(0, 500)
545
+
546
+ # Layout
547
+ fig.legend(orient="vertical", left="right", top=40)
548
+ fig.margins(left=100, right=120, top=40)
549
+ fig.grid(show=True)
550
+
551
+ # Interactivity
552
+ fig.datazoom(start=0, end=80)
553
+ fig.toolbox(download=True, zoom=True)
554
+ fig.tooltip(trigger="axis")
555
+
556
+ # Export
557
+ fig.save(name="my_chart", fmt="png", dpi=3)
558
+ fig.to_html("my_chart.html")
559
+
560
+ # Palette
561
+ fig.palette(["#5470C6", "#91CC75", "#FAC858"])
562
+ ```
563
+
564
+ ---
565
+
566
+ ## Exporting
567
+
568
+ ```python
569
+ # Standalone HTML file (fully interactive, no server needed)
570
+ fig.to_html("report.html")
571
+
572
+ # Raw ECharts option dict (for debugging or custom renderers)
573
+ option = fig.to_option()
574
+ ```
575
+
576
+ ---
577
+
578
+ ## API at a Glance
579
+
580
+ ### `ec.config(engine, adaptive="auto")`
581
+
582
+ Set the global rendering engine (`"python"`, `"jupyter"`, `"streamlit"`) and theme adaptation mode (`"auto"`, `"light"`, `"dark"`).
583
+
584
+ ### `ec.figure(**kwargs)` / `ec.Figure(**kwargs)`
585
+
586
+ Create a chart canvas. Key keyword arguments:
587
+
588
+ | Parameter | Default | Description |
589
+ |:---|:---|:---|
590
+ | `height` | `"400px"` | CSS height of the chart container |
591
+ | `width` | `None` | CSS width (defaults to full container) |
592
+ | `renderer` | `"canvas"` | `"canvas"` or `"svg"` |
593
+ | `style` | `StylePreset.CLINICAL` | A `StylePreset` instance |
594
+
595
+ ### `ec.TimelineFigure(**kwargs)` / `ec.timeline_figure(**kwargs)`
596
+
597
+ Same as `Figure` but adds timeline animation. Extra parameters:
598
+
599
+ | Parameter | Default | Description |
600
+ |:---|:---|:---|
601
+ | `interval` | `2.0` | Seconds between animation frames |
602
+ | `autoplay` | `True` | Start playing automatically |
603
+ | `loop` | `True` | Loop back to the first frame |
604
+
605
+ ### `ec.StylePreset`
606
+
607
+ Frozen dataclass bundling visual defaults: `palette`, `bg`, `font_family`, `title_font_size`, `axis_label_font_size`, `grid_line_color`, and more.
608
+
609
+ ### `ec.detect_time_format(series)`
610
+
611
+ Diagnostic helper that inspects a pandas Series and reports how well TimelineFigure will parse its values.
612
+
613
+ ---
614
+
615
+ ## Generating Showcase Images
616
+
617
+ To regenerate the demo screenshots shown above:
618
+
619
+ ```bash
620
+ # 1. Generate the interactive HTML demos
621
+ python generate_demos.py
622
+
623
+ # 2. Capture PNG screenshots (requires Playwright)
624
+ pip install playwright && playwright install chromium
625
+ python capture_screenshots.py
626
+ ```
627
+
628
+ ---
629
+
630
+ ## Contributing
631
+
632
+ Contributions, bug reports, and feature requests are welcome. Please open an [issue](https://github.com/astrojigs/echartsy/issues) or submit a pull request on [GitHub](https://github.com/astrojigs/echartsy).
633
+
634
+ ## License
635
+
636
+ [MIT](LICENSE) &mdash; Jigar, 2026