# Gradio 塊簡介

在前面的部分中, 我們已經使用 `Interface` 類探索並創建了演示。在本節中, 我們將介紹我們 **新開發**的稱為`gradio.Blocks`低級API。

現在, `接口`和`塊`之間有什麼區別?

- ⚡ `接口`: 一個高級 API, 讓你只需提供輸入和輸出列表即可創建完整的機器學習演示。

- 🧱 `塊`: :一個低級的 API, 它允許你完全控制你的應用程序的數據流和佈局。您可以使用`塊`(如 "構建塊")構建非常複雜的多步驟應用程序。

### 為什麼要塊 🧱?

正如我們在前幾節中看到的, `Interface` 類允許你使用幾行代碼輕鬆創建成熟的機器學習demo。`Interface` API 非常易於使用, 但缺乏 `Blocks` API 提供的靈活性。例如, 你可能想要:

- 將相關演示組合為一個web應用程序中的多個選項卡
- 更改demo的佈局, 例如指定輸入和輸出的位置
- 具有多步驟接口, 其中一個模型的輸出成為下一個模型的輸入, 或者通常具有更靈活的數據流
- 根據用戶輸入更改組件的屬性 (例如, 下拉列表中的選項) 或其可見性

我們將在下面探討所有這些概念。

### 使用塊創建簡單demo

安裝 Gradio 後, 將以下代碼作為 Python 腳本、Jupyter 筆記本或 Colab 筆記本運行。

```py
import gradio as gr

def flip_text(x):
    return x[::-1]

demo = gr.Blocks()

with demo:
    gr.Markdown(
        """
    # Flip Text!
    Start typing below to see the output.
    """
    )
    input = gr.Textbox(placeholder="Flip this text")
    output = gr.Textbox()

    input.change(fn=flip_text, inputs=input, outputs=output)

demo.launch()
```

上述簡單示例介紹了塊的4個基本概念:

