Skip to content
Rodja Trappe edited this page Apr 16, 2024 · 47 revisions

Community

Where can I get help?

Check this FAQ, seek inside the GitHub discussions, Reddit, StackOverflow or connect with other developers on our Discord server. Because NiceGUI is build on FastAPI in the backend and Vue3 + Quasar in the frontend, you can also solve a lot of problems/questions by looking into their documentation.

When starting new questions or discussions, please put effort in formulating your post. Quite a number of people will read and think about your message. Make it worth their time.

If you encounter a bug or other issue with NiceGUI, the best way to report it is by opening a new issue on our GitHub repository.

My English is not so good, what can I do?

Write your question/suggestion/info in your main language and translate it with an online tool like Google Translate, ChatGPT or similar. The output is often surprisingly good.

Coding

How to not get the "reconnect popup" or avoid laggy interaction?

See the question about long running functions below.

Why is my long running function blocking UI updates?

NiceGUI (and the underlying FastAPI) are async frameworks. That means, no io-bound or cpu-bound tasks should be directly executed on the main thread but rather be "awaited". Otherwise they block execution on the main loop. See https://fastapi.tiangolo.com/async/ for a good in-depth explanation. There are great libraries like aiofiles (for writing files async) and httpx (for async requests) or NiceGUIs generic run.io_bound to do io-bound work (eg. if waiting for data). Cpu-bound tasks need to be run in another process which can be achieved with NiceGUI's run.cpu_bound (eg. if having something compute-heavy). See our examples ffmepg, opencv webcam, search-as-you-type, progress and the script executor which could be helpful.

Keep in mind that the run.cpu_bound tasks should not access class properties. As explained in https://github.com/zauberzeug/nicegui/discussions/2221#discussioncomment-7920864, run.cpu_bound needs to execute the function in a separate process. For this it needs to transfer the whole state of the passed function to the process (which is done with pickle). It is encouraged to create static methods (or free functions) which get all the data as simple parameters (eg. no class/ui logic) and return the result (instead of writing it in class properties or global variables).

If a page builder decorated with @ui.page is not marked as async, FastAPI will run it in a background thread. That means you can very-well use io-blocking code like file reading, database access or requests in these functions without pausing the main thread. Just be aware that a later refactoring to make the page builder async will break your code. Thats why we advise against relying on this pattern as much as possible.

To see warnings about any code which blocks the main thread for more than 50 ms you can use the following code snippet:

def startup():
    loop = asyncio.get_running_loop()
    loop.set_debug(True)
    loop.slow_callback_duration = 0.05

app.on_startup(startup)

Do not use this in production because it will significantly slow down execution.

Why is the last element of my for-loop used in every step?

See below.

Why have all my elements the same value?

You are probably experiencing Python's "late binding".

Try

for i in [1, 2, 3]:
    ui.button(i, on_click=lambda: ui.label(i))

vs.

for i in [1, 2, 3]:
    ui.button(i, on_click=lambda i=i: ui.label(i))

The i=i captures the i within the lambda statement. When the lambda is eventually evaluated, it would use the current value of i (which is now 3). But with i=i the label is created using a local copy.

Why is my code executed twice?

You are probably using reload=True which runs the main code once and then spawns a subprocess which is killed and relaunched on file change. To avoid evaluation of your code in the first "init" you have several options:

  • Use ui.run(reload=False). Of course, you loose the handy auto-reload feature. But for production this might be the way to go.

  • Use some kind of main guard:

    if __name__ == '__mp_main__':
        print("Some message")
        ui.label("test")
    ui.run()

    This avoids evaluating the code in the "__main__" process and restricts it to the child process "__mp_main__".

  • Use a page decorator:

    @ui.page('/')
    def main():
        print("Some message")
        ui.label("test")

    This evaluates the UI only when accessed. But if you have an expensive initialization to do once when starting the script, this might not be the way to go. Page decorators also change the visibility, since it generates a new page per client, so the state is not shared anymore.

  • Move expensive initialization into a startup callback:

    def startup():
        print("Some message")
    
    app.on_startup(startup) 
    ui.label("test")

    This way startup is only evaluated once in the child process, since the app doesn't start in the main process (unless reload=False).

How to make new elements appear at the right position?

