目次
Gradioを使うと簡単にwebアプリが作成できて便利ですよね。Gradioについて知らない方は下記の記事をご確認ください。
今回はGradioのレイアウトを改善できるBlocksについて紹介します。
GradioのBlocksとは
BlocksはGradioの低レベルAPIで、Webアプリケーションやデモを作成することができます。
GradioでWebアプリを作成する際には、基本的にはInterfaceを使っていると思いますが、Blocksを使うとより多機能なレイアウトが使えます。
Blocksでできること
(1) コンポーネントのレイアウト
(2) 関数の実行を引き起こすイベント
(3) データの流れ(例えば、入力が出力を引き起こし、それが次のレベルの出力を引き起こすことができる)。
また、Blocksでは、関連するデモをタブなどでグループ化することができます。
Blocksの基本的な使い方は、Blocksオブジェクトを作成し、それをコンテキストとして(with構文で)使用し、Blocksコンテキスト内でレイアウト、コンポーネント、イベントなどを定義します。最後に、launch()メソッドを呼び出すと、アプリが起動します。
今回は基本的なBlocksの使い方と、Blocksで使用できるレイアウトを紹介します。
InterfaceとBlocksの違い
基本的には簡易的に使用する場合は、Interfaceを使い、より複雑なレイアウトを作る場合はBlocksを使うと考えればわかりやすいです。
Interfaceのメリット
例えばInterfaceではショートカット文字列の使用が可能ですが、Blocksでは使えません。また、Interfaceでは基本的なボタンも自動で作成してくれます。したがって爆速でデモアプリを作る場合はInterfaceの方が楽です。
Blocksのメリット
一方でいくつかのレイアウトはBlocksでしか使えないですし、Blocksの方がレイアウトが複雑になった場合でもwith文で可読性の高いコードが書けます。したがってある程度UIを調整したい場合はBlocksを使うのがいいです。
個人的にはデフォルト設定で十分な場合はInterfaceを使い、それ以外はBlocksを使うのがベターだと思います。
Blocksの使い方
アプリケーションを定義するのにInterfaceの代わりにBlocksを使います。
Blocksはwith構文を使って定義します。
import gradio as gr
def greet(name):
    return "Hello " + name + "!"
# Blocksでappを定義
with gr.Blocks() as app:
    inputs = gr.Textbox(placeholder="名前を入力してね!", label="名前")
    outputs = gr.Textbox(label="挨拶")
    btn = gr.Button("クリックしてね!")
    # イベントを定義
    btn.click(fn=greet, inputs=inputs, outputs=outputs)
# launch()メソッドでWebアプリを起動
app.launch()
上記のコードを実行すると、名前を入力して「クリックしてね!」と書かれたボタンを押すと出力欄(挨拶)にHello ○○!と名前を書いてくれるアプリができます。

Blocksのデモアプリ
Blocksのレイアウト
先ほどのBlocksの例では、コンポーネントがデフォルトのレイアウトで表示されました。しかし、これではInterfaceを使った時と同じようなレイアウトでしかありません。
BlocksではInterfaceでは使えないいくつかのレイアウトを使うことができます。
今回はBlocksで使えるレイアウトを一つずつ紹介します。
Row
Rowはその名の通り、コンポーネントを横に並べることができます。
RowはBlockの中のレイアウト要素で、すべての子要素を水平にレンダリングします。
先ほどの例と同様に、with構文を使って定義します。
import gradio as gr
def greet(name):
    return "Hello " + name + "!"
with gr.Blocks() as app:
    # Rowでレイアウトを定義
    with gr.Row():
        inputs = gr.Textbox(placeholder="名前を入力してね!", label="名前")
        outputs = gr.Textbox(label="挨拶")
    btn = gr.Button("クリックしてね!")
    # btnにクリックイベントを追加
    btn.click(fn=greet, inputs=inputs, outputs=outputs)
app.launch()
上で作ったアプリと違って入力コンポーネントと出力コンポーネントが水平に並んでいるのが分かります。

Row()を使ったデモアプリ
Column
ColumnもBlocks のレイアウト要素のひとつで、すべての子要素を縦方向にレンダリングします。元々コンポーネントは縦に並んでいますが、Columnを使うと幅などを制御できます。
カラムの幅は scale と min_width パラメータで設定することができます。scaleにはintを指定し、同じレイアウト内にあるカラムの幅の倍率を調整できます(例: カラムAのscale=2、カラムBのscale=1のとき、カラムAはBの二倍の横幅になる)。min_widthはそのままですが、幅をpxで指定します。ちなみにscaleが min_width よりも狭いカラムになる場合はmin_width パラメータが優先されます。
import gradio as gr
def greet(name):
    return "Hello " + name + "!"
