configuration_deployment.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. from nicegui import ui
  2. from ..demo import bash_window, python_window
  3. from ..tools import load_demo, subheading, text_demo
  4. name = 'configuration_deployment'
  5. title = 'Configuration & Deployment'
  6. def intro() -> None:
  7. ...
  8. def content() -> None:
  9. @text_demo('URLs', '''
  10. You can access the list of all URLs on which the NiceGUI app is available via `app.urls`.
  11. The URLs are not available in `app.on_startup` because the server is not yet running.
  12. Instead, you can access them in a page function or register a callback with `app.urls.on_change`.
  13. ''')
  14. def urls_demo():
  15. from nicegui import app
  16. # @ui.page('/')
  17. # def index():
  18. # for url in app.urls:
  19. # ui.link(url, target=url)
  20. # END OF DEMO
  21. ui.link('https://nicegui.io', target='https://nicegui.io')
  22. load_demo(ui.run)
  23. @text_demo('Native Mode', '''
  24. You can enable native mode for NiceGUI by specifying `native=True` in the `ui.run` function.
  25. To customize the initial window size and display mode, use the `window_size` and `fullscreen` parameters respectively.
  26. Additionally, you can provide extra keyword arguments via `app.native.window_args` and `app.native.start_args`.
  27. Pick any parameter as it is defined by the internally used [pywebview module](https://pywebview.flowrl.com/guide/api.html)
  28. for the `webview.create_window` and `webview.start` functions.
  29. Note that these keyword arguments will take precedence over the parameters defined in `ui.run`.
  30. In native mode the `app.native.main_window` object allows you to access the underlying window.
  31. It is an async version of [`Window` from pywebview](https://pywebview.flowrl.com/guide/api.html#window-object).
  32. ''', tab=lambda: ui.label('NiceGUI'))
  33. def native_mode_demo():
  34. from nicegui import app
  35. app.native.window_args['resizable'] = False
  36. app.native.start_args['debug'] = True
  37. ui.label('app running in native mode')
  38. # ui.button('enlarge', on_click=lambda: app.native.main_window.resize(1000, 700))
  39. #
  40. # ui.run(native=True, window_size=(400, 300), fullscreen=False)
  41. # END OF DEMO
  42. ui.button('enlarge', on_click=lambda: ui.notify('window will be set to 1000x700 in native mode'))
  43. # Show a helpful workaround until issue is fixed upstream.
  44. # For more info see: https://github.com/r0x0r/pywebview/issues/1078
  45. ui.markdown('''
  46. If webview has trouble finding required libraries, you may get an error relating to "WebView2Loader.dll".
  47. To work around this issue, try moving the DLL file up a directory, e.g.:
  48. * from `.venv/Lib/site-packages/webview/lib/x64/WebView2Loader.dll`
  49. * to `.venv/Lib/site-packages/webview/lib/WebView2Loader.dll`
  50. ''')
  51. @text_demo('Environment Variables', '''
  52. You can set the following environment variables to configure NiceGUI:
  53. - `MATPLOTLIB` (default: true) can be set to `false` to avoid the potentially costly import of Matplotlib.
  54. This will make `ui.pyplot` and `ui.line_plot` unavailable.
  55. - `NICEGUI_STORAGE_PATH` (default: local ".nicegui") can be set to change the location of the storage files.
  56. - `MARKDOWN_CONTENT_CACHE_SIZE` (default: 1000): The maximum number of Markdown content snippets that are cached in memory.
  57. ''')
  58. def env_var_demo():
  59. from nicegui.elements import markdown
  60. ui.label(f'Markdown content cache size is {markdown.prepare_content.cache_info().maxsize}')
  61. subheading('Server Hosting')
  62. ui.markdown('''
  63. 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.
  64. 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.
  65. 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.
  66. 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.
  67. With this command you can launch the script `main.py` in the current directory on the public port 80:
  68. ''').classes('bold-links arrow-links')
  69. with bash_window(classes='max-w-lg w-full h-44'):
  70. ui.markdown('''
  71. ```bash
  72. docker run -it --restart always \\
  73. -p 80:8080 \\
  74. -e PUID=$(id -u) \\
  75. -e PGID=$(id -g) \\
  76. -v $(pwd)/:/app/ \\
  77. zauberzeug/nicegui:latest
  78. ```
  79. ''')
  80. ui.markdown('''
  81. The demo assumes `main.py` uses the port 8080 in the `ui.run` command (which is the default).
  82. 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.
  83. Of course this can also be written in a Docker compose file:
  84. ''')
  85. with python_window('docker-compose.yml', classes='max-w-lg w-full h-60'):
  86. ui.markdown('''
  87. ```yaml
  88. app:
  89. image: zauberzeug/nicegui:latest
  90. restart: always
  91. ports:
  92. - 80:8080
  93. environment:
  94. - PUID=1000 # change this to your user id
  95. - PGID=1000 # change this to your group id
  96. volumes:
  97. - ./:/app/
  98. ```
  99. ''')
  100. ui.markdown('''
  101. There are other handy features in the Docker image like non-root user execution and signal pass-through.
  102. For more details we recommend to have a look at our [Docker example](https://github.com/zauberzeug/nicegui/tree/main/examples/docker_image).
  103. ''').classes('bold-links arrow-links')
  104. ui.markdown('''
  105. You can provide SSL certificates directly using [FastAPI](https://fastapi.tiangolo.com/deployment/https/).
  106. 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.
  107. See our development [docker-compose.yml](https://github.com/zauberzeug/nicegui/blob/main/docker-compose.yml) as an example.
  108. You may also have a look at [our demo for using a custom FastAPI app](https://github.com/zauberzeug/nicegui/tree/main/examples/fastapi).
  109. This will allow you to do very flexible deployments as described in the [FastAPI documentation](https://fastapi.tiangolo.com/deployment/).
  110. Note that there are additional steps required to allow multiple workers.
  111. ''').classes('bold-links arrow-links')
  112. subheading('Package for Installation')
  113. ui.markdown('''
  114. NiceGUI apps can also be bundled into an executable with [PyInstaller](https://www.pyinstaller.org/).
  115. This allows you to distribute your app as a single file that can be executed on any computer.
  116. Just take care your `ui.run` command does not use the `reload` argument.
  117. Running the `build.py` below will create an executable `myapp` in the `dist` folder:
  118. ''').classes('bold-links arrow-links')
  119. with ui.row().classes('w-full items-stretch'):
  120. with python_window(classes='max-w-lg w-full'):
  121. ui.markdown('''
  122. ```python
  123. from nicegui import native_mode, ui
  124. ui.label('Hello from PyInstaller')
  125. ui.run(reload=False, port=native_mode.find_open_port())
  126. ```
  127. ''')
  128. with python_window('build.py', classes='max-w-lg w-full'):
  129. ui.markdown('''
  130. ```python
  131. import os
  132. import subprocess
  133. from pathlib import Path
  134. import nicegui
  135. cmd = [
  136. 'python',
  137. '-m', 'PyInstaller',
  138. 'main.py', # your main file with ui.run()
  139. '--name', 'myapp', # name of your app
  140. '--onefile',
  141. #'--windowed', # prevent console appearing, only use with ui.run(native=True, ...)
  142. '--add-data', f'{Path(nicegui.__file__).parent}{os.pathsep}nicegui'
  143. ]
  144. subprocess.call(cmd)
  145. ```
  146. ''')
  147. ui.markdown('''
  148. **Packaging Tips**
  149. - When building a PyInstaller app, your main script can use a native window (rather than a browser window) by
  150. using `ui.run(reload=False, native=True)`.
  151. The `native` parameter can be `True` or `False` depending on whether you want a native window or to launch a
  152. page in the user's browser - either will work in the PyInstaller generated app.
  153. - Specifying `--windowed` to PyInstaller will prevent a terminal console from appearing.
  154. However you should only use this option if you have also specified `native=True` in your `ui.run` command.
  155. Without a terminal console the user won't be able to exit the app by pressing Ctrl-C.
  156. With the `native=True` option, the app will automatically close when the window is closed, as expected.
  157. - Specifying `--windowed` to PyInstaller will create an `.app` file on Mac which may be more convenient to distribute.
  158. When you double-click the app to run it, it will not show any console output.
  159. You can also run the app from the command line with `./myapp.app/Contents/MacOS/myapp` to see the console output.
  160. - Specifying `--onefile` to PyInstaller will create a single executable file.
  161. Whilst convenient for distribution, it will be slower to start up.
  162. This is not NiceGUI's fault but just the way Pyinstaller zips things into a single file, then unzips everything
  163. into a temporary directory before running.
  164. You can mitigate this by removing `--onefile` from the PyInstaller command,
  165. and zip up the generated `dist` directory yourself, distribute it,
  166. and your end users can unzip once and be good to go,
  167. without the constant expansion of files due to the `--onefile` flag.
  168. - Summary of user experience for different options:
  169. | PyInstaller | `ui.run(...)` | Explanation |
  170. | :--- | :--- | :--- |
  171. | `onefile` | `native=False` | Single executable generated in `dist/`, runs in browser |
  172. | `onefile` | `native=True` | Single executable generated in `dist/`, runs in popup window |
  173. | `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 |
  174. | `onefile` and `windowed` | `native=False` | Avoid (no way to exit the app) |
  175. | Specify neither | | A `dist/myapp` directory created which can be zipped manually and distributed; run with `dist/myapp/myapp` |
  176. - If you are using a Python virtual environment, ensure you `pip install pyinstaller` within your virtual environment
  177. so that the correct PyInstaller is used, or you may get broken apps due to the wrong version of PyInstaller being picked up.
  178. That is why the build script invokes PyInstaller using `python -m PyInstaller` rather than just `pyinstaller`.
  179. ''').classes('bold-links arrow-links')
  180. with bash_window(classes='max-w-lg w-full h-42 self-center'):
  181. ui.markdown('''
  182. ```bash
  183. python -m venv venv
  184. source venv/bin/activate
  185. pip install nicegui
  186. pip install pyinstaller
  187. ```
  188. ''')
  189. ui.markdown('''
  190. **Note:**
  191. 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:
  192. ```py
  193. import sys
  194. sys.stdout = open('logs.txt', 'w')
  195. ```
  196. See <https://github.com/zauberzeug/nicegui/issues/681> for more information.
  197. ''').classes('bold-links arrow-links')
  198. subheading('NiceGUI On Air')
  199. ui.markdown('''
  200. By using `ui.run(on_air=True)` you can share your local app with others over the internet 🧞.
  201. When accessing the on-air URL, all libraries (like Vue, Quasar, ...) are loaded from our CDN.
  202. Thereby only the raw content and events need to be transmitted by your local app.
  203. This makes it blazing fast even if your app only has a poor internet connection (e.g. a mobile robot in the field).
  204. By setting `on_air=True` you will get a random URL which is valid for 1 hour.
  205. If you sign-up at <https://on-air.nicegui.io> you get a token which could be used to identify your device: `ui.run(on_air='<your token>'`).
  206. This will give you a fixed URL and the possibility to protect remote access with a passphrase.
  207. Currently On Air is available as a tech preview and can be used free of charge (for now).
  208. We will gradually improve stability, introduce payment options and extend the service with multi-device management, remote terminal access and more.
  209. Please let us know your feedback on [GitHub](https://github.com/zauberzeug/nicegui/discussions),
  210. [Reddit](https://www.reddit.com/r/nicegui/), or [Discord](https://discord.gg/TEpFeAaF4f).
  211. **Data Privacy:**
  212. We take your privacy very serious.
  213. NiceGUI On Air does not log or store any content of the relayed data.
  214. ''').classes('bold-links arrow-links')
  215. ui.element('div').classes('h-32')