In NiceGUI elements are placed wherever they are created. That allows you to quickly create and comprehend nested layout structures. If you create new elements inside an event handler, NiceGUI will place them in the parent container of the element which fired the event.

To pick some other place in the UI, you can enter its context explicitly. For example, if you have a button outside of a custom element to alter its state:

class LabeledCard(ui.card):

    def __init__(self) -> None:
        super().__init__()
        with self:
            ui.label('This is the LabeledCard')

    def add_label(self) -> None:
        with self:  # Make sure to use the context of the card
            ui.label('This is a label')

card = LabeledCard()
ui.button('Add label to card', on_click=card.add_label)

What is the difference between ui.refreshable and .bind?

Binding is for automatically updating individual UI elements when certain values change. This won't add or remove element, but simply update them with new attributes. The refreshable decorator wraps a function that will replace a whole container when refresh is called. This might be more convenient to implement and allows for more complex dependencies (e.g. setting certain style or classes depending on some model state). But you have to be aware of the fact that a whole container element is replaced, which causes more network traffic. And client state, like the cursor position in an input element or the state of a dropdown menu, might get lost.

How to get the path of an uploaded file?

It cannot be achieved with the ui.upload element because it uses the internal file picker dialog of the browser. For security reasons the browser does not provide the full path of the selected files. See #283 and #269 for more details.

But we have build an example to show a custom local file browser which acts on the filesystem of the running app (e.g. picking files from the server, not the user machine running the browser). If you are in native mode you can also use the system file picker which provides the paths:

from nicegui import app, ui

async def choose_file():
    files = await app.native.main_window.create_file_dialog(allow_multiple=True)
    for file in files:
        ui.notify(file)

ui.button('choose file', on_click=choose_file)

ui.run(native=True)

Why is everyone seeing the same content?

In its simplest form NiceGUI elements are all shared between clients through the so called "auto-index client". If you want private pages, you should use page-builder functions marked with the @ui.page decorator.

Styling

Should I use Quasar or Tailwind for styling?

You can use both but need to look out for subtile incompatibilities. For example when defining breakpoints. NiceGUI uses Vue3 with Quasar as a web framework because it has tons of greatly customizable UI elements and a huge community. On top of that, we decided early on that Tailwind adds quite a lot of nice styling features which would be a bit more difficult to achieve with Quasar alone.

Why is my color not working and instead showing white?

The problem could be that for example "green-400" is a Tailwind color and not from the Quasar color palette. The details, why the Tailwind color works in light mode, are a bit more complicated. But Quasar basically assumes the color should be #fff. As the QToggle documentation states, the "color" prop needs to be a name from the Quasar Color Palette.

Why does my ui.row not fit even if children widths add up to 100%?

NiceGUI has a default gap applied so the children's size plus gaps are more than 100%. Therefore the flex layout automatically wraps elements onto the next "line". You can fix it by either set the no-wrap or gap-0 classes. See this question on StackOverflow and this discussion for more information.

Storage

Where is the data stored?

app.storage.user and app.storage.general are saved in json files in the .nicegui folder of your current working dir. That means, if you deploy the app to a cloud server, the data will be stored there. NiceGUI does not provide a central data server.

app.storage.browser is an encrypted session cookie which can also hold small amounts of key/value data. This data is only stored in the browser cookie and available in memory to the Python code. The browser data expires after 30 days after not accessing.

Unexpected Behaviours

How to avoid the "reloading because handshake failed" javascript error message which results in a full page reload?

It might be that you are running Gunicorn or another load balancer with multiple workers. In that case the websocket connection may not be delivered to the same instance which created the html page. To avoid that you need switch to another more sophisticated load balancer like Traefic, Nginx or HAProxy which support sticky sessions. A simpler alternative is to just use a single worker and embrace async/await where an event loop ensures high speed concurrency. See the feature request about multiple worker support for details.

Why are all browser tabs/windows showing the same content?

Thanks to the auto-index page, NiceGUI is very easy to start with. It also helps when trying or sharing short code snippets and simplifies example code. But as correctly stated on Discord, in 99% of all more complex apps, pages should rather be generated for each user individually using the @ui.page decorator.

Why am I seeing ERROR 10048?

It might be that you are using Python's multiprocessing module in a pyinstaller --onefile ... compiled executable in Windows. This link explains the details, but if you use multiprocessing.freeze_support() at the top of your main guard, things should work as you'd expect them to.