Gradio Agent & MCP 黑客马拉松

获奖者
Gradio logo
  1. 使用 Blocks 构建
  2. 自定义CSS和JS

使用 CSS 和 JavaScript 自定义你的演示

Gradio 允许你通过多种方式自定义你的演示。你可以自定义演示的布局、添加自定义 HTML 以及添加自定义主题。本教程将在此基础上,引导你如何在演示中添加自定义 CSS 和 JavaScript 代码,以实现自定义样式、动画、自定义 UI 功能、分析等。

为你的演示添加自定义 CSS

Gradio 主题是自定义应用外观和感受最简单的方式。你可以从多种主题中选择,或创建自己的主题。为此,请将 theme= 关键字参数传递给 Blocks 构造函数。例如

with gr.Blocks(theme=gr.themes.Glass()):
    ...

Gradio 附带了一系列预构建主题,你可以从 gr.themes.* 加载。你可以扩展这些主题或从头开始创建自己的主题——详情请参阅主题指南

为了获得额外的样式能力,你可以使用 css= 关键字参数将任何 CSS 作为字符串传递给你的应用。你也可以将一个 pathlib.Path 指向一个 CSS 文件或一个此类路径列表传递给 css_paths= 关键字参数。

警告:由于 Gradio HTML DOM 可能会发生变化,自定义 JS 和 CSS 中查询选择器的使用保证在绑定到 Gradio 自身 HTML 元素的 Gradio 版本之间兼容。我们建议谨慎使用查询选择器。

Gradio 应用的基类是 gradio-container,这里有一个改变 Gradio 应用背景颜色的示例:

with gr.Blocks(css=".gradio-container {background-color: red}") as demo:
    ...

如果你想在 CSS 中引用外部文件,请在文件路径(可以是相对路径或绝对路径)前加上 "/gradio_api/file=",例如

with gr.Blocks(css=".gradio-container {background: url('/gradio_api/file=clouds.jpg')}") as demo:
    ...

注意:默认情况下,主机上的大多数文件对运行 Gradio 应用的用户不可访问。因此,你应该确保任何引用的文件(例如此处的 clouds.jpg)是 URL 或 此处所述的允许路径

elem_idelem_classes 参数

你可以使用 elem_id 为任何组件添加 HTML 元素 id,并使用 elem_classes 添加一个或多个类。这将让你能够更轻松地使用 CSS 选择元素。这种方法也更有可能在 Gradio 版本之间保持稳定,因为内置的类名或 ID 可能会发生变化(然而,如上文警告所述,如果你使用自定义 CSS,我们无法保证 Gradio 版本之间的完全兼容性,因为 DOM 元素本身可能会改变)。

css = """
#warning {background-color: #FFCCCB}
.feedback textarea {font-size: 24px !important}
"""

with gr.Blocks(css=css) as demo:
    box1 = gr.Textbox(value="Good Job", elem_classes="feedback")
    box2 = gr.Textbox(value="Failure", elem_id="warning", elem_classes="feedback")

CSS 的 #warning 规则集只会针对第二个文本框,而 .feedback 规则集会同时针对两者。请注意,在针对类时,你可能需要使用 !important 选择器来覆盖 Gradio 的默认样式。

为你的演示添加自定义 JavaScript

有 3 种方式可以为你的 Gradio 演示添加 JavaScript 代码

  1. 你可以将 JavaScript 代码作为字符串添加到 BlocksInterface 初始化器的 js 参数中。这将在演示首次加载时运行 JavaScript 代码。

下面是一个添加自定义 JS 的示例,用于在演示首次加载时显示动画欢迎消息。

import gradio as gr

def welcome(name):
    return f"Welcome to Gradio, {name}!"

js = """
function createGradioAnimation() {
    var container = document.createElement('div');
    container.id = 'gradio-animation';
    container.style.fontSize = '2em';
    container.style.fontWeight = 'bold';
    container.style.textAlign = 'center';
    container.style.marginBottom = '20px';

    var text = 'Welcome to Gradio!';
    for (var i = 0; i < text.length; i++) {
        (function(i){
            setTimeout(function(){
                var letter = document.createElement('span');
                letter.style.opacity = '0';
                letter.style.transition = 'opacity 0.5s';
                letter.innerText = text[i];

                container.appendChild(letter);

                setTimeout(function() {
                    letter.style.opacity = '1';
                }, 50);
            }, i * 250);
        })(i);
    }

    var gradioContainer = document.querySelector('.gradio-container');
    gradioContainer.insertBefore(container, gradioContainer.firstChild);

    return 'Animation created';
}
"""
with gr.Blocks(js=js) as demo:
    inp = gr.Textbox(placeholder="What is your name?")
    out = gr.Textbox()
    inp.change(welcome, inp, out)

