Skip to content
Falko Schindler edited this page Aug 25, 2023 · 49 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 also some interesting examples like ffmepg, opencv webcam, search-as-you-type, progress and the script executor which could be helpful.

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 spawnes 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)

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.

Clone this wiki locally