diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 2bcd70e..0000000 --- a/.flake8 +++ /dev/null @@ -1,2 +0,0 @@ -[flake8] -max-line-length = 88 diff --git a/example/py-shiny/example_pyshiny_reactive_scatter.py b/example/py-shiny/example_pyshiny_reactive_scatter.py new file mode 100644 index 0000000..bf7307e --- /dev/null +++ b/example/py-shiny/example_pyshiny_reactive_scatter.py @@ -0,0 +1,52 @@ +import matplotlib.pyplot as plt +import seaborn as sns +from shiny import App, ui + +from maidr.widget.shiny import render_maidr + +# Load the dataset +iris = sns.load_dataset("iris") + +# Define the UI components for the Shiny application +app_ui = ui.page_fluid( + ui.row( + ui.column( + 3, + ui.input_select( + "x_var", + "Select X variable:", + choices=iris.select_dtypes(include=["float64"]).columns.tolist(), + selected="sepal_length", + ), + ui.input_select( + "y_var", + "Select Y variable:", + choices=iris.select_dtypes(include=["float64"]).columns.tolist(), + selected="sepal_width", + ), + ), + ui.column(9, ui.output_ui("create_reactivebarplot")), + ) +) + + +# Define the server +def server(input, output, session): + @render_maidr + def create_reactivebarplot(): + fig, ax = plt.subplots(figsize=(10, 6)) + s_plot = sns.scatterplot( + data=iris, x=input.x_var(), y=input.y_var(), hue="species", ax=ax + ) + ax.set_title(f"Iris {input.y_var()} vs {input.x_var()}") + ax.set_xlabel(input.x_var().replace("_", " ").title()) + ax.set_ylabel(input.y_var().replace("_", " ").title()) + return s_plot + + +# Create the app +app = App(app_ui, server) + +# Run the app +if __name__ == "__main__": + app.run() diff --git a/maidr/__init__.py b/maidr/__init__.py index 201ecd7..2f88036 100644 --- a/maidr/__init__.py +++ b/maidr/__init__.py @@ -12,10 +12,11 @@ lineplot, scatterplot, ) -from .api import close, save_html, show, stacked +from .api import close, render, save_html, show, stacked __all__ = [ "close", + "render", "save_html", "show", "stacked", diff --git a/maidr/api.py b/maidr/api.py index de423c6..192f140 100644 --- a/maidr/api.py +++ b/maidr/api.py @@ -2,6 +2,7 @@ from typing import Literal, Any +from htmltools import Tag from matplotlib.axes import Axes from matplotlib.container import BarContainer @@ -10,6 +11,14 @@ from maidr.core.figure_manager import FigureManager +def render( + plot: Any, *, lib_prefix: str | None = "lib", include_version: bool = True +) -> Tag: + ax = FigureManager.get_axes(plot) + maidr = FigureManager.get_maidr(ax.get_figure()) + return maidr.render() + + def show(plot: Any, renderer: Literal["auto", "ipython", "browser"] = "auto") -> object: ax = FigureManager.get_axes(plot) maidr = FigureManager.get_maidr(ax.get_figure()) diff --git a/maidr/core/maidr.py b/maidr/core/maidr.py index 649a368..2abe874 100644 --- a/maidr/core/maidr.py +++ b/maidr/core/maidr.py @@ -6,14 +6,13 @@ import json import uuid -from htmltools import HTML, HTMLDocument, RenderedHTML, tags, Tag +from htmltools import HTML, HTMLDocument, Tag, tags from lxml import etree from matplotlib.figure import Figure from maidr.core.context_manager import HighlightContextManager from maidr.core.plot import MaidrPlot -from maidr.utils.environment import Environment class Maidr: @@ -52,21 +51,9 @@ def plots(self) -> list[MaidrPlot]: """Return the list of plots extracted from the ``fig``.""" return self._plots - def render( - self, *, lib_prefix: str | None = "lib", include_version: bool = True - ) -> RenderedHTML: - """ - Render the document. - - Parameters - ---------- - lib_prefix : str, default="lib" - A prefix to add to relative paths to dependency files. - include_version : bool, default=True - Whether to include the version number in the dependency's folder name. - """ - html = self._create_html_doc() - return html.render(lib_prefix=lib_prefix, include_version=include_version) + def render(self) -> Tag: + """Return the maidr plot inside an iframe.""" + return self._create_html_tag() def save_html( self, file: str, *, lib_dir: str | None = "lib", include_version: bool = True @@ -79,7 +66,8 @@ def save_html( file : str The file to save to. lib_dir : str, default="lib" - The directory to save the dependencies to (relative to the file's directory). + The directory to save the dependencies to + (relative to the file's directory). include_version : bool, default=True Whether to include the version number in the dependency folder name. """ @@ -163,7 +151,6 @@ def _unique_id() -> str: @staticmethod def _inject_plot(plot: HTML, maidr: str) -> Tag: """Embed the plot and associated MAIDR scripts into the HTML structure.""" - base_html = tags.html( tags.head( tags.meta(charset="UTF-8"), @@ -183,20 +170,20 @@ def _inject_plot(plot: HTML, maidr: str) -> Tag: tags.script(maidr), ) - if Environment.is_interactive_shell(): - # If running in an interactive environment (e.g., Jupyter Notebook), - # display the HTML content using an iframe to ensure proper rendering - # and interactivity. The iframe's height is dynamically adjusted - base_html = tags.iframe( - srcdoc=str(base_html.get_html_string()), - width="100%", - height="100%", - scrolling="auto", - style="background-color: #fff", - frameBorder=0, - onload=""" - this.style.height = this.contentWindow.document.body.scrollHeight + 100 + 'px'; - """, - ) + # If running in an interactive environment (e.g., Jupyter Notebook), + # display the HTML content using an iframe to ensure proper rendering + # and interactivity. The iframe's height is dynamically adjusted + base_html = tags.iframe( + srcdoc=str(base_html.get_html_string()), + width="100%", + height="100%", + scrolling="auto", + style="background-color: #fff", + frameBorder=0, + onload=""" + this.style.height = this.contentWindow.document.body.scrollHeight + + 100 + 'px'; + """, + ) return base_html diff --git a/maidr/widget/shiny.py b/maidr/widget/shiny.py new file mode 100644 index 0000000..0a49d56 --- /dev/null +++ b/maidr/widget/shiny.py @@ -0,0 +1,29 @@ +from __future__ import annotations + +from shiny.render import ui +from shiny.types import Jsonifiable + +import maidr + + +class render_maidr(ui): + """ + A custom UI rendering class for Maidr objects in Shiny applications. + + This class extends the Shiny UI rendering functionality to handle Maidr objects. + + Methods + ------- + render() + Asynchronously renders the Maidr object. + """ + + async def render(self) -> Jsonifiable: + """Return maidr rendered object for a given plot.""" + initial_value = await self.fn() + if initial_value is None: + return None + + maidr_rendered = maidr.render(initial_value) + transformed = await self.transform(maidr_rendered) + return transformed diff --git a/poetry.lock b/poetry.lock index 6d12e3e..c0ff7d8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -248,33 +248,36 @@ lxml = ["lxml"] [[package]] name = "black" -version = "24.3.0" +version = "23.3.0" description = "The uncompromising code formatter." optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "black-24.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395"}, - {file = "black-24.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995"}, - {file = "black-24.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7"}, - {file = "black-24.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0"}, - {file = "black-24.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9"}, - {file = "black-24.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597"}, - {file = "black-24.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d"}, - {file = "black-24.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5"}, - {file = "black-24.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f"}, - {file = "black-24.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11"}, - {file = "black-24.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4"}, - {file = "black-24.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5"}, - {file = "black-24.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837"}, - {file = "black-24.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd"}, - {file = "black-24.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213"}, - {file = "black-24.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959"}, - {file = "black-24.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb"}, - {file = "black-24.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7"}, - {file = "black-24.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7"}, - {file = "black-24.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f"}, - {file = "black-24.3.0-py3-none-any.whl", hash = "sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93"}, - {file = "black-24.3.0.tar.gz", hash = "sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f"}, + {file = "black-23.3.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915"}, + {file = "black-23.3.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9"}, + {file = "black-23.3.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2"}, + {file = "black-23.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c"}, + {file = "black-23.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c"}, + {file = "black-23.3.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6"}, + {file = "black-23.3.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b"}, + {file = "black-23.3.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d"}, + {file = "black-23.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70"}, + {file = "black-23.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326"}, + {file = "black-23.3.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b"}, + {file = "black-23.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2"}, + {file = "black-23.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925"}, + {file = "black-23.3.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27"}, + {file = "black-23.3.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331"}, + {file = "black-23.3.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5"}, + {file = "black-23.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961"}, + {file = "black-23.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8"}, + {file = "black-23.3.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30"}, + {file = "black-23.3.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3"}, + {file = "black-23.3.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266"}, + {file = "black-23.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab"}, + {file = "black-23.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb"}, + {file = "black-23.3.0-py3-none-any.whl", hash = "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4"}, + {file = "black-23.3.0.tar.gz", hash = "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940"}, ] [package.dependencies] @@ -284,11 +287,11 @@ packaging = ">=22.0" pathspec = ">=0.9.0" platformdirs = ">=2" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} [package.extras] colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +d = ["aiohttp (>=3.7.4)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] @@ -2249,6 +2252,7 @@ optional = false python-versions = ">=3.9" files = [ {file = "pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce"}, + {file = "pandas-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7adfc142dac335d8c1e0dcbd37eb8617eac386596eb9e1a1b77791cf2498238"}, {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08"}, {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0"}, {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51"}, @@ -2269,6 +2273,7 @@ files = [ {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"}, {file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"}, {file = "pandas-2.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2"}, + {file = "pandas-2.2.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9057e6aa78a584bc93a13f0a9bf7e753a5e9770a30b4d758b8d5f2a62a9433cd"}, {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863"}, {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921"}, {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a"}, @@ -4160,4 +4165,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" python-versions = ">=3.9" -content-hash = "61be8a252ba02a76cedcedf1f6fa73260800c7ad4b279a67732a9b366b39399a" +content-hash = "f57fde5ce3ad39ea4c74c84ab03a4127563aa856d4596c919aeb6359d6c99d16" diff --git a/pyproject.toml b/pyproject.toml index da84e44..6fd5ead 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,9 +43,9 @@ lxml = ">=5.1.0" htmltools = ">=0.5" jupyter = "^1.0.0" wrapt = "^1.16.0" +black = "23.3.0" [tool.poetry.group.dev.dependencies] -black = "24.3.0" sphinx = "^7.0.1" pre-commit = "^3.3.2" pytest = "^7.3.2"