demo.launch()

  1. 当使用 Blocks 和事件监听器时,事件有一个 js 参数,它可以接收一个 JavaScript 函数作为字符串,并将其视为一个 Python 事件监听器函数。你可以同时传递一个 JavaScript 函数和一个 Python 函数(这种情况下 JavaScript 函数会首先运行),或者只传递 JavaScript 函数(并将 Python fn 设置为 None)。请看下面的代码
import gradio as gr

blocks = gr.Blocks()

with blocks as demo:
    subject = gr.Textbox(placeholder="subject")
    verb = gr.Radio(["ate", "loved", "hated"])
    object = gr.Textbox(placeholder="object")

    with gr.Row():
        btn = gr.Button("Create sentence.")
        reverse_btn = gr.Button("Reverse sentence.")
        foo_bar_btn = gr.Button("Append foo")
        reverse_then_to_the_server_btn = gr.Button(
            "Reverse sentence and send to server."
        )

    def sentence_maker(w1, w2, w3):
        return f"{w1} {w2} {w3}"

    output1 = gr.Textbox(label="output 1")
    output2 = gr.Textbox(label="verb")
    output3 = gr.Textbox(label="verb reversed")
    output4 = gr.Textbox(label="front end process and then send to backend")

    btn.click(sentence_maker, [subject, verb, object], output1)
    reverse_btn.click(
        None, [subject, verb, object], output2, js="(s, v, o) => o + ' ' + v + ' ' + s"
    )
    verb.change(lambda x: x, verb, output3, js="(x) => [...x].reverse().join('')")
    foo_bar_btn.click(None, [], subject, js="(x) => x + ' foo'")

    reverse_then_to_the_server_btn.click(
        sentence_maker,
        [subject, verb, object],
        output4,
        js="(s, v, o) => [s, v, o].map(x => [...x].reverse().join(''))",
    )

demo.launch()

  1. 最后,你可以将 JavaScript 代码添加到 Blocks 初始化器的 head 参数中。这会将代码添加到 HTML 文档的头部。例如,你可以像这样将 Google Analytics 添加到你的演示中
head = f"""
<script async src="https://#/gtag/js?id={google_analytics_tracking_id}"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){{dataLayer.push(arguments);}}
  gtag('js', new Date());
  gtag('config', '{google_analytics_tracking_id}');
</script>
"""

with gr.Blocks(head=head) as demo:
    gr.HTML("<h1>My App</h1>")

demo.launch()

head 参数接受你通常会插入到页面 <head> 中的任何 HTML 标签。例如,你也可以在 head 中包含 <meta> 标签,以便像这样更新你的 Gradio 应用的社交分享预览:

import gradio as gr

custom_head = """
<!-- HTML Meta Tags -->
<title>Sample App</title>
<meta name="description" content="An open-source web application showcasing various features and capabilities.">

<!-- Facebook Meta Tags -->
<meta property="og:url" content="https://example.com">
<meta property="og:type" content="website">
<meta property="og:title" content="Sample App">
<meta property="og:description" content="An open-source web application showcasing various features and capabilities.">
<meta property="og:image" content="https://cdn.britannica.com/98/152298-050-8E45510A/Cheetah.jpg">

<!-- Twitter Meta Tags -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:creator" content="@example_user">
<meta name="twitter:title" content="Sample App">
<meta name="twitter:description" content="An open-source web application showcasing various features and capabilities.">
<meta name="twitter:image" content="https://cdn.britannica.com/98/152298-050-8E45510A/Cheetah.jpg">
<meta property="twitter:domain" content="example.com">
<meta property="twitter:url" content="https://example.com">  
"""

with gr.Blocks(title="My App", head=custom_head) as demo:
    gr.HTML("<h1>My App</h1>")

demo.launch()

请注意,注入自定义 JS 可能会影响浏览器行为和可访问性(例如,如果你的 Gradio 应用嵌入在另一个网页中,键盘快捷键可能会导致意外行为)。你应该在不同的浏览器中测试你的界面,并注意脚本如何与浏览器默认设置交互。下面是一个示例,如果浏览器焦点**不在**输入组件(例如 Textbox 组件)上,按下 Shift + s 会触发特定 Button 组件的 click 事件:

import gradio as gr

shortcut_js = """
<script>
function shortcuts(e) {
    var event = document.all ? window.event : e;
    switch (e.target.tagName.toLowerCase()) {
        case "input":
        case "textarea":
        break;
        default:
        if (e.key.toLowerCase() == "s" && e.shiftKey) {
            document.getElementById("my_btn").click();
        }
    }
}
document.addEventListener('keypress', shortcuts, false);
</script>
"""

with gr.Blocks(head=shortcut_js) as demo:
    action_button = gr.Button(value="Name", elem_id="my_btn")
    textbox = gr.Textbox()
    action_button.click(lambda : "button pressed", None, textbox)
    
demo.launch()