1. 塊允許你允許你構建結合markdown、HTML、按鈕和交互組件的web應用程序, 只需在一個帶有gradio的Python中實例化對象。
> [!TIP]
> 🙋如果你不熟悉 Python 中的 `with` 語句, 我們建議你查看來自 Real Python 的極好的[教程](https://realpython.com/python-with-statement/)。看完後回到這裡 🤗
實例化組件的順序很重要, 因為每個元素都按照創建的順序呈現到 Web 應用程序中。(更復雜的佈局在下面討論)

2. 你可以在代碼中的任何位置定義常規 Python 函數, 並使用`塊`在用戶輸入的情況下運行它們。在我們的示例中, 們有一個"翻轉"輸入文本的簡單函數, 但你可以編寫任何 Python 函數, 從簡單的計算到處理機器學習模型的預測。

3. 你可以將事件指定給任何`塊`組件。這將在組件被單擊、更改等情況下運行函數。當你分配一個事件時, 你傳入三個參數: `fn`: 應該被調用的函數, `inputs`: 輸入組件的(列表), 以及 `outputs`: 應該被調用的輸出組件的(列表)。

   在上面的示例中, 當名為 `input` 的 `Textbox` 中的值發生變化時, 我們運行 `flip_text()` 函數。該事件讀取`輸入`中的值, 將其作為名稱參數傳遞給 `flip_text()`, 然後它返回一個值, 該值被分配給我們的第二個名為 `output` 的 `Textbox`。

   要查看每個組件支持的事件列表, 請參閱 Gradio [文檔](https://www.gradio.app/docs/)。

4. 塊會根據你定義的事件觸發器自動確定組件是否應該是交互式的 (接受用戶輸入)。在我們的示例中, 第一個文本框是交互式的, 因為它的值由 `flip_text()` 函數使用。第二個文本框不是交互式的, 因為它的值從不用作輸入。在某些情況下, 你可能想要覆蓋它, 你可以通過傳遞一個布爾值給組件的`交互`參數(例如 `gr.Textbox(placeholder="Flip this text", interactive=True)`)。

### 自定義演示的佈局

我們如何使用`塊`來定製我們的演示的佈局? 默認情況下, `塊`在一列中垂直呈現創建的組件。你可以通過使用 `with gradio.Column():` 創建其他列或使用 `with gradio.Row():` 創建其他行並在這些上下文中創建組件來改變這一點。

你應該記住: 在 `列` 下創建的任何組件(這也是默認設置) 都將垂直佈局。在 `Row` 下創建的任何組件都將水平佈局, 類似於 [Web 開發中的 flexbox 模型](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox)。

最後, 你還可以使用 `with gradio.Tabs()` 上下文管理器為您的demo創建選項卡。在此上下文中, 您可以通過使用 `gradio.TabItem(name_of_tab):` 指定來創建多個選項卡。在 `gradio.TabItem(name_of_tab):` 中創建的任何組件都會出現在該選項卡中。

現在讓我們在demo中添加一個 `flip_image()`函數並添加一個翻轉圖像的新選項卡。下面是一個帶有 2 個選項卡的示例, 也使用了一個行:

```py
import numpy as np
import gradio as gr

demo = gr.Blocks()

def flip_text(x):
    return x[::-1]

def flip_image(x):
    return np.fliplr(x)

with demo:
    gr.Markdown("Flip text or image files using this demo.")
    with gr.Tabs():
        with gr.TabItem("Flip Text"):
            with gr.Row():
                text_input = gr.Textbox()
                text_output = gr.Textbox()
            text_button = gr.Button("Flip")
        with gr.TabItem("Flip Image"):
            with gr.Row():
                image_input = gr.Image()
                image_output = gr.Image()
            image_button = gr.Button("Flip")

    text_button.click(flip_text, inputs=text_input, outputs=text_output)
    image_button.click(flip_image, inputs=image_input, outputs=image_output)

demo.launch()
```

你會注意到, 在這個示例中, 我們還在每個選項卡中創建了一個 `Button` 組件, 並且我們為每個按鈕分配了一個點擊事件,這是實際運行該函數的事件。

### 探索事件和狀態

正如你可以控制佈局一樣, `塊` 可以讓你對觸發函數調用的事件進行細粒度控制。每個組件和許多佈局都有它們支持的特定事件。

例如, `Textbox` 組件有兩個事件: `change()` (當文本框內的值發生變化時), 和 `submit()` (當用戶在關注文本框時按下enter鍵)。更復雜的組件可以有更多的事件: 例如,`Audio`組件也有單獨的事件, 用於播放、清除、暫停音頻文件等。請參閱文檔瞭解每個組件支持的事件。

你可以將事件觸發器附加到這些事件中的一個、一個或多個。你可以通過在組件實例中調用事件名稱作為函數來創建一個事件觸發器 -- 例如 `textbox.change(...)` 或 `btn.click(...)`。如前所述, 該函數接受三個參數:

- `fn`: 要運行的函數
- `inputs`: 組件的(列表), 其值應作為函數的輸入參數提供。每個組件的值按順序映射到相應的函數參數。如果函數不帶任何參數, 則此參數可以為 None。
- `outputs`: 應根據函數返回的值更新其值的組件(列表)。每個返回值按順序設置相應組件的值。如果函數不返回任何內容, 則此參數可以為None。

你甚至可以使輸入和輸出組件成為同一個組件, 就像我們在這個使用 GPT 模型進行文本補全的示例中所做的那樣:

```py
import gradio as gr

api = gr.Interface.load("huggingface/EleutherAI/gpt-j-6B")

def complete_with_gpt(text):
    # Use the last 50 characters of the text as context
    return text[:-50] + api(text[-50:])

with gr.Blocks() as demo:
    textbox = gr.Textbox(placeholder="Type here and press enter...", lines=4)
    btn = gr.Button("Generate")

    btn.click(complete_with_gpt, textbox, textbox)

demo.launch()
```

### 創建多步demo

在某些情況下, 您可能需要一個 _多步驟的demo_, 其中重用一個函數的輸出作為下一個函數的輸入。使用 `塊` 很容易做到這一點, 因為你可以使用組件作為一個事件觸發器的輸入, 但作為另一個事件觸發器的輸出。看看下面示例中的文本組件, 它的值是語音到文本模型的結果, 但也被傳遞到情感分析模型:

```py
from transformers import pipeline

import gradio as gr

asr = pipeline("automatic-speech-recognition", "facebook/wav2vec2-base-960h")
classifier = pipeline("text-classification")

def speech_to_text(speech):
    text = asr(speech)["text"]
    return text

def text_to_sentiment(text):
    return classifier(text)[0]["label"]

demo = gr.Blocks()

with demo:
    audio_file = gr.Audio(type="filepath")
    text = gr.Textbox()
    label = gr.Label()

    b1 = gr.Button("Recognize Speech")
    b2 = gr.Button("Classify Sentiment")

    b1.click(speech_to_text, inputs=audio_file, outputs=text)
    b2.click(text_to_sentiment, inputs=text, outputs=label)

demo.launch()
```

### 更新組件屬性

到目前為止, 我們已經瞭解瞭如何創建事件來更新另一個組件的值。但是, 如果您想更改組件的其他屬性, 例如文本框的可見性或單選按鈕組中的選項, 會發生什麼? 您可以通過返回組件類的 `update()` 方法而不是函數的常規返回值來做到這一點。

這很容易用一個例子來說明:

```py
import gradio as gr

def change_textbox(choice):
    if choice == "short":
        return gr.Textbox.update(lines=2, visible=True)
    elif choice == "long":
        return gr.Textbox.update(lines=8, visible=True)
    else:
        return gr.Textbox.update(visible=False)

with gr.Blocks() as block:
    radio = gr.Radio(
        ["short", "long", "none"], label="What kind of essay would you like to write?"
    )
    text = gr.Textbox(lines=2, interactive=True)

    radio.change(fn=change_textbox, inputs=radio, outputs=text)
    block.launch()
```

我們剛剛探索了`塊`的所有核心概念! 就像 `參數一樣`, 你可以創建很酷的demo, 可以通過在`launch()`方法中使用`share=True`來共享, 或者部署在[Hugging Face Spaces](https://huggingface.co/spaces)上。

