Wrapping gradio to create applications with more flexible and clean implementations.
The guide is added to gradio guides.
- You have global access to all of the components in the
attach_event
functions. - You can structure your application more readable and it can be maintained easily.
- All additional components have cleared at launch state so your program is launched nearly same as the default implementation.
- You can reuse your components across the application.
-
You can see the example in the
main.py
file. -
Create a main application with the
warp_gradio.application.Application
class. -
Use the layouts from the
warp_gradio.layouts
rather thangradio
package. You can useRowLayout
,ColumnLayout
andTabLayout
classes.- To use the layouts with the
attach_event
function, create classes that inherits from these classes. If you are using child layouts which are implented theattach_event
function, call this functions from the parent layouts'attach_event
function.
- To use the layouts with the
-
Add the components which are created from the
gradio
package to layouts with theadd_component
function. Add the layers which are created from thewarp_gradio.layouts
with theadd_layout
function.- The components have to be created with
render = false
parameter, because they are going to be rendered afterwards.
- The components have to be created with
-
Add the layouts to the main application with the
add
funciton. -
Launch the program with the
launch
function.
- Main classes that used for warping are
LayoutBase
andApplication
. - Below parts are describing their variables, functions and how they are implemented.
- This class is the base class for the layouts which are
RowLayout
,ColumnLayout
,TabLayout
. - You can add components and layouts, attach events to the components.
Variable | Type | Definition |
---|---|---|
main_layout | gradio.blocks.Block | Stores the main layout from the gradio package of this class. For example storing gradio.layouts.Row layout for the RowLayout class. |
name | str | Name of the class. Using to differantiate from other layouts and for debug purposes. |
global_children_dict | Dict[str, gradio.blocks.Block] | Stores the children components with given name. Its values are accumulating with each parent. |
renderables | list | Stores the renderable elements. Layout and children components are not seperated because the order is important. So containing all of them in one list variable. |
def add_component(self, name: str, component: Block) -> None:
self.renderables.append(component)
self.global_children_dict[name] = component
- In this function, new component is added to layout class.
- This component can be Textbox, TextArea, any component from the gradio package.
- If the
Block
component confuse you, the components and layouts in the gradio package are inherit from thegradio.blocks.Block
class. - You can see from this function, new component is added to
renderables
andglobal_children_dict
variables. In theadd_layout
function, you will understand why there are two different variables for the children components.
def add_layout(self, layout: LayoutBase) -> None:
self.renderables.append(layout)
self.global_children_dict.update(layout.global_children_dict)
- In this function, new layout is added as children component.
- As you can see,
global_children_dict
variable is updating the dictionary with the added new layout'sglobal_children_dict
variable. With this functionality, the parent class includes all the components that children layouts have. You can see in the_attach_event
function why this is important.
def render(self) -> None:
with self.main_layout:
for renderable in self.renderables:
renderable.render()
self.main_layout.render()
- In this function, components in the
renderables
are rendered under themain_layout
variable. with
functionality in thegradio.blocks.Block
is setting theContext.block
. This syntax is important because we are not rendering as we initialize the component but we render after withrender
function. So we have to set theContext.block
to render the component. If we did not use thewith
functionality then the components are rendered with the column style because the default style is the column style.- We render the
renderables
then we render themain_layout
because we have just rendered the children components, orrenderables
as their variable name, so we have to render the mainContext.block
as well.
def clear(self) -> None:
self.global_children_dict.clear()
for renderable in self.renderables:
if isinstance(renderable, LayoutBase):
renderable.clear()
self.renderables.clear()
- In this function, we clear the stored children components and renderables.
- We don't need them because the
gradio.blocks.Block
which is named asapp
in theApplication
class is storing the rendered components in theblocks
and attached functions in thefns
variables. - We choose the layouts from the renderables and call the
clear
function of them. With this we can be sure that all the objects that stored are cleaned.
def attach_event(self, block_dict: Dict[str, Block]) -> None:
raise NotImplementedError
- The
attach_event
function is leaved as not implemented because it is more specific to class so each class has to implement theirattach_event
function. - You can see what is the
block_dict
variable in the_attach_event
function which is implemented in theApplication
class.
- Base class for the application. You can add the layouts and launch the program.
Variable | Type | Definition |
---|---|---|
app | gradio.Blocks | Base application component from the gradio package. |
children | list[LayoutBase] | Stores the layouts |
- Passing the
add
function, because it is just adding the given layout to thechildren
variable.
def _render(self):
with self.app:
for child in self.children:
child.render()
self.app.render()
- As we can see from the
render
function from theLayoutBase
class, this_render
function is implemented as same as therender
function. - With this stairway design we can mimic the default implementation from the gradio docs.
def _attach_event(self):
block_dict: Dict[str, Block] = {}
for child in self.children:
block_dict.update(child.global_children_dict)
with self.app:
for child in self.children:
try:
child.attach_event(block_dict=block_dict)
except NotImplementedError:
print(f"{child.name}'s attach_event is not implemented")
- In this function, the components are gathered in one dictionary and passed to all children with
attach_event
function. - You can see why the
global_children_list
is used in theLayoutBase
class. With this, all of the components in the application is gathered into one dictionary so the component can access all the components with names which is used to insert into dictionary. - If the layout is not implent the
attach_event
function, the class prints a message with the name which is assigned in the class to inform the developer.
def _clear(self):
from gc import collect
for child in self.children:
child.clear()
self.children.clear()
collect()
- In this function, we completed the cleanup function.
- We call all the children with their
clear
function then clear thechildren
variable. After that callgc.collect
for memory saving.
launch
function is not interested one because it is just calling the_render
,_attach_event
and_clear
functions with thelaunch
function from thegradio.blocks.Blocks
class and starts the application.
- You can see the example in the
main.py
file.
- This is the layout of the example application which is taken from
gui.app.get_config_file()["layout"]
variable.
{'children': [{'children': [{'children': [{'children': [{'children': [{'id': 3},
{'id': 4}],
'id': 9}],
'id': 2}],
'id': 1},
{'children': [{'children': [{'children': [{'id': 7},
{'id': 8}],
'id': 10}],
'id': 6}],
'id': 5}],
'id': 11}],
'id': 0}
- You can see there are bunch of ids so here is the created components with their ids.
First Tab Main Layout ID : 1
First Tab Row Layout ID : 2
First Tab Left Textbox ID : 3
First Tab Right Textbox ID : 4
Second Tab Main Layout ID : 5
Second Tab Row Layout ID : 6
Second Tab Left Textbox ID : 7
Second Tab Right Textbox ID : 8
-
As you can see from the layout the components are structured as we wanted. But you can see there are additional ids which are 0, 9, 10 and 11.
-
The components can be checked with their ids from the
gui.app.blocks
variable. -
The
id : 0
is the main application which isgradio.blocks.Blocks
. -
The
id : 11
is thegradio.layouts.tabs.Tabs
class. This is inserted here because thegradio.layouts.tabs.Tab
is expecting a parent which isgradio.layouts.tabs.Tabs
class. This can be verified with thegradio.layouts.tabs.Tab.get_expected_parent
function. -
id : 9
andid : 10
are thegradio.layouts.form.Form
classes. These are inserted here because thegradio.components.textbox.Textbox
components are expecting a parent which isgradio.layouts.form.Form
class. This can be verified with theget_expected_parent
function from the Textbox component. If you want to see the real implementation, look at thegradio.components.base.FormComponent.get_expected_parent
function.