gr.Blocks와 이벤트 리스너 — 레이아웃을 내 손으로

gr.Blocks와 이벤트 리스너 — 레이아웃을 내 손으로

gr.Interface는 함수 하나를 감싸는 가장 빠른 방법이라면, gr.Blocks는 그 이상을 원할 때 쓰는 도구예요. 입력-버튼-출력을 원하는 위치에 배치하고, '이 버튼을 누르면 이 함수가 실행돼'라는 연결(event listener)을 직접 지정할 수 있어요. 데모가 조금 복잡해지기 시작하면 여기로 넘어오게 돼요.

출처: https://www.gradio.app/guides/building-with-blocks

Blocks 구조

먼저 with gr.Blocks() as demo:라는 블록 안에 앱 코드를 담아요. 이 안에 컴포넌트들을 배치하면 그게 곧 화면이 돼요.

import gradio as gr

def greet(name):
    return "Hello " + name + "!"

with gr.Blocks() as demo:
    name = gr.Textbox(label="Name")
    output = gr.Textbox(label="Output Box")
    greet_btn = gr.Button("Greet")
    greet_btn.click(fn=greet, inputs=name, outputs=output, api_name="greet")

demo.launch()

이벤트 리스너로 상호작용 만들기

여기서 핵심은 .click(fn=greet, inputs=name, outputs=output, api_name="greet") 부분이에요. "greet_btn 버튼이 클릭되면, name 텍스트박스의 값을 greet 함수에 넣고, 그 결과를 output 텍스트박스에 써줘"라고 하는 거예요. api_name은 이 이벤트에 외부에서 접근할 API 이름을 붙여주는 옵션이에요.

이벤트 리스너는 종류가 여러 개예요. 버튼 클릭(click), 입력값이 바뀔 때(change), 컴포넌트를 선택했을 때(select), 마우스를 올렸을 때(hover) 같은 트리거를 상황에 맞게 골라 쓸 수 있어요.

여러 데이터 흐름

함수는 입력 목록과 출력 목록을 받아요. 입력이 여러 개면 함수의 파라미터 여러 개에 순서대로 들어가요.

def add(a, b):
    return a + b

with gr.Blocks() as demo:
    num1 = gr.Number()
    num2 = gr.Number()
    add_btn = gr.Button("Add")
    result = gr.Number()
    add_btn.click(add, inputs=[num1, num2], outputs=result)

demo.launch()

컴포넌트 값을 다른 컴포넌트 값의 함수로

"어떤 컴포넌트의 값이 항상 다른 컴포넌트 값들의 함수가 되도록" 하려면 이런 축약 문법을 써요.

with gr.Blocks() as demo:
  num1 = gr.Number()
  num2 = gr.Number()
  product = gr.Number(lambda a, b: a * b, inputs=[num1, num2])

이것은 num1·num2 값이 바뀔 때마다 product를 다시 계산해주는 것과 같은 동작이에요. gr.on으로 여러 트리거를 묶은 것과 동일하죠.

더 알아보기

Blocks의 레이아웃 제어(gr.Row, gr.Column)와 상태 관리(gr.State)는 공식 Controlling LayoutState In Blocks 가이드에서 이어서 볼 수 있어요.