diff --git a/examples/requirements.txt b/examples/requirements.txt new file mode 100644 index 0000000..9a35dcb --- /dev/null +++ b/examples/requirements.txt @@ -0,0 +1,5 @@ +trame>3 +trame-vuetify +trame-components +trame-matplotlib +numpy \ No newline at end of file diff --git a/examples/vue2.py b/examples/vue2.py new file mode 100644 index 0000000..b89e8d3 --- /dev/null +++ b/examples/vue2.py @@ -0,0 +1,183 @@ +import numpy as np +import matplotlib.pyplot as plt + +from trame.app import get_server +from trame.ui.vuetify import SinglePageLayout +from trame.widgets import vuetify, trame, matplotlib + +# ----------------------------------------------------------------------------- +# Trame setup +# ----------------------------------------------------------------------------- + +server = get_server(client_type="vue2") +state, ctrl = server.state, server.controller + +# ----------------------------------------------------------------------------- +# Chart examples from: +# - http://jakevdp.github.io/blog/2013/12/19/a-d3-viewer-for-matplotlib/ +# ----------------------------------------------------------------------------- + + +def figure_size(): + if state.figure_size is None: + return {} + + dpi = state.figure_size.get("dpi") + rect = state.figure_size.get("size") + w_inch = rect.get("width") / dpi / 2 + h_inch = rect.get("height") / dpi / 2 + + return { + "figsize": (w_inch, h_inch), + "dpi": dpi, + } + + +def FirstDemo(): + plt.close("all") + fig, ax = plt.subplots(**figure_size()) + np.random.seed(0) + ax.plot( + np.random.normal(size=100), np.random.normal(size=100), "or", ms=10, alpha=0.3 + ) + ax.plot( + np.random.normal(size=100), np.random.normal(size=100), "ob", ms=20, alpha=0.1 + ) + + ax.set_xlabel("this is x") + ax.set_ylabel("this is y") + ax.set_title("Matplotlib Plot Rendered in D3!", size=14) + ax.grid(color="lightgray", alpha=0.7) + + return fig + + +# ----------------------------------------------------------------------------- + + +def MultiLines(): + plt.close("all") + fig, ax = plt.subplots(**figure_size()) + x = np.linspace(0, 10, 1000) + for offset in np.linspace(0, 3, 7): + ax.plot(x, 0.9 * np.sin(x - offset), lw=5, alpha=0.4) + ax.set_ylim(-1.2, 1.0) + ax.text(5, -1.1, "Here are some curves", size=18) + ax.grid(color="lightgray", alpha=0.7) + + return fig + + +# ----------------------------------------------------------------------------- + + +def DotsandPoints(): + plt.close("all") + fig, ax = plt.subplots(**figure_size()) + ax.plot( + np.random.rand(20), + "-o", + alpha=0.5, + color="black", + linewidth=5, + markerfacecolor="green", + markeredgecolor="lightgreen", + markersize=20, + markeredgewidth=10, + ) + ax.grid(True, color="#EEEEEE", linestyle="solid") + ax.set_xlim(-2, 22) + ax.set_ylim(-0.1, 1.1) + + return fig + + +# ----------------------------------------------------------------------------- + + +def MovingWindowAverage(): + np.random.seed(0) + t = np.linspace(0, 10, 300) + x = np.sin(t) + dx = np.random.normal(0, 0.3, 300) + + kernel = np.ones(25) / 25.0 + x_smooth = np.convolve(x + dx, kernel, mode="same") + plt.close("all") + fig, ax = plt.subplots(**figure_size()) + ax.plot(t, x + dx, linestyle="", marker="o", color="black", markersize=3, alpha=0.3) + ax.plot(t, x_smooth, "-k", lw=3) + ax.plot(t, x, "--", lw=3, color="blue") + + return fig + + +# ----------------------------------------------------------------------------- + + +def Subplots(): + plt.close("all") + fig = plt.figure(**figure_size()) + fig.subplots_adjust(hspace=0.3) + + np.random.seed(0) + + for i in range(1, 5): + ax = fig.add_subplot(2, 2, i) + color = np.random.random(3) + ax.plot(np.random.random(30), lw=2, c=color) + ax.set_title("RGB = ({0:.2f}, {1:.2f}, {2:.2f})".format(*color), size=14) + ax.grid(color="lightgray", alpha=0.7) + + return fig + + +# ----------------------------------------------------------------------------- +# Callbacks +# ----------------------------------------------------------------------------- + + +@state.change("active_figure", "figure_size") +def update_chart(active_figure, **kwargs): + ctrl.update_figure(globals()[active_figure]()) + + +# ----------------------------------------------------------------------------- +# UI +# ----------------------------------------------------------------------------- + +state.trame__title = "Matplotly" + +with SinglePageLayout(server) as layout: + layout.title.set_text("trame ❤️ matplotlib") + + with layout.toolbar: + vuetify.VSpacer() + vuetify.VSelect( + v_model=("active_figure", "FirstDemo"), + items=( + "figures", + [ + {"text": "First Demo", "value": "FirstDemo"}, + {"text": "Multi Lines", "value": "MultiLines"}, + {"text": "Dots and Points", "value": "DotsandPoints"}, + {"text": "Moving Window Average", "value": "MovingWindowAverage"}, + {"text": "Subplots", "value": "Subplots"}, + ], + ), + hide_details=True, + dense=True, + ) + + with layout.content: + with vuetify.VContainer(fluid=True, classes="fill-height pa-0 ma-0"): + with trame.SizeObserver("figure_size"): + html_figure = matplotlib.Figure(style="position: absolute") + ctrl.update_figure = html_figure.update + +# ----------------------------------------------------------------------------- +# Main +# ----------------------------------------------------------------------------- + +if __name__ == "__main__": + server.start() diff --git a/examples/vue3.py b/examples/vue3.py new file mode 100644 index 0000000..36946ae --- /dev/null +++ b/examples/vue3.py @@ -0,0 +1,183 @@ +import numpy as np +import matplotlib.pyplot as plt + +from trame.app import get_server +from trame.ui.vuetify3 import SinglePageLayout +from trame.widgets import vuetify3 as vuetify, trame, matplotlib + +# ----------------------------------------------------------------------------- +# Trame setup +# ----------------------------------------------------------------------------- + +server = get_server(client_type="vue3") +state, ctrl = server.state, server.controller + +# ----------------------------------------------------------------------------- +# Chart examples from: +# - http://jakevdp.github.io/blog/2013/12/19/a-d3-viewer-for-matplotlib/ +# ----------------------------------------------------------------------------- + + +def figure_size(): + if state.figure_size is None: + return {} + + dpi = state.figure_size.get("dpi") + rect = state.figure_size.get("size") + w_inch = rect.get("width") / dpi / 2 + h_inch = rect.get("height") / dpi / 2 + + return { + "figsize": (w_inch, h_inch), + "dpi": dpi, + } + + +def FirstDemo(): + plt.close("all") + fig, ax = plt.subplots(**figure_size()) + np.random.seed(0) + ax.plot( + np.random.normal(size=100), np.random.normal(size=100), "or", ms=10, alpha=0.3 + ) + ax.plot( + np.random.normal(size=100), np.random.normal(size=100), "ob", ms=20, alpha=0.1 + ) + + ax.set_xlabel("this is x") + ax.set_ylabel("this is y") + ax.set_title("Matplotlib Plot Rendered in D3!", size=14) + ax.grid(color="lightgray", alpha=0.7) + + return fig + + +# ----------------------------------------------------------------------------- + + +def MultiLines(): + plt.close("all") + fig, ax = plt.subplots(**figure_size()) + x = np.linspace(0, 10, 1000) + for offset in np.linspace(0, 3, 7): + ax.plot(x, 0.9 * np.sin(x - offset), lw=5, alpha=0.4) + ax.set_ylim(-1.2, 1.0) + ax.text(5, -1.1, "Here are some curves", size=18) + ax.grid(color="lightgray", alpha=0.7) + + return fig + + +# ----------------------------------------------------------------------------- + + +def DotsandPoints(): + plt.close("all") + fig, ax = plt.subplots(**figure_size()) + ax.plot( + np.random.rand(20), + "-o", + alpha=0.5, + color="black", + linewidth=5, + markerfacecolor="green", + markeredgecolor="lightgreen", + markersize=20, + markeredgewidth=10, + ) + ax.grid(True, color="#EEEEEE", linestyle="solid") + ax.set_xlim(-2, 22) + ax.set_ylim(-0.1, 1.1) + + return fig + + +# ----------------------------------------------------------------------------- + + +def MovingWindowAverage(): + np.random.seed(0) + t = np.linspace(0, 10, 300) + x = np.sin(t) + dx = np.random.normal(0, 0.3, 300) + + kernel = np.ones(25) / 25.0 + x_smooth = np.convolve(x + dx, kernel, mode="same") + plt.close("all") + fig, ax = plt.subplots(**figure_size()) + ax.plot(t, x + dx, linestyle="", marker="o", color="black", markersize=3, alpha=0.3) + ax.plot(t, x_smooth, "-k", lw=3) + ax.plot(t, x, "--", lw=3, color="blue") + + return fig + + +# ----------------------------------------------------------------------------- + + +def Subplots(): + plt.close("all") + fig = plt.figure(**figure_size()) + fig.subplots_adjust(hspace=0.3) + + np.random.seed(0) + + for i in range(1, 5): + ax = fig.add_subplot(2, 2, i) + color = np.random.random(3) + ax.plot(np.random.random(30), lw=2, c=color) + ax.set_title("RGB = ({0:.2f}, {1:.2f}, {2:.2f})".format(*color), size=14) + ax.grid(color="lightgray", alpha=0.7) + + return fig + + +# ----------------------------------------------------------------------------- +# Callbacks +# ----------------------------------------------------------------------------- + + +@state.change("active_figure", "figure_size") +def update_chart(active_figure, **kwargs): + ctrl.update_figure(globals()[active_figure]()) + + +# ----------------------------------------------------------------------------- +# UI +# ----------------------------------------------------------------------------- + +state.trame__title = "Matplotly" + +with SinglePageLayout(server) as layout: + layout.title.set_text("trame ❤️ matplotlib") + + with layout.toolbar: + vuetify.VSpacer() + vuetify.VSelect( + v_model=("active_figure", "FirstDemo"), + items=( + "figures", + [ + {"title": "First Demo", "value": "FirstDemo"}, + {"title": "Multi Lines", "value": "MultiLines"}, + {"title": "Dots and Points", "value": "DotsandPoints"}, + {"title": "Moving Window Average", "value": "MovingWindowAverage"}, + {"title": "Subplots", "value": "Subplots"}, + ], + ), + hide_details=True, + dense="compact", + ) + + with layout.content: + with vuetify.VContainer(fluid=True, classes="fill-height pa-0 ma-0"): + with trame.SizeObserver("figure_size"): + html_figure = matplotlib.Figure(style="position: absolute") + ctrl.update_figure = html_figure.update + +# ----------------------------------------------------------------------------- +# Main +# ----------------------------------------------------------------------------- + +if __name__ == "__main__": + server.start() diff --git a/setup.cfg b/setup.cfg index 784ba68..485913e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,6 +28,7 @@ packages = find: include_package_data = True install_requires = trame-client + mpld3 [semantic_release] version_pattern = setup.cfg:version = (\d+\.\d+\.\d+) diff --git a/vue-components/package-lock.json b/vue-components/package-lock.json index 1281fa7..3363d96 100644 --- a/vue-components/package-lock.json +++ b/vue-components/package-lock.json @@ -3604,9 +3604,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001339", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001339.tgz", - "integrity": "sha512-Es8PiVqCe+uXdms0Gu5xP5PF2bxLR7OBp3wUzUnuO7OHzhOfCyg3hdiGWVPVxhiuniOzng+hTc1u3fEQ0TlkSQ==", + "version": "1.0.30001599", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001599.tgz", + "integrity": "sha512-LRAQHZ4yT1+f9LemSMeqdMpMxZcc4RMWdj4tiFe3G8tNkWK+E58g+/tzotb5cU6TbcVJLr4fySiAW7XmxQvZQA==", "dev": true, "funding": [ { @@ -3616,6 +3616,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ] }, @@ -13882,9 +13886,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001339", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001339.tgz", - "integrity": "sha512-Es8PiVqCe+uXdms0Gu5xP5PF2bxLR7OBp3wUzUnuO7OHzhOfCyg3hdiGWVPVxhiuniOzng+hTc1u3fEQ0TlkSQ==", + "version": "1.0.30001599", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001599.tgz", + "integrity": "sha512-LRAQHZ4yT1+f9LemSMeqdMpMxZcc4RMWdj4tiFe3G8tNkWK+E58g+/tzotb5cU6TbcVJLr4fySiAW7XmxQvZQA==", "dev": true }, "case-sensitive-paths-webpack-plugin": { diff --git a/vue-components/src/components/VueMatplotlib/script.js b/vue-components/src/components/VueMatplotlib.js similarity index 94% rename from vue-components/src/components/VueMatplotlib/script.js rename to vue-components/src/components/VueMatplotlib.js index 5656571..b5b76e4 100644 --- a/vue-components/src/components/VueMatplotlib/script.js +++ b/vue-components/src/components/VueMatplotlib.js @@ -33,4 +33,5 @@ export default { beforeDestroy() { mpld3.remove_figure(this.name); }, + template: `
`, }; diff --git a/vue-components/src/components/VueMatplotlib/index.vue b/vue-components/src/components/VueMatplotlib/index.vue deleted file mode 100644 index 420f931..0000000 --- a/vue-components/src/components/VueMatplotlib/index.vue +++ /dev/null @@ -1,2 +0,0 @@ -