Skip to content
/ gro Public

A wrapper library around Gradio that is perfect for simplifying the creation of any persistent single-user UI application.

License

Notifications You must be signed in to change notification settings

aliakyurek/gro

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

gro Library

The gro library is a wrapper around the excellent Gradio library that simplifies the creation of any persistent single-user, multi-page UI application. It provides a way to separate the UI from the logic, making it easier to maintain and test the application. As gradio is designed to be very easy to use for the input-output applications, its flexibility allows even more complex applications to be built on top of it. However, the complexity of the application can grow quickly as the number of UI elements and their interactions increase. The gro library aims to provide a structure to manage this complexity by introducing a clear separation between the UI and the logic. It is designed to be used for applications that require a persistent state, such as a todo list, a note-taking app, or any other application that requires a multi-page UI. The library provides a way to define the UI elements and their layout, and then bind them to the logic of the application.

Quick Start

Installation

Use pip:

pip install git+https://github.com/aliakyurek/gro.git

How to use

Suppose you want to build a simple todo application with an authentication page and a home page that displays the todo list. The application will have the following features:

  • An authentication page with a password input and a login button.
  • A home page with a text input for adding new todo items, a button to add the item, and a markdown component to display the list of todo items.
  • A top bar with a title and a logout button.

To build this application using the gro library, you will need to follow these steps:

  • Inherit from the gro.Page for each sub page of the application
  • Define the gradio UI components in the class members. Define the layout of the UI components in the layout_elements overridden method.
  • Inherit from the gro.App class to create the top UI that contains the pages and the top bar.
  • Here is the code for these all steps:
Auth Page HomePage
alt text alt text alt text

Implement the model. This is where the logic of the application resides and independent from the gradio UI. It can be a simple class that holds the state of the application and provides methods to manipulate it. For example, a simple todo application model can look like this:

class Todo:
    def __init__(self):
        self.tasks = []
        self.logged_in = False

    def add_task(self, task):
        self.tasks.append({"task": task, "completed": False})

    def get_task_markdown(self):
        return "\n".join(f"- {t['task']}" for t in self.tasks if not t["completed"])

Now outline the application. This is where the UI and model are tied together.

  • Instantiate the model class.

  • Instantiate the UI from UIApp and model from Todo classes.

    • Parameters passed to the UIApp are passed to the Gradio Block constructor under the hood.
  • Implement event listeners in outside the UI class and bind them to the UI elements.

  • Attach event listeners. under a with self.ui.block: context

    • See self.ui.add_button.click in the Application class example.
    • Multiple event handlers are supported that utilizes then method of Gradio code.
    • See how multiple listeners binded for buttons.
  • When the page is refreshed or closed/reopend all the state is normally lost if a Gradio state structure is not used. As we operate with a model, we can persist the state in the model and update UI from it. This is done by binding so called a data source function from model (or application) to a UI element.

    • Binding can be used for Pages and any gradio UI element.
    • The binder function is called when the UI element is to be rendered.
    • The return value of the binder function is a dictionary of parameters which are valid for that UI element like parameters of gr.Button.
    • Check self.ui.logout_button.bind to see how the logout button visbility depends on the logged_in state of the model.
    • Pages can be also bound to a binder function. This is useful for showing/hiding pages based on the model state.
    • For binder functions to be called, UI element must be rendered, and for UI element to be rendered, page must be refreshed.
    • Therefore, when the login and logout buttons are clicked, as a post action, the UI is reloaded by calling gro.App.RELOAD which is a special dictionary that can be passed to the then or success methods of Gradio event listeners.
    • The default password is 123 and the application will show an error message if the password is incorrect, otherwise it changes the logged_in state of the model to True and shows the home page.
  • Finally call the start() method of the UI to start the application. This function is blocking and will return when the application is closed.

    • As the HelloBlock utilizes Gradio Block under the hood, launch parameters can be passed to UI constructor.
class Application:
    def __init__(self):
        self.todo = Todo()

        # ui configuration
        self.ui = UIApp(title="gro Todo App")

        # populate some initial data for model
        self.todo.add_task("Wake up at 8. (Default task)")

        # ui <-> model configuration
        # set custom data bind, gradio event listeners
        with self.ui.block:
            # set custom data bind
            # set custom event listeners
            self.ui.auth_page.bind(lambda: { "visible": not self.todo.logged_in })
            self.ui.home_page.bind(lambda: { "visible": self.todo.logged_in })
            self.ui.home_page.tasks_markdown.bind(lambda: { "value": self.todo.get_task_markdown() }) # type: ignore
            self.ui.logout_button.bind(lambda: { "visible": self.todo.logged_in }) # type: ignore

            # set gradio event listeners
            self.ui.auth_page.login_button.click(fn=self.login_button_click,
                                                 inputs=[self.ui.auth_page.password_textbox], queue=False).success(**gro.App.RELOAD)
            self.ui.logout_button.click(fn=self.logout_button_click, queue=False).then(**gro.App.RELOAD)
            self.ui.home_page.add_button.click(fn=self.add_button_click, inputs=[self.ui.home_page.todo_textbox],
                                 outputs=[self.ui.home_page.tasks_markdown])
            self.ui.home_page.add_button.click(fn=self.add_button_click_then, outputs=[self.ui.home_page.todo_textbox], show_progress="hidden")

 
    def login_button_click(self, password):
        self.todo.logged_in = password =="123"
        # raise an error if password is not correct
        if not self.todo.logged_in:
            raise gr.Error("Incorrect password. Please try again.")

    def logout_button_click(self):
        self.todo.logged_in = False
    
    def add_button_click(self, task):
        self.todo.add_task(task)
        return self.todo.get_task_markdown()
    
    def add_button_click_then(self):
        return ""

    def run(self):
        # following should be the last call as it doesn't return.
        self.ui.launch(inbrowser=True)

if __name__ == "__main__":
    app = Application()
    app.run()

Demo

Demo Animation

Library Development

  • Clone the repository git clone https://github.com/aliakyurek/gro.git
  • Change directory to the cloned repository cd gro
  • Create a virtual environment python -m venv venv
  • Activate the virtual environment
    • On Windows: venv\Scripts\activate
    • On Linux/Mac: source venv/bin/activate
  • Install the library in editable mode pip install -e .

License

This project is licensed under the MIT License.

About

A wrapper library around Gradio that is perfect for simplifying the creation of any persistent single-user UI application.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages