-
I have a NiceGUI 1.x app that in the backend is constantly processing a bunch of incoming data, with bindings to UI elements. Think of it as a mini SCADA/HMI system. But, the app is only needed for specific tasks, so is usually wasting CPU. Since it's running on a Raspberry Pi, I need to be mindful of resource usage. I'd like to detect when no client connections exist, to throttle down the backend data processing, and, if possible, increase the binding_refresh_interval. Can a NiceGUI app detect when no active browser connections exist? |
Beta Was this translation helpful? Give feedback.
Replies: 7 comments 7 replies
-
You're probably not using the You should try explicitly wrapping your UI in a page. Then the UI is only built when a client connects, including timers and bindings. When a client disconnects, corresponding timers are stopped, bindings are removed and memory is freed up. In case you want to count the number of clients anyway: When using individual pages, you can count clients as follows: from nicegui import globals, ui
@ui.page('/')
def page():
ui.label(f'{len(globals.clients)} clients')
ui.run() Note that the displayed number includes one shared client for the auto-index page not used in this example. When only using the shared auto-index page, this number is always 1 (not very helpful in your case). Alternatively, you could register to |
Beta Was this translation helpful? Give feedback.
-
Ah, now I get it: "They refer only to the page they are placed on." That's nice functionality to have, tracking clients per page. Might want to add that clarification to the docs... and/or rename the methods to If feasible I do think an app-level |
Beta Was this translation helpful? Give feedback.
-
@bapowell this indeed would be a nice addition. Let's gather all the facts:
|
Beta Was this translation helpful? Give feedback.
-
Should we rename from nicegui import ui
@ui.page('/')
async def main_page(client: Client):
# setup code
await client.connected()
# working with the connection
await client.disconnected()
# cleanup
ui.run() |
Beta Was this translation helpful? Give feedback.
-
I've started a new pull-request #234 to provide a better basis for the discussion. @bapowell I would be very happy if you could do a review. Maybe a good way to learn the code base ;-) |
Beta Was this translation helpful? Give feedback.
-
I think this is a bit more complicated than it sounds. Currently a "client" is basically a browser tab with a websocket connection to the Python backend. If the user opens a new tab, we treat it as a complete new client. And if the tab is closed, the client disconnects no matter if there is another browser tab. To reflect multiple tabs, we would need to use something like session IDs. Although this is doable, it might complicate the API once more. A "disconnect" could either mean a tab closed or a complete session closed. This is independent of the current discussion about whether to fire on the current page only or globally. To keep the required changes minimal, we should leave the concept of clients and their connect/disconnect events unchanged and introduce the concept of "sessions" with something like "session started" and "session ended" events. But I'm not sure if this is worth the effort. |
Beta Was this translation helpful? Give feedback.
-
Regarding this discussion, the changes introduced in v1.1.0 work well. Thank you. import asyncio
import logging
from nicegui import ui, app, Client
import nicegui.globals
from fastapi import Request
logging.basicConfig(level=logging.DEBUG)
webui_client_count = 0
ui.label('Hello') # will be put on the auto-index page
@ui.page('/page1', title='Page #1')
def page1(request: Request, client: Client):
logging.debug(f'page1(request={request}, client={client})')
ui.label('Lifecycle testing - using client.on_connect|disconnect').classes('text-lg text-weight-bold')
client.on_connect(lambda: logging.debug('page1: on_connect fired'))
client.on_disconnect(lambda: logging.debug('page1: on_disconnect fired'))
@ui.page('/page2', title='Page #2')
async def page2(request: Request, client: Client):
logging.debug(f'page2(request={request}, client={client})')
ui.label('Lifecycle testing - using await client.connected|disconnected').classes('text-lg text-weight-bold')
await client.connected()
logging.debug('page2: client connected')
await asyncio.sleep(3)
ui.label('displayed 3 secs after client connected')
await client.disconnected()
logging.debug('page2: client disconnected')
def setup_ui():
app.on_startup(lambda: print('starting up'))
def register_client_connect():
global webui_client_count
webui_client_count += 1
nicegui_client_count = len(nicegui.globals.clients) - 1 # subtract 1 because NiceGUI always has a shared client for the auto-index page
logging.info(f'client connected; webui_client_count={webui_client_count}, nicegui_client_count={nicegui_client_count}')
app.on_connect(register_client_connect)
def register_client_disconnect():
global webui_client_count
webui_client_count -= 1
nicegui_client_count = len(nicegui.globals.clients) - 1 # subtract 1 because NiceGUI always has a shared client for the auto-index page
logging.info(f'client DISconnected; webui_client_count={webui_client_count}, nicegui_client_count={nicegui_client_count}')
app.on_disconnect(register_client_disconnect)
setup_ui()
ui.run() |
Beta Was this translation helpful? Give feedback.
Regarding this discussion, the changes introduced in v1.1.0 work well. Thank you.
Here's an example demonstrating the functionality: