Open pngwn opened 2 years ago
Besides direct URLs, what functionality does a gr.Page()
allow that a gr.Tab()
doesn't? All sorts of interesting cases pop up with pages -- e.g. can pages be nested inside other pages similar to tabs? Can you .load()
a Space with pages inside of another Gradio app? We could implement it but I'm not sure if the value is that high?
Main thing is that it is a different level of hierarchy to tabs. Tabs and Pages are different, one could not replace the other but they fill a similar role. Nested Tabs are a very poor experience for the end user and should be discouraged, tabs inside pages are nice and clean. This shift in hierarchy is also reflected in having a different levels of navigation for Pages vs Tabs, and allows us more flexibility in how we communicate them to the user.
can pages be nested inside other pages similar to tabs?
Probably, nested routing is a thing
Can you .load() a Space with pages inside of another Gradio app?
Probably, it would form a nested route.
Haven't thought about the implementation much and I'm not going to fight for this feature but it has been requested a couple of times. Value is middling, the URL thing makes it very tempting. A cleaner Tab implementation might go someway to addressing the visual side of things, i dislike tabs as they stand.
One option is to mount multiple gradio apps on a single FastAPI object, and treat each mounted gradio app/route as a page in the overall app, I have a barebones example below extending the doc example from https://gradio.app/sharing-your-app/#mounting-within-another-fastapi-app
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
import gradio as gr
app = FastAPI()
HELLO_ROUTE = "/hello"
GOODBYE_ROUTE = "/goodbye"
iframe_dimensions = "height=300px width=1000px"
index_html = f'''
<h1>Put header here</h1>
<h3>
You can mount multiple gradio apps on a single FastAPI object for a multi-page app.
However if you mount a gradio app downstream of another gradio app, the downstream
apps will be stuck loading.
</h3>
<h3>
So in particular if you mount a gradio app at the index route "/", then all your
other mounted gradio apps will be stuck loading. But don't worry, you can still embed
your downstream gradio apps into the index route using iframes like I do here. In fact,
you probably want to do this anyway since its your index page, which you want to detail
more fully with a jinja template.
For a full example, you can see my <a href=https://yfu.one/>generative avatar webapp</a>
</h3>
<div>
<iframe src={HELLO_ROUTE} {iframe_dimensions}></iframe>
</div>
<div>
<iframe src={GOODBYE_ROUTE} {iframe_dimensions}></iframe>
</div>
'''
@app.get("/", response_class=HTMLResponse)
def index():
return index_html
hello_app = gr.Interface(lambda x: "Hello, " + x + "!", "textbox", "textbox")
goodbye_app = gr.Interface(lambda x: "Goodbye, " + x + "!", "textbox", "textbox")
app = gr.mount_gradio_app(app, hello_app, path=HELLO_ROUTE)
app = gr.mount_gradio_app(app, goodbye_app, path=GOODBYE_ROUTE)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app)
(btw how do you embed a gist into comment?)
A key observation I've found is that you cannot mount a gradio app downstream of another mounted gradio app, or else the downstream gradio apps will be disabled. But you can still embed your downstream gradio apps into your upstream routes like I do in the example.
This is what I use for my generative avatar webapp https://yfu.one catering to ML/AI art enthusiasts (based on observations of aesthetic trends in social media). I have multiple pages of gradio apps at https://yfu.one/apps/front_ui/, https://yfu.one/apps/full_ui/, https://yfu.one/register, etc.
Realizing I could make my webapp by just serving jinja templates with embedded gradio apps for reactive web components was a breakthrough moment for me after banging my head on react tutorials for some time. I hope the example can be useful for others seeking to turn their gradio app into a more fully featured webapp with just writing Python.
Love to have this enhancement too, To me, Gradio is a framework to GUI-fy a function, FastAPI a framework to Web-API-fy a function, so if a Gradio app instance can be mapped to a route or exposed as an URL-endpoint, it will open up potential to compose complex Gradio app out of simple atomic apps. My use-case is to build a multi-lingual dictionary in Gradio for educational purpose.
Love to have this enhancement too, To me, Gradio is a framework to GUI-fy a function, FastAPI a framework to Web-API-fy a function, so if a Gradio app instance can be mapped to a route or exposed as an URL-endpoint, it will open up potential to compose complex Gradio app out of simple atomic apps. My use-case is to build a multi-lingual dictionary in Gradio for educational purpose.
Well said @wgong, I agree completely with your statement and the discussion above. In my case, I'm interested in grouping several LLMs Application built with Gradio and having each of those apps under a given route.
I also agree with this idea. I can see many use cases where this feature would be advantageous. Additionally, it would allow for more complex uses of Gradio, making it more of a universal GUI framework.
Should be possible with custom components Soon ™️
Greetings,
I am using Starlette (not Fast API) for a long time. I am following the endpoints pattern. (Yes, in Django context, I also prefer Class Based Views) So, I have the mindset to break a whole thing (one api entry uri, with multiple api endpoints) into small parts. When it comes to gradio, it's a little bit strange feeling to put things together for first time.
IMHO, the existing gradio flavor to mitigate such strange feeling is to use TabbedInterface
, then separate each tab into different python files.
Last week, I was following the OAuth with External Providers example to enable gradio integration with GitHub Enterprise. It's a successful try. Instead of using decorators for defining routing, I am using routes list pattern. For now, it's just redirections to two gradio apps. But it's possible to define a static HTML page as a landing page with different links to different gradio apps.
I am fine with a gradio package without native multi pages support. I respect the project owner design decision (I guess the project target for gradio will not be something like django-cms). Eventually, the existing gradio users will become full stack developers. Then welcome to the Angular/React/Vue jungles. ;)
Index Page
from starlette.endpoints import HTTPEndpoint
from starlette.requests import Request
from starlette.responses import RedirectResponse
from starlette.routing import Route
class Index(HTTPEndpoint):
async def get(self, request: Request):
user = request.session.get('user', None)
if user:
return RedirectResponse(url='/gradio/')
else:
return RedirectResponse(url='/login/')
routes = [
Route('/', Index, name='index'),
]
routes.py
from starlette.routing import Mount
from views.api.routes import routes as routes_api
from views.ui.root import routes as routes_root
from views.ui.auth import routes as routes_auth
# when using starlette
routes = [
Mount('/api', routes=routes_api),
Mount('/auth', routes=routes_auth),
Mount('/', routes=routes_root),
]
main.py
# snipped
# do login
app = gr.mount_gradio_app(app, login_blocks, path='/login')
# show gradio page if authentication is done
app = gr.mount_gradio_app(app, main_blocks, path="/gradio",
auth_dependency=get_user)
# add addtional routes
for route in routes:
app.routes.append(route)
I'm very new to Gradio, but I agree that some form of a multi-page app example would be nice. This isn't perfect but it does work with the 30 minutes of testing I've put on it :)
@abidlabs @pngwn I agree that it would be better/easier to have some kind of multipage framework, although as a work-around, I figured out that under gr.Blocks, I could configure multiple gr.Rows with individual layouts and treat them as "pages", i.e. login, welcome page, desktop app, mobile app using visibility= to control the flow.
This is exactly what I need. I hope to add this feature and integrate it with Flask or other framework.
Depends on #8795
in general, now we are using SvelteKit, this should be relatively straightforward.
We are using a catch all route right ([...] intercepts all routes) and redirecting everything to /
. We should be able to remove the redirect, read the current path and filter the config to only pass down the portion of the config that we want to render for a given page.
We need to decide what to do with unknown paths, we could 404, which is technically correct but it might be nicer to just redirect to /
on unknown urls.
The bigger issue is that not everything in gradio is using the SvelteKit app right now. The big pending issue is making sure we are using the SvelteKit app in js/app
when ssr_mode
is True _or_
False`.
Is your feature request related to a problem? Please describe.
It would be good to distinct pages for different parts of a gradio app, including the ability to navigate directly to that page using the url. This feature should also add some kind of navigation bar/ structure in order to navigate the application.
Describe the solution you'd like
Something like this would be cool:
This would generate an app with a client side router that shows the first page by default with some kind of navigation bar with a link to the first + second pages. Clicking the second page link would change the url and change what is displayed. the urls would be something like:
/#/
/#/page-two
We could use hashbangs
/#!/xxx
but i don't know if there is any value in doing that anymore, might help google a bit but I don't know how crawlable spaces are anyway.Additional context
It is important that this features works well on huggingface spaces, since it is one of our most used platforms. This introduces certain challenges/ limitations as, even though spaces have their own subdomain now, they are typically used via the the spaces chrome embed.
Additionally as we support embedding via the web component, it is important that gradio doesn't have too many opinions about the structure of the URL and works on any subpath.
With this in mind, I think the best solution is a hash-based router, as hashes are inherently very flexible and can be appended to any URL. This will work well in spaces (and can be made really nice with some tweaks to spaces themselves) and will work well when embedded on blogposts etc.