|
15 | 15 | "source": [
|
16 | 16 | "# Autostart\n",
|
17 | 17 | "\n",
|
18 |
| - "Autostart is a feature implemented using the [`pluggy`](https://pluggy.readthedocs.io/en/stable/index.html#pluggy) plugin system. Plugins registered under the entry point `ipylab-python-backend` will be called once when `ipylab` is activated. Normally activation of `ipylab` will occur when Jupyterlab is started (assuming `ipylab` is installed and enabled). \n", |
| 18 | + "Autostart is a feature implemented using the [`pluggy`](https://pluggy.readthedocs.io/en/stable/index.html#pluggy) plugin system. The code associated with the entry point `ipylab-python-backend` will be called (imported) when `ipylab` is activated. `ipylab` will activate when Jupyterlab is started (provided `ipylab` is installed and enabled). \n", |
19 | 19 | "\n",
|
20 |
| - "There are no limitations to what can be done. But some possibilities include:\n", |
21 |
| - "* Launch an app to run in its own thread;\n", |
| 20 | + "There are no limitations to what can be done. But it is recommended to import on demand to minimise the time required to launch. Some possibilities include:\n", |
22 | 21 | "* Create and register custom commands;\n",
|
23 | 22 | "* Create launchers;\n",
|
24 |
| - "* Create new notebooks;\n", |
| 23 | + "* Modify the appearance of Jupyterlab.\n", |
25 | 24 | "\n",
|
26 | 25 | "## Entry points\n",
|
27 | 26 | "\n",
|
|
32 | 31 | "my-plugins-name = \"my_module.ipylab_plugin:ipylab_plugin\"\n",
|
33 | 32 | "```\n",
|
34 | 33 | "\n",
|
35 |
| - "In `my_module.autostart.py` define the plugins.\n", |
| 34 | + "In `my_module.autostart.py` write code that will be run once.\n", |
36 | 35 | "\n",
|
37 | 36 | "Example:\n",
|
38 | 37 | "\n",
|
39 | 38 | "```python\n",
|
40 | 39 | "# @ipylab_plugin.py\n",
|
41 | 40 | "\n",
|
42 |
| - "import ipylab\n", |
43 |
| - "\n", |
44 |
| - "app = ipylab.JupyterFrontEnd()\n", |
| 41 | + "import asyncio\n", |
45 | 42 | "\n",
|
46 |
| - "def create_app():\n", |
47 |
| - " Add code here to create the app\n", |
| 43 | + "async def startup():\n", |
| 44 | + " import ipylab\n", |
| 45 | + " \n", |
| 46 | + " app = ipylab.JupyterFrontEnd() \n", |
| 47 | + " await app.read_wait()\n", |
| 48 | + " #Do everything to startup\n", |
48 | 49 | "\n",
|
49 |
| - "class IpylabPlugins:\n", |
50 |
| - " @ipylab.hookspecs.hookimpl()\n", |
51 |
| - " def run_once_at_startup(self):\n", |
52 |
| - " # May want to use a launcher instead\n", |
53 |
| - " app.newSession(path=\"my app\", code=create_app)\n", |
54 |
| - " # Do more stuff ...\n", |
55 |
| - "ipylab_plugin = IpylabPlugins()\n", |
| 50 | + "ipylab_plugin = None # Provide an empty object with the expected name.\n", |
| 51 | + "asyncio.create_task(startup())\n", |
56 | 52 | "```"
|
57 | 53 | ]
|
58 | 54 | },
|
59 | 55 | {
|
60 | 56 | "cell_type": "markdown",
|
61 | 57 | "metadata": {},
|
62 | 58 | "source": [
|
63 |
| - "## Example launching a small app" |
| 59 | + "## Example creating a launcher" |
64 | 60 | ]
|
65 | 61 | },
|
66 | 62 | {
|
|
71 | 67 | "source": [
|
72 | 68 | "# @my_module.autostart.py\n",
|
73 | 69 | "\n",
|
| 70 | + "import asyncio\n", |
| 71 | + "\n", |
74 | 72 | "import ipylab\n",
|
75 | 73 | "\n",
|
76 | 74 | "app = ipylab.JupyterFrontEnd()\n",
|
77 | 75 | "\n",
|
| 76 | + "n = 0\n", |
| 77 | + "\n", |
78 | 78 | "\n",
|
79 | 79 | "async def create_app():\n",
|
80 |
| - " # Ensure this function provides all the imports.\n", |
| 80 | + " # The code in this function is called in the new kernel (session).\n", |
| 81 | + " # Ensure imports are performed inside the function.\n", |
81 | 82 | " global ma\n",
|
82 | 83 | " import ipywidgets as ipw\n",
|
83 | 84 | "\n",
|
84 | 85 | " import ipylab\n",
|
85 | 86 | "\n",
|
86 |
| - " # app = ipylab.JupyterFrontEnd()\n", |
87 |
| - " # await app.wait_ready()\n", |
88 | 87 | " ma = ipylab.MainArea(name=\"My demo app\")\n",
|
| 88 | + " ma.content.title.label = \"Simple app\"\n", |
| 89 | + " ma.content.title.caption = ma.kernelId\n", |
| 90 | + " await ma.load()\n", |
89 | 91 | " console_button = ipw.Button(description=\"Toggle console\")\n",
|
| 92 | + " error_button = ipw.Button(\n", |
| 93 | + " description=\"Do an error\",\n", |
| 94 | + " tooltip=\"An error dialog will pop up when this is clicked.\\n\"\n", |
| 95 | + " \"The dialog demonstrates the use of the `on_frontend_error` plugin.\",\n", |
| 96 | + " )\n", |
90 | 97 | " console_button.on_click(\n",
|
91 | 98 | " lambda b: ma.load_console() if ma.console_status == \"unloaded\" else ma.unload_console()\n",
|
92 | 99 | " )\n",
|
| 100 | + " error_button.on_click(lambda b: ma.execute_command(\"Not a command\"))\n", |
93 | 101 | " ma.content.children = [\n",
|
94 | 102 | " ipw.HTML(f\"<h3>My simple app</h3> Welcome to my app.<br> kernel id: {ma.kernelId}\"),\n",
|
95 |
| - " console_button,\n", |
| 103 | + " ipw.HBox([console_button, error_button]),\n", |
96 | 104 | " ]\n",
|
97 |
| - " ma.content.label = \"This is my app\"\n", |
98 |
| - " ma.load()\n", |
99 |
| - " print(\"Finished creating my app\")\n", |
100 | 105 | "\n",
|
| 106 | + " # Shutdown when MainArea is unloaded.\n", |
| 107 | + " def on_status_change(change):\n", |
| 108 | + " if change[\"new\"] == \"unloaded\":\n", |
| 109 | + " ma.app.shutdownKernel()\n", |
101 | 110 | "\n",
|
102 |
| - "class MyPlugins:\n", |
103 |
| - " @ipylab.hookspecs.hookimpl()\n", |
104 |
| - " def run_once_at_startup(self):\n", |
105 |
| - " app.newSession(path=\"my app\", code=create_app)\n", |
| 111 | + " ma.observe(on_status_change, \"status\")\n", |
106 | 112 | "\n",
|
| 113 | + " class IpylabPlugins:\n", |
| 114 | + " # Define plugins (see IpylabHookspec for available hooks)\n", |
| 115 | + " @ipylab.hookimpl\n", |
| 116 | + " def on_frontend_error(self, obj, error, content):\n", |
| 117 | + " ma.app.dialog.show_error_message(\"Error\", str(error))\n", |
107 | 118 | "\n",
|
108 |
| - "ipylab_plugin = MyPlugins()" |
109 |
| - ] |
110 |
| - }, |
111 |
| - { |
112 |
| - "cell_type": "markdown", |
113 |
| - "metadata": {}, |
114 |
| - "source": [ |
115 |
| - "### Launch the app manually\n", |
| 119 | + " # Register plugin for this kernel.\n", |
| 120 | + " ipylab.hookspecs.pm.register(IpylabPlugins())\n", |
116 | 121 | "\n",
|
117 |
| - "We can 'launch' the app in a new kernel." |
| 122 | + "\n", |
| 123 | + "def start_my_app(cwd):\n", |
| 124 | + " global n\n", |
| 125 | + " n += 1\n", |
| 126 | + " app.newSession(path=f\"my app {n}\", code=create_app)\n", |
| 127 | + "\n", |
| 128 | + "\n", |
| 129 | + "async def register_commands():\n", |
| 130 | + " await app.command.addPythonCommand(\n", |
| 131 | + " \"start_my_app\",\n", |
| 132 | + " execute=start_my_app,\n", |
| 133 | + " label=\"Start Custom App\",\n", |
| 134 | + " icon_class=\"jp-PythonIcon\",\n", |
| 135 | + " )\n", |
| 136 | + " await app.launcher.add_item(\"start_my_app\", \"Ipylab\")\n", |
| 137 | + " return \"done\"\n", |
| 138 | + "\n", |
| 139 | + "\n", |
| 140 | + "ipylab_plugin = None\n", |
| 141 | + "t = asyncio.create_task(register_commands())" |
118 | 142 | ]
|
119 | 143 | },
|
120 | 144 | {
|
|
123 | 147 | "metadata": {},
|
124 | 148 | "outputs": [],
|
125 | 149 | "source": [
|
126 |
| - "app.newSession(path=\"my app\", code=create_app)" |
127 |
| - ] |
128 |
| - }, |
129 |
| - { |
130 |
| - "cell_type": "markdown", |
131 |
| - "metadata": {}, |
132 |
| - "source": [ |
133 |
| - "### Auto Launch app\n", |
134 |
| - "Simulate code launch in the as it happens in `Ipylab backend`" |
| 150 | + "t.result()" |
135 | 151 | ]
|
136 | 152 | },
|
137 | 153 | {
|
|
140 | 156 | "metadata": {},
|
141 | 157 | "outputs": [],
|
142 | 158 | "source": [
|
143 |
| - "# Register plugin (normally via the entry point `ipylab-python-backend`)\n", |
144 |
| - "ipylab.hookspecs.pm.register(ipylab_plugin)\n", |
145 |
| - "\n", |
146 |
| - "# Called when Ipylab is activated and Ipylab backend launches\n", |
147 |
| - "app._init_python_backend()" |
| 159 | + "# There is a new launcher called 'Start custom app'\n", |
| 160 | + "t = app.execute_command(\"launcher:create\")" |
148 | 161 | ]
|
149 | 162 | }
|
150 | 163 | ],
|
|
0 commit comments