{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Customize Trame toolbar {#customize_trame_toolbar_example}\n\nBring more of the power of trame to the jupyter view.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import asyncio\n\nimport pyvista as pv\nfrom pyvista.trame.ui.vuetify3 import button, divider, select, slider, text_field" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let\\'s first create the menu items we want to add to the trame\\'s\ntoolbar. Here we want a \\\"play\\\" button that will be later connected to\na slider through the `button_play` function. The slider itself will\nrepresent the \\\"resolution\\\" of the model we will render, a text field\nwhere the value of the \\\"resolution\\\" will be displayed. We will also\nadd a dropdown menu to toggle the visibility of the model. The dividers\nare the same as already used to divide and organize the toolbar.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def custom_tools():\n divider(vertical=True, classes='mx-1')\n button(\n click=button_play,\n icon='mdi-play',\n tooltip='Play',\n )\n\n slider(\n model=(\"resolution\", 10),\n tooltip=\"Resolution slider\",\n min=3,\n max=20,\n step=1,\n dense=True,\n hide_details=True,\n style=\"width: 300px\",\n classes='my-0 py-0 ml-1 mr-1',\n )\n text_field(\n model=(\"resolution\", 10),\n tooltip=\"Resolution value\",\n readonly=True,\n type=\"number\",\n dense=True,\n hide_details=True,\n style=\"min-width: 40px; width: 60px\",\n classes='my-0 py-0 ml-1 mr-1',\n )\n\n divider(vertical=True, classes='mx-1')\n select(\n model=(\"visibility\", \"Show\"),\n tooltip=\"Toggle visibility\",\n items=['Visibility', [\"Hide\", \"Show\"]],\n hide_details=True,\n dense=True,\n )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The button callback function `button_play` needs to be created before\nstarting the server. This function will toggle the boolean state\nvariable `play` and flush the server, i.e. \\\"force\\\" the server to see\nthe change. We will see more on the state variables in a bit, but we\nneed to create the function here otherwise the server will complain\n`button_play` does not exist.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def button_play():\n state.play = not state.play\n state.flush()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will do a simple rendering of a Cone using [ConeSouce]{.title-ref}.\n\nWhen using the `pl.show` method. The function we created `custom_tools`\nshould be passed as a `jupyter_kwargs` argument under the key\n`add_menu_items`.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "pl = pv.Plotter(notebook=True)\nalgo = pv.ConeSource()\nmesh_actor = pl.add_mesh(algo)\n\nwidget = pl.show(jupyter_kwargs=dict(add_menu_items=custom_tools), return_viewer=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To interact with `trame`\\'s server we need to get the server\\'s state.\n\nWe initialize the `play` variable in the shared state and this will be\ncontrolled by the play button we created. Note that when creating the\n`slider`, the `text_field` and the `select` tools, we passed something\nlike\n`model=(\"variable\", value). This will automatically create the variable \"variable\" with value`value`in the server's shared state, so we do not need to create`state.resolution`or`state.visibility\\`\\`.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "state, ctrl = widget.viewer.server.state, widget.viewer.server.controller\nstate.play = False\nctrl.view_update = widget.viewer.update" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can create the callback functions for our menu items.\n\nThe functions are decorated with a `state.change(\"variable\")`. This\nmeans they will be called when this specific variable has its value\nchanged in the server\\'s shared state. When `resolution` changes, we\nwant to update the resolution of our cone algorithm. When `visibility`\nchanges, we want to toggle the visibility of our cone.\n\nThe `play` variable is a little bit trickier. We want to start something\nlike a timer so that an animation can be set to play. To do that with\n`trame` we need to have an asynchronous function so we can continue to\ndo stuff while the \\\"timer\\\" function is running. The `_play` function\nwill be called when the `play` variable is changed (when we click the\nplay button, through the `button_play` callback). While `state.play` is\n`True` we want to play the animation. We change the `state.resolution`\nvalue, but to really call the `update_resolution` function we need to\n`flush` the server and force it to see the change in the shared\nvariables. When `state.play` changes to `False`, the animation stops.\n\nNote that using `while play: ...` would not work here because it is not\nthe actual state variable, but only an argument value passed to the\ncallback function.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# trame callbacks\n@state.change(\"play\")\nasync def _play(play, **kwargs):\n while state.play:\n state.resolution += 1\n state.flush()\n if state.resolution >= 20:\n state.play = False\n await asyncio.sleep(0.3)\n\n\n@state.change(\"resolution\")\ndef update_resolution(resolution, **kwargs):\n algo.resolution = resolution\n ctrl.view_update()\n\n\n@state.change(\"visibility\")\ndef set_visibility(visibility, **kwargs):\n toggle = {\"Hide\": 0, \"Show\": 1}\n mesh_actor.visibility = toggle[visibility]\n ctrl.view_update()\n\n\nwidget" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.2" } }, "nbformat": 4, "nbformat_minor": 0 }