jsp 网站连接数据库,学编程入门先学什么,做网站网页兼容性,ios 常用网站全网最详细Gradio教程系列10——Blocks#xff1a;底层区块类#xff08;下#xff09; 前言本篇摘要10. Blocks#xff1a;底层区块类10.4 Blocks Layout#xff1a;布局10.4.1 行与列1. Rows2. Columns 10.4.2 选项卡和折叠类10.4.3 重渲染.render()10.4.4 Group分组10.… 全网最详细Gradio教程系列10——Blocks底层区块类下 前言本篇摘要10. Blocks底层区块类10.4 Blocks Layout布局10.4.1 行与列1. Rows2. Columns 10.4.2 选项卡和折叠类10.4.3 重渲染.render()10.4.4 Group分组10.4.4 其它1. 填充浏览器的高与宽2. Visibility可见性 10.5 动态渲染render()10.5.1 动态参数10.5.2 动态事件监听器10.5.3 将两者结合应用1. 待办事项列表2. 混音器 10.6 定制demo的theme、CSS和JS10.6.1 自定义主题theme10.6.2 自定义CSS1. 参数css2. 参数elem_id与elem_classes 10.6.3 自定义JavaScript1. Blocks/Interface构造函数的js参数2. 事件监听器的js参数3. Blocks初始化的head参数 10.7 将Gradio Blocks用作函数10.7.1 gr.load()加载demo10.7.2 如何指定demo中函数1. api_name指定2. fn_index指定 参考文献 前言
本系列文章主要介绍WEB界面工具Gradio。Gradio是Hugging Face发布的一个简易的webui开发框架它基于FastAPI和svelte便于部署人工智能相关模型是当前热门的非常易于开发和展示机器学习大语言模型LLM及扩散模型DM的UI框架。本系列文章分为前置概念和实战演练两部分。前置概念先介绍Gradio的详细技术架构、历史、应用场景、与其他框架Gradio/NiceGui/StreamLit/Dash/PyWebIO的区别然后详细介绍了大模型及数据的资源网站Hugging Face包括三种资源models/datasets/spaces、六类开源库transformers/diffusers/datasets/PEFT/accelerate/optimum实战及Course课程资源。实战演练部分先讲解了多种不同的安装、运行和部署方式安装包括Linux/Win/Mac三种安装方式运行包括普通方式运行和热重载方式运行两种运行方式部署包括本地部署、HuggingFace托管、FastAPI挂载和Gradio-Lite浏览器集成然后按照先整体再细节的逻辑讲解Gradio的多种高级特性三种Gradio Clientspython/javascript/curl、Gradio Tools等方便读者对Gradio整体把握最后深入细节也是本系列文章的核心实践基础功能Interface、Blocks和Additional Features高级功能Chatbots、Data Science And Plots、Streaming和Custom Components。本系列文章注解详细代码均可运行并附有大量运行截图方便读者理解Gradio一定会成为每个技术人员实现奇思妙想的最称手工具。
本系列文章目录如下
《全网最详细Gradio教程系列1——Gradio简介》《全网最详细Gradio教程系列2——Gradio的安装与运行》《全网最详细Gradio教程系列3——Gradio的31种部署方式实践》《全网最详细Gradio教程系列4——浏览器集成Gradio-Lite》《全网最详细Gradio教程系列5——Gradio Clientpython客户端》《全网最详细Gradio教程系列5——Gradio Clientjavascript客户端》《全网最详细Gradio教程系列5——Gradio Clientcurl客户端》《全网最详细Gradio教程系列6——Gradio Tools将Gradio用于LLM Agents》《全网最详细Gradio教程系列7——Data Science And Plots数据科学与绘图》《全网最详细Gradio教程系列8——Gradio库的模块架构和环境变量》《全网最详细Gradio教程系列9——Interface高级抽象界面类上》《全网最详细Gradio教程系列9——Interface高级抽象界面类下》《全网最详细Gradio教程系列——Blocks底层区块类上》《全网最详细Gradio教程系列——Blocks底层区块类下》《全网最详细Gradio教程系列——Custom Components》
本篇摘要
本篇介绍Gradio的底层区块类Blocks。按照惯例先进行Blocks类详解包括示例讲解、API参数和成员函数由于其中五个成员函数和Interface类一模一样所以参照前面的成员函数即可这里不再重复。然后进行实践实践部分内容较多主要包括Blocks基础操作、高级特性、控制布局、动态渲染、定制theme/CSS/JS及将Blocks用作函数下面逐一讲述。
10. Blocks底层区块类
本章开始介绍Gradio的底层类Blocks即区块类Blocks是Gradio的重中之重请读者务必理解并进行演练。Blocks实现了多种界面的定制化功能但仍完全基于Python。相比Interface类Blocks提供了更大的灵活性和可控性。本章概括总结Blocks的大部分知识包括Blocks类详解、Blocks基础操作、高级特性、控制布局、动态渲染、定制theme/CSS/JS及将Blocks用作函数下面逐一讲述。
10.4 Blocks Layout布局
Blocks Layout组件对应Blocks布局模块中元素。Blocks中的组件默认以垂直方向排列那么如何重新排列组件呢在Blocks驱动中用web开发中的flexbox模块实现重新布局。弹性盒Flexbox是一种CSS布局模型旨在为网页提供灵活的、自适应的排列方式。它通过定义容器和内部项目的行为使得页面元素能够以可预测的方式在容器中进行排列和分布。
10.4.1 行与列
1. Rows
默认情况下在“with gr.Row():“语句中的元素都将水平显示行中元素的高度通过Row参数height和equal_height设置而行中元素的宽度则可以通过每个组件中存在的参数scale和min_width的组合来控制
height行的高度如果传递数字则以像素为单位指定如果传递字符串则以该CSS单位的尺寸直接应用于封装块元素如视口宽度viewport widthvw。如果内容超过高度则该行将垂直滚动。如果未设置该行将展开以适应内容。equal_height布尔类型为True时表示行中的每个元素高度相同scale 整数类型它定义了元素如何占用Row中的空间。如果scale设置为0则元素不会扩展以占用空间。如果scale设置为1或更大则一行中的多个元素将按scale扩展。比如代码演示中btn2将膨胀到btn1的两倍而btn0则不会膨胀min_width min_width将设置元素的最小宽度。如果没有足够的空间来满足所有min_width值则Row将换行。size 元素大小可以是sm或lg。
有关Row和组件的更多信息请参考其官方文档示例演示如下
with gr.Blocks() as demo:with gr.Row(equal_heightTrue):btn0 gr.Button(Button 0, scale0)btn1 gr.Button(Button 1, scale1)btn2 gr.Button(Button 2, scale2)运行截图如下
2. Columns
每列内的组件将从顶部依次垂直放置。由于垂直布局是Blocks程序的默认布局为了便于使用列通常嵌套在行中即嵌套列演示如下
import gradio as grwith gr.Blocks() as demo:with gr.Row():text1 gr.Textbox(labelt1)slider2 gr.Textbox(labels2)drop3 gr.Dropdown([a, b, c], labeld3)with gr.Row():with gr.Column(scale1, min_width300):text1 gr.Textbox(labelprompt 1)text2 gr.Textbox(labelprompt 2)inbtw gr.Button(Between)text4 gr.Textbox(labelprompt 1)text5 gr.Textbox(labelprompt 2)with gr.Column(scale2, min_width300):img1 gr.Image(images/cheetah.jpg, width50vw)btn gr.Button(Go)demo.launch()运行截图如下 请注意第二行如何排列两列中的元素第一列垂直排列各元素第二列垂直排列图像和按钮。另外请留意两列的相对宽度如何通过参数scale设置即具有两倍scale值的列占据两倍宽度。
10.4.2 选项卡和折叠类
通过子句“with gr.Tab(‘tab_name’):”可以创建选项卡Tab在其上下文中创建的任何组件都会显示在该选项卡页面Tab中。当多个Tab子句被分在一组时一次只能选择单个Tab并且只显示该Tab上下文中的组件。示例如下
import numpy as np
import gradio as grdef flip_text(x):return x[::-1]def flip_image(x):return np.fliplr(x)with gr.Blocks() as demo:gr.Markdown(Flip text or image files using this demo.)with gr.Tab(Flip Text):text_input gr.Textbox()text_output gr.Textbox()text_button gr.Button(Flip)with gr.Tab(Flip Image):with gr.Row():image_input gr.Image()image_output gr.Image()image_button gr.Button(Flip)with gr.Accordion(Open for More!, openFalse):gr.Markdown(Look at me...)temp_slider gr.Slider(0, 1,value0.1,step0.1,interactiveTrue,labelSlide me,)text_button.click(flip_text, inputstext_input, outputstext_output)image_button.click(flip_image, inputsimage_input, outputsimage_output)demo.launch()运行界面如下 点击Flip Image切换到另一Tab 注意示例中的gr.Accordion(‘label’)Accordion折叠类是可以点击以打开或关闭的布局组件以选择性的显示或隐藏内容。在前面Interface中已详细解释过它的用法这里只有少许不同请仔细甄别这里不再赘述。
10.4.3 重渲染.render()
在某些情况下需要分别定义和渲染组件也就是先定义组件然后在UI中再实际渲染它。比如下面的例子如何在相应输入gr.Textbox的上方使用gr.Examples显示示例部分
由于gr.Examples需要输入组件对象作为参数所以需要先定义输入组件然后在定义gr.Examples对象后再渲染输入组件。解决办法是在gr.Blocks() 作用域外定义gr.Textbox然后在UI的任意位置使用组件的.render()方法重新渲染即可。下面是完整的代码示例
input_textbox gr.Textbox()with gr.Blocks() as demo:gr.Examples([hello, bonjour, merhaba], input_textbox)input_textbox.render()运行截图如下
10.4.4 Group分组
Group是Blocks中的一个布局元素它将子元素组合在一起使它们之间没有任何填充或边距。调用形式为with gr.Group():。它的API参数主要有elem_id、elem_classes、visible和render有关elem_id、elem_classes和visible请参照后边的讲解。这里的参数render用法与事件监听器.render()及装饰器render()均不同请仔细区分。
示例用法
import gradio as grwith gr.Blocks() as demo:with gr.Group():gr.Textbox(labelFirst)gr.Textbox(labelSecond)gr.Textbox(labelLast)demo.launch()运行截图如下
10.4.4 其它
1. 填充浏览器的高与宽
如果希望通过删除侧边填充以使应用程序占据浏览器的整个宽度可以使用gr.Blocks(fill_widthTrue)。而如果希望顶级组件展开以占据浏览器的整个高度可以使用gr.Blocks(fill_heightTrue)并可以对展开的组件应用设置参数scale。示例如下
import gradio as grwith gr.Blocks(fill_widthTrue, fill_heightTrue) as demo:gr.Chatbot(scale1)gr.Textbox(scale0)这时整个浏览器都会被这两个组件填满如图
2. Visibility可见性
Components组件和Layout布局元素都有一个可见参数visible可以进行初始设置和更新。在列上设置gr.Columnvisible…可用于显示或隐藏一组组件示例如下
import gradio as grwith gr.Blocks() as demo:name_box gr.Textbox(labelName)age_box gr.Number(labelAge, minimum0, maximum100)symptoms_box gr.CheckboxGroup([Cough, Fever, Runny Nose])submit_btn gr.Button(Submit)with gr.Column(visibleFalse) as output_col:diagnosis_box gr.Textbox(labelDiagnosis)patient_summary_box gr.Textbox(labelPatient Summary)def submit(name, age, symptoms):return {submit_btn: gr.Button(visibleFalse),output_col: gr.Column(visibleTrue),diagnosis_box: covid if Cough in symptoms else flu,patient_summary_box: f{name}, {age} y/o,}submit_btn.click(submit,[name_box, age_box, symptoms_box],[submit_btn, diagnosis_box, patient_summary_box, output_col],)demo.launch()运行截图如下 当填充名字、年龄和选择症状后点击提交会隐藏按钮Submit并显示诊断结果
10.5 动态渲染render()
到目前为止的演示中Blocks中定义的组件和事件监听器都已经固定——一旦启动demo就无法添加新的组件和监听器也无法删除现有的组件和监听器。但是装饰器gr.render的引入可以动态修改它们下面让我们看看如何实现。
10.5.1 动态参数
在下面的示例中我们将创建可变数量的文本框。当用户编辑输入文本框时我们会为输入的每个字母创建一个文本框。代码如下
import gradio as grwith gr.Blocks() as demo:input_text gr.Textbox(labelinput)gr.render(inputsinput_text)def show_split(text):if len(text) 0:gr.Markdown(## No Input Provided)else:for letter in text:gr.Textbox(letter)demo.launch()运行截图如下 如何使用自定义逻辑创建可变数量的文本框呢在本例中只需一个简单的for循环。而gr.render装饰器通过以下步骤实现此功能
创建一个函数并将gr.render装饰符附加到该函数上。将输入组件添加到gr.render的参数inputs中并在函数中分割输入并创建对应参数。此功能将在输入发生更改时自动重新运行。根据输入在函数内添加所有要渲染的组件。
现在每当输入发生变化时函数都会重新运行并用最新运行结果替换上一次函数运行时创建的组件相当直接现在为这个应用程序增加一点复杂性如下
import gradio as grwith gr.Blocks() as demo:input_text gr.Textbox(labelinput)mode gr.Radio([textbox, button], valuetextbox)gr.render(inputs[input_text, mode], triggers[input_text.submit])def show_split(text, mode):if len(text) 0:gr.Markdown(## No Input Provided)else:for letter in text:if mode textbox:gr.Textbox(letter)else:gr.Button(letter)demo.launch()当选择添加按钮组件时运行截图如下 当选择添加文本框时运行截图如下 默认情况下gr.render的重运行是由应用程序的.load监听器或任意输入组件的.change监听器触发的。我们还可以通过在装饰器中显式设置触发器来覆盖它们比如本例中设置只在input_text.submit上触发。在设置自定义触发器时如果希望在应用程序启动时自动渲染请将demo.load添加到触发器列表中。
10.5.2 动态事件监听器
当创建组件时可能会想给组件添加事件监听器比如下面的演示它接收可变数量的文本框作为输入并将所有文本合并到一个框中。代码如下
import gradio as grwith gr.Blocks() as demo:text_count gr.State(1)add_btn gr.Button(Add Box)add_btn.click(lambda x: x 1, text_count, text_count)gr.render(inputstext_count)def render_count(count):boxes []for i in range(count):box gr.Textbox(keyi, labelfBox {i})boxes.append(box)def merge(*args):return .join(args)merge_btn.click(merge, boxes, output)merge_btn gr.Button(Merge)output gr.Textbox(labelMerged Output)demo.launch()运行截图如下 点击“Add Box”增添一个文本框分别输入文字后点击Merge将合并文本框内容如下图 下面对代码进行解析
状态变量text_count跟踪要创建的文本框的数量。通过单击Add按钮增加了text_count从而触发render装饰器创建新文本框请注意在函数render创建的每个Textbox中我们都显式设置了一个参数key它的作用是当组件被重新渲染时保留值不变。即如果在文本框中键入值然后单击“添加”按钮时所有文本框都会被重新渲染但它们的值不会被清除因为键key在渲染过程中会保持组件的值。我们已将创建的文本框存储在列表boxes中并将此列表作为merge按钮事件侦听器的输入。请注意1当事件侦听器需要使用渲染函数内创建的组件或参数时则这些事件侦听器及对应处理函数如.click()也必须在该渲染函数内定义2事件侦听器仍然可以引用渲染函数外的组件就像本例引用merge_btn和output一样这两个组件都是在渲染函数外定义的。
与Components一样每当函数重新渲染时在之前render中创建的事件监听器都会被清除并附加最新render的事件监听器。动态事件监听器可以使我们创建高度可定制和超复杂的交互
10.5.3 将两者结合应用
现在让我们看看如何结合上述的两个动态功能即动态参数和动态事件监听器这里举两个示例。
1. 待办事项列表
首先试试下面的待办事项列表应用程序
import gradio as grwith gr.Blocks() as demo:tasks gr.State([])new_task gr.Textbox(labelTask Name, autofocusTrue)def add_task(tasks, new_task_name):return tasks [{name: new_task_name, complete: False}], new_task.submit(add_task, [tasks, new_task], [tasks, new_task])gr.render(inputstasks)def render_todos(task_list):complete [task for task in task_list if task[complete]]incomplete [task for task in task_list if not task[complete]]gr.Markdown(f### Incomplete Tasks ({len(incomplete)}))for task in incomplete:with gr.Row():gr.Textbox(task[name], show_labelFalse, containerFalse)done_btn gr.Button(Done, scale0)def mark_done(tasktask):task[complete] Truereturn task_listdone_btn.click(mark_done, None, [tasks])delete_btn gr.Button(Delete, scale0, variantstop)def delete(tasktask):task_list.remove(task)return task_listdelete_btn.click(delete, None, [tasks])gr.Markdown(f### Complete Tasks ({len(complete)}))for task in complete:gr.Textbox(task[name], show_labelFalse, containerFalse)demo.launch()添加5个任务以动态调整组件完成其中两个并删除一个以实现动态事件监听器最终结果如下 请注意几乎整个应用程序都在一个gr.render中该gr.render对任务的gr.State变量做出反应。这个变量是一个嵌套列表这会有一些复杂在设计gr.render来响应列表或字典结构时请确保执行以下操作
当以触发重渲染的方式修改状态变量时事件监听器必须将状态变量设置为输出这会让Gradio知悉状态变量是否在幕后发生了变化在gr.render中如果事件监听器函数中使用了可能被其它函数操作的循环时loop-time变量则该变量应该在函数头部中通过以默认参数方式将它设置为自身以进行frozen冻结防止该变量在函数运行中被其它函数篡改但可以被监听器函数操作。比如函数mark_done和delete中的tasktask这会将变量在循环时间内被冻结只允许本函数操作。
2. 混音器
让我们来看最后一个例子混音器它使用了我们学到的大部分知识它可以将多个音轨混合在一起代码如下
import gradio as grwith gr.Blocks() as demo:track_count gr.State(1)add_track_btn gr.Button(Add Track)add_track_btn.click(lambda count: count 1, track_count, track_count)gr.render(inputstrack_count)def render_tracks(count):names []audios []volumes []with gr.Row():for i in range(count):with gr.Column(variantpanel, min_width200):track_name gr.Textbox(placeholderAudio Name, keyfname-{i}, show_labelFalse)track_audio gr.Audio(labelfAudio {i}, keyfaudio-{i})track_volume gr.Slider(0, 100, value100, labelVolume {i}, keyfvolume-{i})names.append(track_name)audios.append(track_audio)volumes.append(track_volume)def merge(data):name_output, output None, Nonefor name, audio, volume in zip(srs, audios, volumes):name_val data[name]audio_val data[audio]volume_val data[volume]final_track audio_val * (volume_val / 100)name_output name_valif output is None:output final_trackelse:min_shape tuple(min(s1, s2) for s1, s2 in zip(output.shape, final_track.shape))trimmed_output output[:min_shape[0], ...][:, :min_shape[1], ...] if output.ndim 1 else output[:min_shape[0]]trimmed_final final_track[:min_shape[0], ...][:, :min_shape[1], ...] if final_track.ndim 1 else final_track[:min_shape[0]]output trimmed_output trimmed_finalreturn name_output, outputmerge_btn.click(merge, set(names audios volumes), [output_text, output_audio])merge_btn gr.Button(Merge Tracks)output_text gr.Textbox(labelOutput, interactiveFalse)output_audio gr.Audio(labelOutput, interactiveFalse)demo.launch()添加一次Track并上传两段音频然后滑动Volume最后点击Merge Tracks生成合成音频运行截图如下 在这个应用程序中要注意两点
程序中所有组件均提供参数key当为现有音轨设置值后再添加其它音轨时对现有音轨的输入值在重新渲染时不会被重置当有任意类型和数量的组件被传递给事件监听器时使用集合和字典表示法作为输入比使用列表表示法更容易。本例中我们把所有输入的gr.Textbox、gr.Audio和gr.Slider组件组成一个大集合传递给合并函数而在函数体中通过字典查询组件值。
gr.render极大扩展了gradio功能——试试它能为你做些什么吧
10.6 定制demo的theme、CSS和JS
在Gradio内可以使用多种方式定制demo比如定制demo的布局、添加自定义HTML或自定义主题。本节将探讨如何通过添加自定义的CSS和JavaScript代码将自定义样式、动画、功能性UI或分析等添加到demo中。
10.6.1 自定义主题theme
Gradio主题是改变应用程序观感最简单的办法即可以从各种主体中选择也可以创建自己的主题。应用时只需将主题传递给Blocks构造函数的参数theme即可如下
with gr.Blocks(themegr.themes.Glass()):Gradio附带了一组预构建的主题可以通过gr.themes.*加载也可以扩展这些主题或从头开始创建自己的主题。有关主题的更多详细信息请参阅官方theming-guide。
10.6.2 自定义CSS
这里介绍两种添加自定义的CSS样式的方法参数css和参数elem_id/elem_classes。
1. 参数css
为了呈现更多样式可以将CSS文件或CSS代码传递给Blocks构造函数的参数CSS。警告在自定义JS和CSS中使用查询选择器querySelector时由于Gradio HTML DOM可能发生变化因此并不能保证绑定到Gradio的HTML元素在不同版本的Gradio中正常工作所以建议谨慎使用查询选择器。
由于Gradio应用程序的基类是gradio-container所以可以这样更改Gradio应用的背景颜色或背景图片
# 更换背景颜色
with gr.Blocks(css.gradio-container {background-color: red}) as demo:
# 更换背景图片
with gr.Blocks(css.gradio-container {background: url(fileclouds.jpg)}) as demo:在css中引用外部文件需在文件路径前加上“file”文件路径可以是相对或绝对路径如本例与python同目录的clouds.jpg就采用了相对路径。另外需注意默认情况下运行Gradio应用程序的主机中的文件无法被用户访问因此应该确保任何引用的文件都是URL或者在函数launch()中的参数allow_list参数目录中。关于这部分请参照下一章的安全访问文件。
2. 参数elem_id与elem_classes
另一种添加自定义css样式的方式是通过参数elem_id与elem_classes大部分组件都具备这两个参数详细说明如下
elem_id一个可选字符串在HTML DOM中指定为此组件的id可用于定位CSS样式elem_classes一个可选的字符串或字符串列表在HTML DOM中被指定为该组件的类可用于定位CSS样式。
参数elem_id可以向任意组件添加HTML元素的IDelem_classes可以添加元素类或类列表通过它们可以轻松的使用CSS选择元素。由于Gradio内置的id或类名可能发生变化所以这种方法更有可能让界面在不同版Gradio之间保持稳定。但就像之前提过的那样由于DOM元素本身可能发生变化所以自定义的CSS可能在不同Gradio版本之间产生兼容性问题如何取舍请读者根据自身需求选择。
下面看一个使用参数elem_id/elem_classes的演示
css
.gradio-container {background-color: red}
#warning {background-color: #FFCCCB}
.feedback textarea {font-size: 24px !important}
with gr.Blocks(csscss) as demo:box1 gr.Textbox(valueGood Job, elem_classesfeedback)box2 gr.Textbox(valueFailure, elem_idwarning, elem_classesfeedback)CSS的#warning规则集将仅针对第二个文本框而.feedback规则集将同时针对这两个文本框。请注意在指定类时可能需要使用“!important”选择器覆盖默认的Gradio样式。
运行截图如下
10.6.3 自定义JavaScript
有三种方法可以将javascript代码添加到Gradio演示中Blocks/Interface构造函数的js参数、事件监听器的js参数和Blocks初始化的head参数下面逐一讲述。
1. Blocks/Interface构造函数的js参数
通过字符串或文件路径将JavaScript代码添加到Blocks或Interface构造器的js参数中demo在首次加载时将运行JavaScript代码。
下面是一个添加自定义js的示例它在演示首次加载时动态显示欢迎消息
import gradio as grdef hello(name):return fhello, {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(jsjs) as demo:inp gr.Textbox(placeholderWhat is your name?)out gr.Textbox()inp.change(hello, inp, out)demo.launch()运行截图如下 这里也可以通过文件路径的方式传递js代码比如Python脚本的同一目录中有一个名为custom.js的文件可以这样将它添加到demo中with gr.Blocks(js“custom.js”) as demo:如果在界面中gr.Interface(…, js“custom.js”)。
2. 事件监听器的js参数
当使用Blocks或Interface的事件监听器时它有一个js参数可以将JavaScript函数视为字符串并将其当作Python事件监听器函数使用。这时我们可以同时通过js传递JavaScript函数和通过fn传递Python函数但在这种情况下首先运行JavaScript函数也可以只传递JavaScript此时只需将Python的fn设置为None。
请看下面的演示
import gradio as grblocks gr.Blocks()with blocks as demo:subject gr.Textbox(placeholdersubject)verb gr.Radio([ate, loved, hated, detah, devol, eta])object gr.Textbox(placeholderobject)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.)output1 gr.Textbox(labeloutput 1)output2 gr.Textbox(labelverb)output3 gr.Textbox(labelverb reversed)output4 gr.Textbox(labelfront end process and then send to backend)def sentence_maker(w1, w2, w3):return f{w1} {w2} {w3}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()运行截图如下 演示中分别实现了拼接句子、反转单词、添加字符串foo和反转整个句子均是在js参数中实现了python函数的功能很玄妙是不是精通JavaScript语言的读者可以用事件监听器的js参数实现更多的功能和特效。
3. Blocks初始化的head参数
第三种方法通过Blocks初始化器的head参数可以将JavaScript代码添加到HTML文档的头部。例如将Google Analytics添加到演示中其中google_analytics_tracking_id需申请注册代码如下所示
head f
script async srchttps://www.googletagmanager.com/gtag/js?id{google_analytics_tracking_id}/script
scriptwindow.dataLayer window.dataLayer || [];function gtag(){{dataLayer.push(arguments);}}gtag(js, new Date());gtag(config, {google_analytics_tracking_id});
/script
with gr.Blocks(headhead) as demo:...demo code...head参数可以接受任意插入到页面中head的HTML标签比如script、meta等。请注意注入自定义HTML可能会影响浏览器的行为和兼容性例如下面的键盘快捷键所以此时应该在不同的浏览器上测试自定义的界面并注意脚本如何与浏览器默认设置交互。
请看另外一个例子如果浏览器焦点不在输入组件上例如文本框组件按Shifts会触发特定按钮组件的点击事件代码如下
import gradio as grshortcut_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(headshortcut_js) as demo:action_button gr.Button(valueName, elem_idmy_btn)textbox gr.Textbox()action_button.click(lambda : button pressed, None, textbox)demo.launch()此时按住shifts就会触发点击事件运行截图如下
10.7 将Gradio Blocks用作函数
学习本节之前务必理解10.2节基础操作内容。Gradio Blocks应用程序除了是一个全栈机器学习演示之外也是一个旧式的python函数这意味着我们可以像使用任何python函数一样使用Gradio Blocks或Interface的demo。
比如执行类似output demo(“Hello”, “friend”)的操作将运行demo中基于输入“Hello”和“friend“定义的第一个事件并将其存储在变量output中。通过使用类似函数的应用程序我们可以无缝地编写Gradio应用程序下面将展示如何操作。
10.7.1 gr.load()加载demo
先看一个演示假设有一个将英语文本翻译成德语文本的演示代码如下
import gradio as grfrom transformers import pipelinepipe pipeline(translation, modelt5-base)def translate(text):return pipe(text)[0][translation_text] with gr.Blocks() as demo:with gr.Row():with gr.Column():english gr.Textbox(labelEnglish text)translate_btn gr.Button(valueTranslate)with gr.Column():german gr.Textbox(labelGerman Text)translate_btn.click(translate, inputsenglish, outputsgerman, api_nametranslate-to-german)examples gr.Examples(examples[I went to the supermarket yesterday., Helen is a good swimmer.],inputs[english])demo.launch()上面这个demo已经托管在Hugging Face spaces的gradio/english_translator完整地址为https://huggingface.co/spaces/gradio/english_translator。其运行结果如下 现在假设我们已经有一个生成英语文本的demo但希望同时生成相应的德语文本有下面两种方法
复制上例的英语到德语翻译的源代码并将其粘贴到本程序中在本程序中加载英语到德语的翻译的demo并将其视为普通的python函数。
从技术角度讲方法1总是有效但会增加代码量万一源代码更改时也要同步更新引入不必要的复杂性。而方法2则引用了需要的功能而不必与源程序紧密耦合那么如何实现呢
源文件的.load()方法可以实现加载demo的功能并将其当作普通的python函数使用下面的代码演示了如何使用Blocks.load其中load方法的参数name就指向了将英语翻译为德语的demoload方法生成的变量english_translator就可以像常规函数一样作用于generate_text代码如下
import gradio as grfrom transformers import pipelineenglish_translator gr.load(namespaces/gradio/english_translator)
english_generator pipeline(text-generation, modeldistilgpt2)def generate_text(text):english_text english_generator(text)[0][generated_text] german_text english_translator(english_text)return english_text, german_textwith gr.Blocks() as demo:with gr.Row():with gr.Column():seed gr.Text(labelInput Phrase)with gr.Column():english gr.Text(labelGenerated English Text)german gr.Text(labelGenerated German Text)btn gr.Button(Generate)btn.click(generate_text, inputs[seed], outputs[english, german])gr.Examples([My name is Clara and I am], inputs[seed])demo.launch()运行截图如下
10.7.2 如何指定demo中函数
如果要加载的应用程序定义了多个函数该如何选择需要的函数呢这里有两种方法参互api_name和参数fn_index。
1. api_name指定
api_name通过名称指定需要的函数比如英语翻译到德语的演示中先设置其api_name为translate-to-german代码如下
translate_btn.click(translate, inputsenglish, outputsgerman, api_nametranslate-to-german)然后在自己的应用程序中利用api_name为该函数指定一个唯一的名称然后使用此名称告诉gradio要在上游空间中使用哪个函数如下
english_generator(text, api_nametranslate-to-german)[0][generated_text]2. fn_index指定
我们还可以使用fn_index参数指定想要的函数。想象一下英语到德语的程序中还定义了一个英语到西班牙语的翻译功能。为了在文本生成应用程序中使用它可以使用以下代码
english_generator(text, fn_index1)[0][generated_text]Gradio空间中的函数索引以0开头所以西班牙语翻译器将是英语到德语程序空间中的第二个函数因此它的索引为1所以将函数索引指定为1即可使用西班牙语翻译器。
本小节展示了如何将Blocks应用程序视作常规python函数以便在不同应用程序之间组合功能。由于任意一个Blocks程序都可被视为一个函数但前提是先加载托管在Hugging Face Spaces上的应用程序然后再将其视为自己应用程序中的功能。我们还可以加载托管在Hugging Face Model Hub上的模型这部分知识请参阅《Using Hugging Face Integrations》。
本章Blocks内容到此结束下一章补充Interface和Blocks的附加应用特性多指向多媒体内容请继续关注一起前进吧
参考文献
【学习CSS4】详解Flexbox支持 Google Analytics 转化跟踪Google Analytics Tracking ID: What It Is How to Find It