# Blocksでappを定義
with gr.Blocks() as app:
    # Column()でレイアウトを定義
    with gr.Row():
      with gr.Column(scale=2):
          inputs = gr.Textbox(placeholder="名前を入力してね!", label="名前")
          outputs = gr.Textbox(label="挨拶")
      with gr.Column(scale=1):
          btn = gr.Button("クリックしてね!")
    # btnにクリックイベントを追加
    btn.click(fn=greet, inputs=inputs, outputs=outputs)
app.launch()
上記のコードを実行したのが下記の画像ですが、ボタンの横幅が入出力のテキストボックスより短くなっていることがわかります(半分になっているはず)。

Column()のデモアプリ
Tab
タブを使うと、複数のコンポーネントをグループ化し、タブで分けることができます。Tab()の場合は、labelパラメータの指定が必須です。
import gradio as gr
def greet(name):
    return "Hello " + name + "!"
with gr.Blocks() as app:
    # 入力タブを定義
    with gr.Tab("入力タブ"):
        inputs = gr.Textbox(placeholder="名前を入力してね!", label="名前")
        btn = gr.Button("クリックしてね!")
    # 出力タブを定義
    with gr.Tab("出力タブ"):
        outputs = gr.Textbox(label="挨拶")
    btn.click(fn=greet, inputs=inputs, outputs=outputs)
app.launch()
上記のコードを動かしてみたのが下記の動画です。

Tab()のデモアプリ
タブをクリックすると別のタブの中身が表示されているのがわかります。
Group(旧:Box)
Group はレイアウト要素の一つで、角が丸く、周囲にマージンやパディングがないように子要素をまとめます。
ちょっと前のverではBox という名前のレイアウト要素の代わりになるものです。Box は角丸で周囲にパディングありのボックス内に子要素をまとめるものでしたが、レイアウト崩れがあったので改善されたのかもしれません。
import gradio as gr
def greet(name):
    return "Hello " + name + "!"
with gr.Blocks() as app:
    # Group()でレイアウトを定義
    with gr.Group():
        inputs = gr.Textbox(placeholder="名前を入力してね!", label="名前")
        outputs = gr.Textbox(label="挨拶")
        btn = gr.Button("クリックしてね!")
    # イベントを定義
    btn.click(fn=greet, inputs=inputs, outputs=outputs)
app.launch()
上記のコードを実行したのが下記の画像です。全てが一つにまとめられているのがわかるかと思います。
Boxだとボタンは小さくなってレイアウトが崩れるので、ボタンを使う場合はgradioのバージョンを上げてBoxではまとめない方が良いかもしれません。

Group()のデモアプリ
Accordion
Accordion はレイアウト要素の一つで、子要素を折りたたみ可能なセクションに配置します。
アコーディオンはwebページによく出てくるクリックすると下に情報が表示されるやつです。例として下にアコーディオンを表示しておきます。
アコーディオンの例
こいつです!
ちなみにAccordion()の場合もTab()と同様に、labelパラメータの指定が必須です。openパラメータもデフォルトで開いた状態になっているのでFalseがオススメ(というよりデフォルトでFalseにしてほしい)。
import gradio as gr
def greet(name):
    return "Hello " + name + "!"
with gr.Blocks() as app:
    # Accordion()でレイアウトを定義
    with gr.Accordion(label="アプリを見る", open=False):
        inputs = gr.Textbox(placeholder="名前を入力してね!", label="名前")
        outputs = gr.Textbox(label="挨拶")
    btn = gr.Button("クリックしてね!")
    # イベントを定義
    btn.click(fn=greet, inputs=inputs, outputs=outputs)
app.launch()
上記のコードを実行したのが下記の動画です。

Accordion()のデモアプリ
アコーディオンをクリックすると展開されて中身が表示されているのがわかります。
色々やるならInterfaceよりもBlocksがオススメ
GradioのBlocksでレイアウトを定義するとwebアプリケーションをより綺麗なレイアウトで作成することができます。
個人的にはBlocksを使うといろいろレイアウトできるので、よほど簡単なアプリを作る場合以外はInterfaceではなく基本Blocksを使うつもりです。
