Migrating from pre-alpha
Before you begin, it is highly recommended that you download a release and run the interactive tour.py
that ships with the release to get a feel for what Wave programs look like in practice.
What has changed?
From an app-development perspective, the most important change is that Wave is more of a library rather than a framework.
With the previous framework, the only way to execute an app was via the Q server. This limitation has been removed. The script/app you author is just a regular Python program in which you import h2o_wave
and execute via:
- The command line:
python3 my_script.py
. - In the Python REPL.
- In a Jupyter notebook.
- In your favorite IDE (PyCharm, VSCode, etc.).
This also means that you can apply breakpoints and debug or step-through your program in your debugger of choice.
From an information architecture perspective, control has been inverted: instead of your app being an extension to Wave's data/prep/search features, Wave's features are now optional additions to your app, and your app takes center stage. Implementation-wise, instead of your app running in a sidebar inside of Wave's UI, your app now occupies the entire UI.
Breaking changes
@Q.app
, @Q.ui
annotations.
Removed: Instead, define a async
request-handling function, say main()
, and pass that function to listen()
, like this:
q.wait()
, q.show()
, q.fail()
, q.exit()
.
Removed: The above four methods were the primary mechanism to make changes to your app's UI. They have all been replaced with a single h2o_wave.core.Page.save()
method.
The new technique is:
- Access the page or card you want to modify.
- Modify the page or card.
- Call
h2o_wave.core.Page.save()
to save your changes and update the browser page.
Before:
After:
Note that the After example requires a box
that specifies where to draw your form. This is because you are not limited to using a sidebar, and can use the entire width/length of the page.
The same technique can be used to update the UI again (or display intermediate results):
Before:
After:
Removed: callback functions for request-handling.
Wave apps are 100% push-based, using duplex communication instead of a request/reply paradigm. There is no need to have a tangled mess of callbacks to handle UI events.
Instead, all requests are routed to a single function, and you can decide how to organize your application logic by branching on q.args.*
.
Before:
After:
q.dashboard()
and q.notebook()
.
Removed: Every page in Wave is a dashboard page. Instead of creating a separate dashboard or notebook, simply add cards to a page and arrange it the way you want. Cards can be created by using one of the several ui.*_card()
APIs. Also see the dashboard, layout and sizing examples to learn how to lay out several cards on a page.
If you want to display a notebook-style vertical stack of markdown, html or other content, use h2o_wave.ui.text()
and h2o_wave.ui.frame()
contained inside a h2o_wave.ui.form_card()
, like this:
Before:
After: Note the parameter name change frame_cell(source=...)
to frame(content=...)
.
ui.buttons()
, ui.expander()
and ui.tabs()
accept a list
of items instead of var args *args
.
Changed: Before:
After:
q.upload()
changed to q.site.upload()
.
Changed: The upload()
method has been moved to the h2o_wave.core.Site
instance, since each h2o_wave.core.Site
represents a distinct server, and makes it possible to control multiple sites from a single Python script.
q.args.foo=
changed to q.client.foo=
.
Changed: Setting attributes on q.args
(e.g. q.args.foo = 'bar'
) is no longer preserved between requests. This was the primary mechanism employed previously to preserve data between requests.
Instead, Wave provides 4 mechanisms for preserving data between requests:
- Process-level: Use global variables.
- App-level: Use
q.app.foo = 'bar'
to save; accessq.app.foo
to read it back again. - User-level: Use
q.user.foo = 'bar'
to save; accessq.user.foo
to read it back again. - Client-level: Use
q.client.foo = 'bar'
to save; accessq.client.foo
to read it back again.
Here, Client refers to a distinct tab in a browser.
If you want to rely on the old behavior of preserving q.args
for the lifetime of the application, copy q.args
to q.client
like this:
Changed: No need to JSON-serialize values to preserve them between requests.
q.args.foo=
only supported JSON-serialized values. No such restrictions exist for the q.app
, q.user
and q.client
containers. You could, for example, load a Pandas dataframe and set q.user.df = my_df
, and the dataframe will be accessible across requests for the lifetime of the app.