maidr 1.7.2__tar.gz → 1.8.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.
- {maidr-1.7.2 → maidr-1.8.0}/CHANGELOG.md +33 -0
- {maidr-1.7.2 → maidr-1.8.0}/PKG-INFO +1 -1
- {maidr-1.7.2 → maidr-1.8.0}/example/flask/test_flask_app.py +13 -6
- {maidr-1.7.2 → maidr-1.8.0}/maidr/__init__.py +1 -1
- maidr-1.8.0/maidr/api.py +155 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/core/figure_manager.py +53 -8
- {maidr-1.7.2 → maidr-1.8.0}/maidr/core/maidr.py +47 -6
- {maidr-1.7.2 → maidr-1.8.0}/maidr/core/plot/candlestick.py +17 -4
- {maidr-1.7.2 → maidr-1.8.0}/maidr/core/plot/lineplot.py +3 -1
- {maidr-1.7.2 → maidr-1.8.0}/maidr/core/plot/maidr_plot.py +1 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/core/plot/mplfinance_lineplot.py +4 -1
- maidr-1.8.0/maidr/patch/barplot.py +218 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/patch/mplfinance.py +2 -1
- {maidr-1.7.2 → maidr-1.8.0}/maidr/util/datetime_conversion.py +35 -20
- {maidr-1.7.2 → maidr-1.8.0}/maidr/util/environment.py +5 -9
- {maidr-1.7.2 → maidr-1.8.0}/maidr/util/mixin/extractor_mixin.py +3 -1
- {maidr-1.7.2 → maidr-1.8.0}/maidr/util/mplfinance_utils.py +8 -8
- {maidr-1.7.2 → maidr-1.8.0}/pyproject.toml +3 -2
- {maidr-1.7.2 → maidr-1.8.0}/uv.lock +1425 -1082
- maidr-1.7.2/example/stacked/matplotlib/example_mpl_stacked.html +0 -9
- maidr-1.7.2/example/stacked/seaborn/example_sns_stacked.html +0 -479
- maidr-1.7.2/maidr/api.py +0 -64
- maidr-1.7.2/maidr/patch/barplot.py +0 -77
- {maidr-1.7.2 → maidr-1.8.0}/.commitlintrc.cjs +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/.editorconfig +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/.github/copilot-instructions.md +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/.github/workflows/ci.yml +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/.github/workflows/docs.yml +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/.github/workflows/release.yml +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/.gitignore +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/.pre-commit-config.yaml +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/.vscode/extensions.json +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/.vscode/settings.json +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/CONDUCT.md +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/CONTRIBUTING.md +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/LICENSE +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/README.md +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/docs/.gitignore +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/docs/CNAME +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/docs/_environment +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/docs/_extensions/machow/interlinks/.gitignore +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/docs/_extensions/machow/interlinks/_extension.yml +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/docs/_extensions/machow/interlinks/interlinks.lua +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/docs/_extensions/shafayetShafee/line-highlight/_extension.yml +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/docs/_extensions/shafayetShafee/line-highlight/line-highlight.lua +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/docs/_extensions/shafayetShafee/line-highlight/resources/css/line-highlight.css +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/docs/_extensions/shafayetShafee/line-highlight/resources/js/line-highlight.js +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/docs/_quarto.yml +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/docs/examples.qmd +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/docs/index.qmd +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/docs/styles.css +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/bar/example_bar_plot.ipynb +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/bar/matplotlib/example_mpl_bar_plot.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/bar/seaborn/example_sns_bar_plot.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/box/example_box_plot.ipynb +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/box/matplotlib/example_mpl_box.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/box/seaborn/example_sns_box.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/candle_stick/legacy_candlestick_example.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/candle_stick/mplfinance_candlestick_example.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/candle_stick/test_data_daily_current_year.csv +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/candle_stick/test_data_daily_mixed_years.csv +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/candle_stick/test_data_hourly.csv +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/candle_stick/test_data_minute.csv +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/candle_stick/test_data_weekly.csv +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/candle_stick/volcandat.csv +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/count/example_count_plot.ipynb +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/count/seaborn/example_sns_count_plot.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/dodged/matplotlib/example_mpl_dodged.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/dodged/seaborn/example_sns_dodged.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/facet-subplots/matplotlib/example_mpl_facet_bar_plot.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/facet-subplots/matplotlib/example_mpl_facet_combined_plot.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/facet-subplots/seaborn/example_sns_facet_bar_plot.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/facet-subplots/seaborn/example_sns_facet_combined_plot.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/heatmap/example_heatmap_plot.ipynb +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/heatmap/matplotlib/example_mpl_heatmap.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/heatmap/seaborn/example_sns_heatmap.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/histogram/example_histogram_plot.ipynb +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/histogram/matplotlib/example_mpl_hist.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/histogram/matplotlib/histogram_with_kde_matplotlib.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/histogram/seaborn/example_sns_hist.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/histogram/seaborn/histogram_with_kde_seaborn.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/kde/example_kde_plots.ipynb +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/kde/matplotlib/multiple_kde_matplotlib.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/kde/matplotlib/single_kde_matplotlib.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/kde/seaborn/multiple_kde_seaborn.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/kde/seaborn/single_kde_seaborn.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/line/example_line_plot.ipynb +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/line/matplotlib/example_mpl_line.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/line/seaborn/example_sns_line.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/multilayer/example_mpl_multilayer.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/multilayer/example_multilayer_plot.ipynb +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/multiline/example_multiline_plot.ipynb +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/multiline/matplotlib/example_mpl_multiline.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/multiline/seaborn/example_sns_multiline.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/multipanel/example_multipanel_plot.ipynb +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/multipanel/matplotlib/example_mpl_multipanel.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/multipanel/seaborn/example_sns_multipanel.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/quarto/demo.qmd +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/reg/example_reg_plots.ipynb +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/reg/matplotlib/example_matplotlib_smooth_plot.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/reg/seaborn/example_sns_reg.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/scatter/example_scatter_plot.ipynb +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/scatter/matplotlib/example_mpl_scatter.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/scatter/seaborn/example_sns_scatter.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/shiny/example_shiny_scatter.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/stacked/matplotlib/example_mpl_stacked.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/stacked/seaborn/example_sns_stacked.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/example/streamlit/example_streamlit_app.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/core/__init__.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/core/context_manager.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/core/enum/__init__.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/core/enum/library.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/core/enum/maidr_key.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/core/enum/plot_type.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/core/enum/smooth_keywords.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/core/plot/__init__.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/core/plot/barplot.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/core/plot/boxplot.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/core/plot/grouped_barplot.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/core/plot/heatmap.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/core/plot/histogram.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/core/plot/maidr_plot_factory.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/core/plot/mplfinance_barplot.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/core/plot/regplot.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/core/plot/scatterplot.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/exception/__init__.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/exception/extraction_error.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/patch/__init__.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/patch/boxplot.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/patch/candlestick.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/patch/clear.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/patch/common.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/patch/heatmap.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/patch/highlight.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/patch/histogram.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/patch/kdeplot.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/patch/lineplot.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/patch/regplot.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/patch/scatterplot.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/util/__init__.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/util/dedup_utils.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/util/mixin/__init__.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/util/mixin/merger_mixin.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/util/plot_detection.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/util/regression_line_utils.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/util/svg_utils.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/widget/__init__.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/maidr/widget/shiny.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/tests/__init__.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/tests/conftest.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/tests/core/__init__.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/tests/core/enum/__init__.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/tests/core/plot/__init__.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/tests/core/test_figure_manager.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/tests/core/test_maidr_plot.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/tests/core/test_maidr_plot_factory.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/tests/fixture/__init__.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/tests/fixture/library_factory.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/tests/fixture/matplotlib_factory.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/tests/fixture/seaborn_factory.py +0 -0
- {maidr-1.7.2 → maidr-1.8.0}/tox.ini +0 -0
|
@@ -1,6 +1,39 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
## v1.8.0 (2025-09-17)
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
- Remove maidr.show params ([#244](https://github.com/xability/py-maidr/pull/244),
|
|
9
|
+
[`493cf57`](https://github.com/xability/py-maidr/commit/493cf5713068b1514b4836e46763c3619a51dd23))
|
|
10
|
+
|
|
11
|
+
### Refactoring
|
|
12
|
+
|
|
13
|
+
- **maidr.api**: Improve lazy figure detection, eliminate code duplication, and resolve merge
|
|
14
|
+
conflicts ([#241](https://github.com/xability/py-maidr/pull/241),
|
|
15
|
+
[`15a966e`](https://github.com/xability/py-maidr/commit/15a966ea2ca9170ddf8bc28634705fb6233a1d58))
|
|
16
|
+
|
|
17
|
+
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
|
|
18
|
+
|
|
19
|
+
Co-authored-by: jooyoungseo <19754711+jooyoungseo@users.noreply.github.com>
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## v1.7.3 (2025-09-15)
|
|
23
|
+
|
|
24
|
+
### Bug Fixes
|
|
25
|
+
|
|
26
|
+
- Ensure all subplots are accessible and improve dodged plot detection
|
|
27
|
+
([#242](https://github.com/xability/py-maidr/pull/242),
|
|
28
|
+
[`979b971`](https://github.com/xability/py-maidr/commit/979b9713d07be8bc9046056e7f1c0519336ddd22))
|
|
29
|
+
|
|
30
|
+
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
|
|
31
|
+
|
|
32
|
+
Co-authored-by: jooyoungseo <19754711+jooyoungseo@users.noreply.github.com>
|
|
33
|
+
|
|
34
|
+
Co-authored-by: JooYoung Seo <jseo1005@illinois.edu>
|
|
35
|
+
|
|
36
|
+
|
|
4
37
|
## v1.7.2 (2025-09-12)
|
|
5
38
|
|
|
6
39
|
### Bug Fixes
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import matplotlib
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
matplotlib.use("Agg") # Use non-interactive backend for Flask
|
|
3
4
|
|
|
4
5
|
from flask import Flask
|
|
5
6
|
import matplotlib.pyplot as plt
|
|
@@ -14,7 +15,8 @@ logger = logging.getLogger(__name__)
|
|
|
14
15
|
|
|
15
16
|
app = Flask(__name__)
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
|
|
19
|
+
@app.route("/")
|
|
18
20
|
def index():
|
|
19
21
|
# Log environment detection
|
|
20
22
|
logger.info("=== Environment Detection ===")
|
|
@@ -34,8 +36,12 @@ def index():
|
|
|
34
36
|
logger.info(f"flask_detected: {flask_detected}")
|
|
35
37
|
logger.info(f"notebook_detected: {notebook_detected}")
|
|
36
38
|
logger.info(f"shiny_detected: {shiny_detected}")
|
|
37
|
-
logger.info(
|
|
38
|
-
|
|
39
|
+
logger.info(
|
|
40
|
+
"Condition: use_iframe and (flask_detected or notebook_detected or shiny_detected)"
|
|
41
|
+
)
|
|
42
|
+
logger.info(
|
|
43
|
+
f"Result: {use_iframe and (flask_detected or notebook_detected or shiny_detected)}"
|
|
44
|
+
)
|
|
39
45
|
|
|
40
46
|
# Load the penguins dataset
|
|
41
47
|
penguins = sns.load_dataset("penguins")
|
|
@@ -65,7 +71,8 @@ def index():
|
|
|
65
71
|
# Return the maidr HTML directly
|
|
66
72
|
return str(maidr_html)
|
|
67
73
|
|
|
68
|
-
|
|
74
|
+
|
|
75
|
+
if __name__ == "__main__":
|
|
69
76
|
logger.info("Starting Flask app with logging...")
|
|
70
77
|
logger.info("Visit http://localhost:5002 to see the maidr plot in Flask")
|
|
71
|
-
app.run(debug=False, host=
|
|
78
|
+
app.run(debug=False, host="0.0.0.0", port=5002)
|
maidr-1.8.0/maidr/api.py
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Literal
|
|
4
|
+
|
|
5
|
+
from htmltools import Tag
|
|
6
|
+
from matplotlib.axes import Axes
|
|
7
|
+
from matplotlib.container import BarContainer
|
|
8
|
+
|
|
9
|
+
from maidr.core import Maidr
|
|
10
|
+
from maidr.core.enum import PlotType
|
|
11
|
+
from maidr.core.figure_manager import FigureManager
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _get_plot_or_current(plot: Any | None) -> Any:
|
|
15
|
+
"""
|
|
16
|
+
Get the plot object or current matplotlib figure if plot is None.
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
plot : Any or None
|
|
21
|
+
The plot object. If None, returns the current matplotlib figure.
|
|
22
|
+
|
|
23
|
+
Returns
|
|
24
|
+
-------
|
|
25
|
+
Any
|
|
26
|
+
The plot object or current matplotlib figure.
|
|
27
|
+
"""
|
|
28
|
+
if plot is None:
|
|
29
|
+
# Lazy import matplotlib.pyplot when needed
|
|
30
|
+
import matplotlib.pyplot as plt
|
|
31
|
+
|
|
32
|
+
return plt.gcf()
|
|
33
|
+
return plot
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def render(plot: Any | None = None) -> Tag:
|
|
37
|
+
"""
|
|
38
|
+
Render a MAIDR plot to HTML.
|
|
39
|
+
|
|
40
|
+
Parameters
|
|
41
|
+
----------
|
|
42
|
+
plot : Any or None, optional
|
|
43
|
+
The plot object to render. If None, uses the current matplotlib figure.
|
|
44
|
+
|
|
45
|
+
Returns
|
|
46
|
+
-------
|
|
47
|
+
htmltools.Tag
|
|
48
|
+
The rendered HTML representation of the plot.
|
|
49
|
+
"""
|
|
50
|
+
plot = _get_plot_or_current(plot)
|
|
51
|
+
|
|
52
|
+
ax = FigureManager.get_axes(plot)
|
|
53
|
+
if isinstance(ax, list):
|
|
54
|
+
for axes in ax:
|
|
55
|
+
maidr = FigureManager.get_maidr(axes.get_figure())
|
|
56
|
+
return maidr.render()
|
|
57
|
+
else:
|
|
58
|
+
maidr = FigureManager.get_maidr(ax.get_figure())
|
|
59
|
+
return maidr.render()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def show(
|
|
63
|
+
plot: Any | None = None,
|
|
64
|
+
renderer: Literal["auto", "ipython", "browser"] = "auto",
|
|
65
|
+
clear_fig: bool = True,
|
|
66
|
+
) -> object:
|
|
67
|
+
"""
|
|
68
|
+
Display a MAIDR plot.
|
|
69
|
+
|
|
70
|
+
Parameters
|
|
71
|
+
----------
|
|
72
|
+
plot : Any or None, optional
|
|
73
|
+
The plot object to display. If None, uses the current matplotlib figure.
|
|
74
|
+
renderer : {"auto", "ipython", "browser"}, default "auto"
|
|
75
|
+
The renderer to use for display.
|
|
76
|
+
clear_fig : bool, default True
|
|
77
|
+
Whether to clear the figure after displaying.
|
|
78
|
+
|
|
79
|
+
Returns
|
|
80
|
+
-------
|
|
81
|
+
object
|
|
82
|
+
The display result.
|
|
83
|
+
"""
|
|
84
|
+
plot = _get_plot_or_current(plot)
|
|
85
|
+
|
|
86
|
+
ax = FigureManager.get_axes(plot)
|
|
87
|
+
if isinstance(ax, list):
|
|
88
|
+
for axes in ax:
|
|
89
|
+
maidr = FigureManager.get_maidr(axes.get_figure())
|
|
90
|
+
return maidr.show(renderer)
|
|
91
|
+
else:
|
|
92
|
+
maidr = FigureManager.get_maidr(ax.get_figure())
|
|
93
|
+
return maidr.show(renderer, clear_fig=clear_fig)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def save_html(
|
|
97
|
+
plot: Any | None = None,
|
|
98
|
+
*,
|
|
99
|
+
file: str,
|
|
100
|
+
lib_dir: str | None = "lib",
|
|
101
|
+
include_version: bool = True
|
|
102
|
+
) -> str:
|
|
103
|
+
"""
|
|
104
|
+
Save a MAIDR plot as HTML file.
|
|
105
|
+
|
|
106
|
+
Parameters
|
|
107
|
+
----------
|
|
108
|
+
plot : Any or None, optional
|
|
109
|
+
The plot object to save. If None, uses the current matplotlib figure.
|
|
110
|
+
file : str
|
|
111
|
+
The file path where to save the HTML.
|
|
112
|
+
lib_dir : str or None, default "lib"
|
|
113
|
+
Directory name for libraries.
|
|
114
|
+
include_version : bool, default True
|
|
115
|
+
Whether to include version information.
|
|
116
|
+
|
|
117
|
+
Returns
|
|
118
|
+
-------
|
|
119
|
+
str
|
|
120
|
+
The path to the saved HTML file.
|
|
121
|
+
"""
|
|
122
|
+
plot = _get_plot_or_current(plot)
|
|
123
|
+
|
|
124
|
+
ax = FigureManager.get_axes(plot)
|
|
125
|
+
htmls = []
|
|
126
|
+
if isinstance(ax, list):
|
|
127
|
+
for axes in ax:
|
|
128
|
+
maidr = FigureManager.get_maidr(axes.get_figure())
|
|
129
|
+
htmls.append(maidr._create_html_doc(use_iframe=False))
|
|
130
|
+
return htmls[-1].save_html(
|
|
131
|
+
file, libdir=lib_dir, include_version=include_version
|
|
132
|
+
)
|
|
133
|
+
else:
|
|
134
|
+
maidr = FigureManager.get_maidr(ax.get_figure())
|
|
135
|
+
return maidr.save_html(file, lib_dir=lib_dir, include_version=include_version)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def stacked(plot: Axes | BarContainer) -> Maidr:
|
|
139
|
+
ax = FigureManager.get_axes(plot)
|
|
140
|
+
return FigureManager.create_maidr(ax, PlotType.STACKED)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def close(plot: Any | None = None) -> None:
|
|
144
|
+
"""
|
|
145
|
+
Close a MAIDR plot and clean up resources.
|
|
146
|
+
|
|
147
|
+
Parameters
|
|
148
|
+
----------
|
|
149
|
+
plot : Any or None, optional
|
|
150
|
+
The plot object to close. If None, uses the current matplotlib figure.
|
|
151
|
+
"""
|
|
152
|
+
plot = _get_plot_or_current(plot)
|
|
153
|
+
|
|
154
|
+
ax = FigureManager.get_axes(plot)
|
|
155
|
+
FigureManager.destroy(ax.get_figure())
|
|
@@ -15,20 +15,24 @@ from maidr.core.plot import MaidrPlotFactory
|
|
|
15
15
|
|
|
16
16
|
class FigureManager:
|
|
17
17
|
"""
|
|
18
|
-
Manages creation and retrieval of Maidr instances associated with
|
|
18
|
+
Manages creation and retrieval of Maidr instances associated with figures.
|
|
19
19
|
|
|
20
|
-
This class provides methods to manage Maidr objects which facilitate the
|
|
21
|
-
manipulation of plots within matplotlib figures.
|
|
20
|
+
This class provides methods to manage Maidr objects which facilitate the
|
|
21
|
+
organization and manipulation of plots within matplotlib figures.
|
|
22
22
|
|
|
23
23
|
Attributes
|
|
24
24
|
----------
|
|
25
25
|
figs : dict
|
|
26
|
-
A dictionary that maps matplotlib Figure objects to their corresponding
|
|
26
|
+
A dictionary that maps matplotlib Figure objects to their corresponding
|
|
27
|
+
Maidr instances.
|
|
28
|
+
PLOT_TYPE_PRIORITY : dict
|
|
29
|
+
Defines the priority order for plot types. Higher numbers take precedence.
|
|
27
30
|
|
|
28
31
|
Methods
|
|
29
32
|
-------
|
|
30
33
|
create_maidr(ax, plot_type, **kwargs)
|
|
31
|
-
Creates a Maidr instance for the given Axes and plot type, and adds a
|
|
34
|
+
Creates a Maidr instance for the given Axes and plot type, and adds a
|
|
35
|
+
plot to it.
|
|
32
36
|
_get_maidr(fig)
|
|
33
37
|
Retrieves or creates a Maidr instance associated with the given Figure.
|
|
34
38
|
get_axes(artist)
|
|
@@ -37,6 +41,21 @@ class FigureManager:
|
|
|
37
41
|
|
|
38
42
|
figs = {}
|
|
39
43
|
|
|
44
|
+
# Define plot type priority order (higher numbers take precedence)
|
|
45
|
+
PLOT_TYPE_PRIORITY = {
|
|
46
|
+
PlotType.BAR: 1,
|
|
47
|
+
PlotType.STACKED: 2,
|
|
48
|
+
PlotType.DODGED: 2, # DODGED and STACKED have same priority
|
|
49
|
+
PlotType.LINE: 1,
|
|
50
|
+
PlotType.SCATTER: 1,
|
|
51
|
+
PlotType.HIST: 1,
|
|
52
|
+
PlotType.BOX: 1,
|
|
53
|
+
PlotType.HEAT: 1,
|
|
54
|
+
PlotType.COUNT: 1,
|
|
55
|
+
PlotType.SMOOTH: 1,
|
|
56
|
+
PlotType.CANDLESTICK: 1,
|
|
57
|
+
}
|
|
58
|
+
|
|
40
59
|
_instance = None
|
|
41
60
|
_lock = threading.Lock()
|
|
42
61
|
|
|
@@ -44,14 +63,15 @@ class FigureManager:
|
|
|
44
63
|
if not cls._instance:
|
|
45
64
|
with cls._lock:
|
|
46
65
|
if not cls._instance:
|
|
47
|
-
cls._instance = super(FigureManager, cls).__new__()
|
|
66
|
+
cls._instance = super(FigureManager, cls).__new__(cls)
|
|
48
67
|
return cls._instance
|
|
49
68
|
|
|
50
69
|
@classmethod
|
|
51
70
|
def create_maidr(
|
|
52
71
|
cls, axes: Axes | list[Axes], plot_type: PlotType, **kwargs
|
|
53
72
|
) -> Maidr:
|
|
54
|
-
"""Create a Maidr instance for the given Axes and plot type, and
|
|
73
|
+
"""Create a Maidr instance for the given Axes and plot type, and
|
|
74
|
+
adds a plot to it."""
|
|
55
75
|
if axes is None:
|
|
56
76
|
raise ValueError("No plot found.")
|
|
57
77
|
if plot_type is None:
|
|
@@ -72,9 +92,34 @@ class FigureManager:
|
|
|
72
92
|
|
|
73
93
|
@classmethod
|
|
74
94
|
def _get_maidr(cls, fig: Figure, plot_type: PlotType) -> Maidr:
|
|
75
|
-
"""
|
|
95
|
+
"""
|
|
96
|
+
Retrieve or create a Maidr instance for the given Figure.
|
|
97
|
+
|
|
98
|
+
If a Maidr instance already exists for the figure, update its plot type
|
|
99
|
+
if the new plot type has higher priority (DODGED/STACKED > BAR).
|
|
100
|
+
|
|
101
|
+
Parameters
|
|
102
|
+
----------
|
|
103
|
+
fig : Figure
|
|
104
|
+
The matplotlib figure to get or create a Maidr instance for.
|
|
105
|
+
plot_type : PlotType
|
|
106
|
+
The plot type to set or update for the Maidr instance.
|
|
107
|
+
|
|
108
|
+
Returns
|
|
109
|
+
-------
|
|
110
|
+
Maidr
|
|
111
|
+
The Maidr instance associated with the figure.
|
|
112
|
+
"""
|
|
76
113
|
if fig not in cls.figs.keys():
|
|
77
114
|
cls.figs[fig] = Maidr(fig, plot_type)
|
|
115
|
+
else:
|
|
116
|
+
# Update plot type if the new type has higher priority
|
|
117
|
+
maidr = cls.figs[fig]
|
|
118
|
+
current_priority = cls.PLOT_TYPE_PRIORITY.get(maidr.plot_type, 0)
|
|
119
|
+
new_priority = cls.PLOT_TYPE_PRIORITY.get(plot_type, 0)
|
|
120
|
+
|
|
121
|
+
if new_priority > current_priority:
|
|
122
|
+
maidr.plot_type = plot_type
|
|
78
123
|
return cls.figs[fig]
|
|
79
124
|
|
|
80
125
|
@classmethod
|
|
@@ -10,6 +10,7 @@ import webbrowser
|
|
|
10
10
|
import subprocess
|
|
11
11
|
from pathlib import Path
|
|
12
12
|
from typing import Any, Literal, cast
|
|
13
|
+
from collections import defaultdict
|
|
13
14
|
|
|
14
15
|
import matplotlib.pyplot as plt
|
|
15
16
|
from htmltools import HTML, HTMLDocument, Tag, tags
|
|
@@ -159,10 +160,7 @@ class Maidr:
|
|
|
159
160
|
if explorer_path:
|
|
160
161
|
try:
|
|
161
162
|
result = subprocess.run(
|
|
162
|
-
[explorer_path, url],
|
|
163
|
-
capture_output=True,
|
|
164
|
-
text=True,
|
|
165
|
-
timeout=10
|
|
163
|
+
[explorer_path, url], capture_output=True, text=True, timeout=10
|
|
166
164
|
)
|
|
167
165
|
|
|
168
166
|
if result.returncode == 0:
|
|
@@ -204,10 +202,49 @@ class Maidr:
|
|
|
204
202
|
"""Create an HTML document from Tag objects."""
|
|
205
203
|
return HTMLDocument(self._create_html_tag(use_iframe), lang="en")
|
|
206
204
|
|
|
205
|
+
def _merge_plots_by_subplot_position(self) -> list[MaidrPlot]:
|
|
206
|
+
"""
|
|
207
|
+
Merge plots by their subplot position, keeping only the first plot per position.
|
|
208
|
+
|
|
209
|
+
For DODGED and STACKED plot types, multiple plots on the same subplot
|
|
210
|
+
should be merged into a single plot since GroupedBarPlot extracts all
|
|
211
|
+
containers from the axes itself.
|
|
212
|
+
|
|
213
|
+
Returns
|
|
214
|
+
-------
|
|
215
|
+
list[MaidrPlot]
|
|
216
|
+
List of plots with one plot per unique subplot position.
|
|
217
|
+
|
|
218
|
+
Examples
|
|
219
|
+
--------
|
|
220
|
+
If we have plots at positions [(0,0), (0,0), (0,1), (1,0)],
|
|
221
|
+
this will return plots at positions [(0,0), (0,1), (1,0)].
|
|
222
|
+
"""
|
|
223
|
+
# Group plots by their subplot position (row, col) using defaultdict
|
|
224
|
+
subplot_groups: dict[tuple[int, int], list[MaidrPlot]] = defaultdict(list)
|
|
225
|
+
|
|
226
|
+
for plot in self._plots:
|
|
227
|
+
# Get subplot position, defaulting to (0, 0) if not set
|
|
228
|
+
position = (getattr(plot, "row_index", 0), getattr(plot, "col_index", 0))
|
|
229
|
+
subplot_groups[position].append(plot)
|
|
230
|
+
|
|
231
|
+
# Keep only the first plot for each subplot position
|
|
232
|
+
# The GroupedBarPlot will extract all containers from that axes
|
|
233
|
+
merged_plots: list[MaidrPlot] = []
|
|
234
|
+
for position_plots in subplot_groups.values():
|
|
235
|
+
merged_plots.append(
|
|
236
|
+
position_plots[0]
|
|
237
|
+
) # Each list is guaranteed to have at least one plot
|
|
238
|
+
|
|
239
|
+
return merged_plots
|
|
240
|
+
|
|
207
241
|
def _flatten_maidr(self) -> dict | list[dict]:
|
|
208
242
|
"""Return a single plot schema or a list of schemas from the Maidr instance."""
|
|
243
|
+
# Handle DODGED/STACKED plots: only keep one plot per subplot position
|
|
244
|
+
# because GroupedBarPlot extracts all containers from the axes itself
|
|
209
245
|
if self.plot_type in (PlotType.DODGED, PlotType.STACKED):
|
|
210
|
-
self._plots =
|
|
246
|
+
self._plots = self._merge_plots_by_subplot_position()
|
|
247
|
+
|
|
211
248
|
# Deduplicate: if any SMOOTH plots exist, remove LINE plots
|
|
212
249
|
self._plots = deduplicate_smooth_and_line(self._plots)
|
|
213
250
|
|
|
@@ -354,7 +391,11 @@ class Maidr:
|
|
|
354
391
|
# Render the plot inside an iframe if in a Jupyter notebook, Google Colab
|
|
355
392
|
# or VSCode notebook. No need for iframe if this is a Quarto document.
|
|
356
393
|
# For TypeScript we will use iframe by default for now
|
|
357
|
-
if use_iframe and (
|
|
394
|
+
if use_iframe and (
|
|
395
|
+
Environment.is_flask()
|
|
396
|
+
or Environment.is_notebook()
|
|
397
|
+
or Environment.is_shiny()
|
|
398
|
+
):
|
|
358
399
|
unique_id = "iframe_" + Maidr._unique_id()
|
|
359
400
|
|
|
360
401
|
def generate_iframe_script(unique_id: str) -> str:
|
|
@@ -43,7 +43,9 @@ class CandlestickPlot(MaidrPlot):
|
|
|
43
43
|
self._maidr_wick_collection = kwargs.get("_maidr_wick_collection", None)
|
|
44
44
|
self._maidr_body_collection = kwargs.get("_maidr_body_collection", None)
|
|
45
45
|
self._maidr_date_nums = kwargs.get("_maidr_date_nums", None)
|
|
46
|
-
self._maidr_original_data = kwargs.get(
|
|
46
|
+
self._maidr_original_data = kwargs.get(
|
|
47
|
+
"_maidr_original_data", None
|
|
48
|
+
) # Store original data
|
|
47
49
|
self._maidr_datetime_converter = kwargs.get("_maidr_datetime_converter", None)
|
|
48
50
|
|
|
49
51
|
# Store the GID for proper selector generation (legacy/shared)
|
|
@@ -122,7 +124,9 @@ class CandlestickPlot(MaidrPlot):
|
|
|
122
124
|
for rect in body_rectangles:
|
|
123
125
|
rect.set_gid(self._maidr_gid)
|
|
124
126
|
# Keep a dedicated body gid for legacy dict selectors
|
|
125
|
-
self._maidr_body_gid =
|
|
127
|
+
self._maidr_body_gid = (
|
|
128
|
+
getattr(self, "_maidr_body_gid", None) or self._maidr_gid
|
|
129
|
+
)
|
|
126
130
|
|
|
127
131
|
# Assign a shared gid to wick Line2D (vertical 2-point lines) on the same axis
|
|
128
132
|
wick_lines = []
|
|
@@ -132,7 +136,11 @@ class CandlestickPlot(MaidrPlot):
|
|
|
132
136
|
if xydata is None:
|
|
133
137
|
continue
|
|
134
138
|
xy_arr = np.asarray(xydata)
|
|
135
|
-
if
|
|
139
|
+
if (
|
|
140
|
+
xy_arr.ndim == 2
|
|
141
|
+
and xy_arr.shape[0] == 2
|
|
142
|
+
and xy_arr.shape[1] >= 2
|
|
143
|
+
):
|
|
136
144
|
x0 = float(xy_arr[0, 0])
|
|
137
145
|
x1 = float(xy_arr[1, 0])
|
|
138
146
|
if abs(x0 - x1) < 1e-10:
|
|
@@ -176,7 +184,12 @@ class CandlestickPlot(MaidrPlot):
|
|
|
176
184
|
- Legacy path: return a dict with body and shared wick selectors (no open/close keys)
|
|
177
185
|
"""
|
|
178
186
|
# Modern path: build structured selectors using separate gids
|
|
179
|
-
if
|
|
187
|
+
if (
|
|
188
|
+
self._maidr_body_collection
|
|
189
|
+
and self._maidr_wick_collection
|
|
190
|
+
and self._maidr_body_gid
|
|
191
|
+
and self._maidr_wick_gid
|
|
192
|
+
):
|
|
180
193
|
# Determine candle count N
|
|
181
194
|
N = None
|
|
182
195
|
if self._maidr_original_data is not None:
|
|
@@ -115,7 +115,9 @@ class MultiLinePlot(MaidrPlot, LineExtractorMixin):
|
|
|
115
115
|
line_type = label
|
|
116
116
|
|
|
117
117
|
# Use the new method to extract data with categorical labels
|
|
118
|
-
line_coords = LineExtractorMixin.extract_line_data_with_categorical_labels(
|
|
118
|
+
line_coords = LineExtractorMixin.extract_line_data_with_categorical_labels(
|
|
119
|
+
self.ax, line
|
|
120
|
+
)
|
|
119
121
|
if line_coords is None:
|
|
120
122
|
continue
|
|
121
123
|
|
|
@@ -105,7 +105,10 @@ class MplfinanceLinePlot(MaidrPlot, LineExtractorMixin):
|
|
|
105
105
|
continue
|
|
106
106
|
|
|
107
107
|
# Use datetime converter for enhanced data extraction
|
|
108
|
-
datetime_converter =
|
|
108
|
+
datetime_converter = (
|
|
109
|
+
getattr(line, "_maidr_datetime_converter", None)
|
|
110
|
+
or self._maidr_datetime_converter
|
|
111
|
+
)
|
|
109
112
|
if datetime_converter is not None:
|
|
110
113
|
# Convert x-coordinate (matplotlib index) to formatted datetime
|
|
111
114
|
x_value = datetime_converter.get_formatted_datetime(int(round(x)))
|