1
0

section_configuration_deployment.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. from nicegui import ui
  2. from ..windows import bash_window, python_window
  3. from . import doc, run_documentation
  4. doc.title('Configuration & Deployment')
  5. @doc.demo('URLs', '''
  6. You can access the list of all URLs on which the NiceGUI app is available via `app.urls`.
  7. The URLs are not available in `app.on_startup` because the server is not yet running.
  8. Instead, you can access them in a page function or register a callback with `app.urls.on_change`.
  9. ''')
  10. def urls_demo():
  11. from nicegui import app
  12. # @ui.page('/')
  13. # def index():
  14. # for url in app.urls:
  15. # ui.link(url, target=url)
  16. # END OF DEMO
  17. ui.link('https://nicegui.io', target='https://nicegui.io')
  18. doc.intro(run_documentation)
  19. @doc.demo('Native Mode', '''
  20. You can enable native mode for NiceGUI by specifying `native=True` in the `ui.run` function.
  21. To customize the initial window size and display mode, use the `window_size` and `fullscreen` parameters respectively.
  22. Additionally, you can provide extra keyword arguments via `app.native.window_args` and `app.native.start_args`.
  23. Pick any parameter as it is defined by the internally used [pywebview module](https://pywebview.flowrl.com/guide/api.html)
  24. for the `webview.create_window` and `webview.start` functions.
  25. Note that these keyword arguments will take precedence over the parameters defined in `ui.run`.
  26. Additionally, you can change `webview.settings` via `app.native.settings`.
  27. In native mode the `app.native.main_window` object allows you to access the underlying window.
  28. It is an async version of [`Window` from pywebview](https://pywebview.flowrl.com/guide/api.html#window-object).
  29. ''', tab=lambda: ui.label('NiceGUI'))
  30. def native_mode_demo():
  31. from nicegui import app
  32. app.native.window_args['resizable'] = False
  33. app.native.start_args['debug'] = True
  34. app.native.settings['ALLOW_DOWNLOADS'] = True
  35. ui.label('app running in native mode')
  36. # ui.button('enlarge', on_click=lambda: app.native.main_window.resize(1000, 700))
  37. #
  38. # ui.run(native=True, window_size=(400, 300), fullscreen=False)
  39. # END OF DEMO
  40. ui.button('enlarge', on_click=lambda: ui.notify('window will be set to 1000x700 in native mode'))
  41. # Show a helpful workaround until issue is fixed upstream.
  42. # For more info see: https://github.com/r0x0r/pywebview/issues/1078
  43. doc.text('', '''
  44. If webview has trouble finding required libraries, you may get an error relating to "WebView2Loader.dll".
  45. To work around this issue, try moving the DLL file up a directory, e.g.:
  46. * from `.venv/Lib/site-packages/webview/lib/x64/WebView2Loader.dll`
  47. * to `.venv/Lib/site-packages/webview/lib/WebView2Loader.dll`
  48. ''')
  49. @doc.demo('Environment Variables', '''
  50. You can set the following environment variables to configure NiceGUI:
  51. - `MATPLOTLIB` (default: true) can be set to `false` to avoid the potentially costly import of Matplotlib.
  52. This will make `ui.pyplot` and `ui.line_plot` unavailable.
  53. - `NICEGUI_STORAGE_PATH` (default: local ".nicegui") can be set to change the location of the storage files.
  54. - `MARKDOWN_CONTENT_CACHE_SIZE` (default: 1000): The maximum number of Markdown content snippets that are cached in memory.
  55. - `RST_CONTENT_CACHE_SIZE` (default: 1000): The maximum number of ReStructuredText content snippets that are cached in memory.
  56. ''')
  57. def env_var_demo():
  58. from nicegui.elements import markdown
  59. ui.label(f'Markdown content cache size is {markdown.prepare_content.cache_info().maxsize}')
  60. doc.text('Custom Vue Components', '''
  61. You can create custom components by subclassing `ui.element` and implementing a corresponding Vue component.
  62. The ["Custom Vue components" example](https://github.com/zauberzeug/nicegui/tree/main/examples/custom_vue_component)
  63. demonstrates how to create a custom counter component which emits events and receives updates from the server.
  64. The ["Signature pad" example](https://github.com/zauberzeug/nicegui/blob/main/examples/signature_pad)
  65. shows how to define dependencies for a custom component using a `package.json` file.
  66. This allows you to use third-party libraries via NPM in your component.
  67. Last but not least, the ["Node module integration" example](https://github.com/zauberzeug/nicegui/blob/main/examples/node_module_integration)
  68. demonstrates how to create a package.json file and a webpack.config.js file to bundle a custom Vue component with its dependencies.
  69. ''')
  70. doc.text('Server Hosting', '''
  71. To deploy your NiceGUI app on a server, you will need to execute your `main.py` (or whichever file contains your `ui.run(...)`) on your cloud infrastructure.
  72. You can, for example, just install the [NiceGUI python package via pip](https://pypi.org/project/nicegui/) and use systemd or similar service to start the main script.
  73. In most cases, you will set the port to 80 (or 443 if you want to use HTTPS) with the `ui.run` command to make it easily accessible from the outside.
  74. A convenient alternative is the use of our [pre-built multi-arch Docker image](https://hub.docker.com/r/zauberzeug/nicegui) which contains all necessary dependencies.
  75. With this command you can launch the script `main.py` in the current directory on the public port 80:
  76. ''')
  77. @doc.ui
  78. def docker_run():
  79. with bash_window(classes='max-w-lg w-full h-44'):
  80. ui.markdown('''
  81. ```bash
  82. docker run -it --restart always \\
  83. -p 80:8080 \\
  84. -e PUID=$(id -u) \\
  85. -e PGID=$(id -g) \\
  86. -v $(pwd)/:/app/ \\
  87. zauberzeug/nicegui:latest
  88. ```
  89. ''')
  90. doc.text('', '''
  91. The demo assumes `main.py` uses the port 8080 in the `ui.run` command (which is the default).
  92. The `-d` tells docker to run in background and `--restart always` makes sure the container is restarted if the app crashes or the server reboots.
  93. Of course this can also be written in a Docker compose file:
  94. ''')
  95. @doc.ui
  96. def docker_compose():
  97. with python_window('docker-compose.yml', classes='max-w-lg w-full h-60'):
  98. ui.markdown('''
  99. ```yaml
  100. app:
  101. image: zauberzeug/nicegui:latest
  102. restart: always
  103. ports:
  104. - 80:8080
  105. environment:
  106. - PUID=1000 # change this to your user id
  107. - PGID=1000 # change this to your group id
  108. volumes:
  109. - ./:/app/
  110. ```
  111. ''')
  112. doc.text('', '''
  113. There are other handy features in the Docker image like non-root user execution and signal pass-through.
  114. For more details we recommend to have a look at our [Docker example](https://github.com/zauberzeug/nicegui/tree/main/examples/docker_image).
  115. To serve your application with [HTTPS](https://fastapi.tiangolo.com/deployment/https/) encryption, you can provide SSL certificates in multiple ways.
  116. For instance, you can directly provide your certificates to [Uvicorn](https://www.uvicorn.org/), which NiceGUI is based on, by passing the
  117. relevant [options](https://www.uvicorn.org/#command-line-options) to `ui.run()`:
  118. ''')
  119. @doc.ui
  120. def uvicorn_ssl():
  121. with python_window('main.py', classes='max-w-lg w-full'):
  122. ui.markdown('''
  123. ```python
  124. from nicegui import ui
  125. ui.run(
  126. port=443,
  127. ssl_certfile="<path_to_certfile>",
  128. ssl_keyfile="<path_to_keyfile>",
  129. )
  130. ```
  131. ''')
  132. doc.text('', '''
  133. In production we also like using reverse proxies like [Traefik](https://doc.traefik.io/traefik/) or [NGINX](https://www.nginx.com/) to handle these details for us.
  134. See our development [docker-compose.yml](https://github.com/zauberzeug/nicegui/blob/main/docker-compose.yml) as an example based on traefik or
  135. [this example nginx.conf file](https://github.com/zauberzeug/nicegui/blob/main/examples/nginx_https/nginx.conf) showing how NGINX can be used to handle the SSL certificates and
  136. reverse proxy to your NiceGUI app.
  137. You may also have a look at [our demo for using a custom FastAPI app](https://github.com/zauberzeug/nicegui/tree/main/examples/fastapi).
  138. This will allow you to do very flexible deployments as described in the [FastAPI documentation](https://fastapi.tiangolo.com/deployment/).
  139. Note that there are additional steps required to allow multiple workers.
  140. ''')
  141. doc.text('Package for Installation', '''
  142. NiceGUI apps can also be bundled into an executable with `nicegui-pack` which is based on [PyInstaller](https://www.pyinstaller.org/).
  143. This allows you to distribute your app as a single file that can be executed on any computer.
  144. Just make sure to call `ui.run` with `reload=False` in your main script to disable the auto-reload feature.
  145. Running the `nicegui-pack` command below will create an executable `myapp` in the `dist` folder:
  146. ''')
  147. @doc.ui
  148. def pyinstaller():
  149. with ui.row().classes('w-full items-stretch'):
  150. with python_window(classes='max-w-lg w-full'):
  151. ui.markdown('''
  152. ```python
  153. from nicegui import native, ui
  154. ui.label('Hello from PyInstaller')
  155. ui.run(reload=False, port=native.find_open_port())
  156. ```
  157. ''')
  158. with bash_window(classes='max-w-lg w-full'):
  159. ui.markdown('''
  160. ```bash
  161. nicegui-pack --onefile --name "myapp" main.py
  162. ```
  163. ''')
  164. doc.text('', '''
  165. **Packaging Tips:**
  166. - When building a PyInstaller app, your main script can use a native window (rather than a browser window) by
  167. using `ui.run(reload=False, native=True)`.
  168. The `native` parameter can be `True` or `False` depending on whether you want a native window or to launch a
  169. page in the user's browser - either will work in the PyInstaller generated app.
  170. - Specifying `--windowed` to `nicegui-pack` will prevent a terminal console from appearing.
  171. However you should only use this option if you have also specified `native=True` in your `ui.run` command.
  172. Without a terminal console the user won't be able to exit the app by pressing Ctrl-C.
  173. With the `native=True` option, the app will automatically close when the window is closed, as expected.
  174. - Specifying `--windowed` to `nicegui-pack` will create an `.app` file on Mac which may be more convenient to distribute.
  175. When you double-click the app to run it, it will not show any console output.
  176. You can also run the app from the command line with `./myapp.app/Contents/MacOS/myapp` to see the console output.
  177. - Specifying `--onefile` to `nicegui-pack` will create a single executable file.
  178. Whilst convenient for distribution, it will be slower to start up.
  179. This is not NiceGUI's fault but just the way Pyinstaller zips things into a single file, then unzips everything
  180. into a temporary directory before running.
  181. You can mitigate this by removing `--onefile` from the `nicegui-pack` command,
  182. and zip up the generated `dist` directory yourself, distribute it,
  183. and your end users can unzip once and be good to go,
  184. without the constant expansion of files due to the `--onefile` flag.
  185. - Summary of user experience for different options:
  186. | `nicegui-pack` | `ui.run(...)` | Explanation |
  187. | :--- | :--- | :--- |
  188. | `onefile` | `native=False` | Single executable generated in `dist/`, runs in browser |
  189. | `onefile` | `native=True` | Single executable generated in `dist/`, runs in popup window |
  190. | `onefile` and `windowed` | `native=True` | Single executable generated in `dist/` (on Mac a proper `dist/myapp.app` generated incl. icon), runs in popup window, no console appears |
  191. | `onefile` and `windowed` | `native=False` | Avoid (no way to exit the app) |
  192. | Specify neither | | A `dist/myapp` directory created which can be zipped manually and distributed; run with `dist/myapp/myapp` |
  193. - If you are using a Python virtual environment, ensure you `pip install pyinstaller` within your virtual environment
  194. so that the correct PyInstaller is used, or you may get broken apps due to the wrong version of PyInstaller being picked up.
  195. That is why the `nicegui-pack` invokes PyInstaller using `python -m PyInstaller` rather than just `pyinstaller`.
  196. ''')
  197. @doc.ui
  198. def install_pyinstaller():
  199. with bash_window(classes='max-w-lg w-full h-42 self-center'):
  200. ui.markdown('''
  201. ```bash
  202. python -m venv venv
  203. source venv/bin/activate
  204. pip install nicegui
  205. pip install pyinstaller
  206. ```
  207. ''')
  208. doc.text('', '''
  209. Note:
  210. If you're getting an error "TypeError: a bytes-like object is required, not 'str'", try adding the following lines to the top of your `main.py` file:
  211. ```py
  212. import sys
  213. sys.stdout = open('logs.txt', 'w')
  214. ```
  215. See <https://github.com/zauberzeug/nicegui/issues/681> for more information.
  216. ''')
  217. doc.text('', '''
  218. **macOS Packaging**
  219. Add the following snippet before anything else in your main app's file, to prevent new processes from being spawned in an endless loop:
  220. ```python
  221. # macOS packaging support
  222. from multiprocessing import freeze_support # noqa
  223. freeze_support() # noqa
  224. # all your other imports and code
  225. ```
  226. The `# noqa` comment instructs Pylance or autopep8 to not apply any PEP rule on those two lines, guaranteeing they remain on top of anything else.
  227. This is key to prevent process spawning.
  228. ''')
  229. doc.text('NiceGUI On Air', '''
  230. By using `ui.run(on_air=True)` you can share your local app with others over the internet 🧞.
  231. When accessing the on-air URL, all libraries (like Vue, Quasar, ...) are loaded from our CDN.
  232. Thereby only the raw content and events need to be transmitted by your local app.
  233. This makes it blazing fast even if your app only has a poor internet connection (e.g. a mobile robot in the field).
  234. By setting `on_air=True` you will get a random URL which is valid for 1 hour.
  235. If you sign-up at <https://on-air.nicegui.io>, you can setup an organization and device name to get a fixed URL:
  236. `https://on-air.nicegui.io/<my-org>/<my_device_name>`.
  237. The device is then identified by a unique, private token which you can use instead of a boolean flag: `ui.run(on_air='<your token>')`.
  238. If you [sponsor us](https://github.com/sponsors/zauberzeug),
  239. we will enable multi-device management and provide built-in passphrase protection for each device.
  240. Currently On Air is available as a tech preview and can be used free of charge.
  241. We will gradually improve stability and extend the service with usage statistics, remote terminal access and more.
  242. Please let us know your feedback on [GitHub](https://github.com/zauberzeug/nicegui/discussions),
  243. [Reddit](https://www.reddit.com/r/nicegui/), or [Discord](https://discord.gg/TEpFeAaF4f).
  244. **Data Privacy:**
  245. We take your privacy very serious.
  246. NiceGUI On Air does not log or store any content of the relayed data.
  247. ''')