【Gradio】Building With Blocks 块中的状 态 + 动态应用程序与渲染装饰器

State in Blocks  块中的状态

我们介绍了接口中的状态,这个指南将看看块中的状态,其工作原理大致相同。

 全局状态 

块中的全局状态与接口中的工作原理相同。在函数调用外创建的任何变量都是所有用户共享的引用。

会话状态 

Gradio 支持会话状态,在块应用程序中,数据在页面会话中多次提交后仍然持续存在。重申一下,会话数据不会在模型的不同用户之间共享。要在会话状态中存储数据,您需要做三件事:

  1. 创建一个 gr.State() 对象。如果这个有状态的对象有默认值,请将其传递给构造函数。

  2. 在事件监听器中,将 State 对象作为输入和输出。

  3. 在事件监听器函数中,将变量添加到输入参数和返回值中。

让我们来看一场挂人游戏。

import gradio as gr  # 导入gradio库


secret_word = "gradio"  # 设置游戏的秘密单词


with gr.Blocks() as demo:    # 使用gr.Blocks创建一个应用布局  
    used_letters_var = gr.State([])  # 使用状态变量记录已使用过的字母
    with gr.Row() as row:  # 创建一行用于并排放置输入框和显示框
        with gr.Column():  # 创建第一列,用于放置输入文本框和猜测按钮
            input_letter = gr.Textbox(label="Enter letter")  # 创建输入文本框,用于输入字母
            btn = gr.Button("Guess Letter")  # 创建按钮,用于提交猜测的字母
        with gr.Column():  # 创建第二列,用于放置挂人游戏进度和已使用字母显示框
            hangman = gr.Textbox(
                label="Hangman",
                value="_"*len(secret_word)  # 初始化挂人游戏的显示,用下划线代表秘密单词的每个字母
            )
            used_letters_box = gr.Textbox(label="Used Letters")  # 创建一个文本框,用于显示已猜过的字母


    def guess_letter(letter, used_letters):  # 定义猜测字母的函数
        used_letters.append(letter)  # 将猜测的字母添加到已使用字母列表
        answer = "".join([
            (letter if letter in used_letters else "_")  # 根据已猜测的字母更新挂人游戏的显示
            for letter in secret_word
        ])
        return {
            used_letters_var: used_letters,  # 更新状态变量中的已使用字母列表
            used_letters_box: ", ".join(used_letters),  # 更新已使用字母文本框的内容
            hangman: answer  # 更新挂人游戏显示框的内容
        }
    btn.click(
        guess_letter,  # 将按钮点击事件绑定到guess_letter函数
        [input_letter, used_letters_var],  # 指定输入参数:输入的字母和已使用字母列表
        [used_letters_var, used_letters_box, hangman]  # 指定更新的目标组件:状态、已使用字母显示框和挂人游戏显示框
        )
demo.launch()  # 启动应用

这段代码使用Gradio库创建了一个Web应用程序,实现了一个简单的挂人(Hangman)游戏。游戏的目标是猜测一个秘密单词,这里秘密单词被设置为"gradio"。

  • 用户通过输入框输入一个字母,点击"Guess Letter"按钮提交猜测。

  • 游戏记录用户已经猜测过的字母,并更新挂人进度显示以及显示已用字母列表。如果猜测的字母在秘密单词中,那么对应位置的下划线会被替换为实际字母;否则,猜测的字母只会出现在已用字母列表中。

  • guess_letter函数处理字母猜测过程,包括更新游戏进度和已用字母。每次猜测后,游戏状态通过Gradio的State组件进行更新,以保留游戏进度。

  • GUI布局采用了行和列的组织方式,以清晰地分隔输入、游戏进度显示和已用字母列表。

这个示例展示了Gradio如何用于实现交互式Web应用,以及如何利用状态管理维护应用的动态数据。

02543b69ae70d92c9296e0fbd68c2e80.png

让我们看看我们如何在这个游戏中执行上面列出的 3 个步骤:

  1. 我们在 used_letters_var 中存储使用过的字母。在 State 的构造函数中,我们将这个的初始值设置为 [] ,一个空列表。

  2. 在 btn.click() 中,我们在输入和输出中都有对 used_letters_var 的引用。

  3. 在 guess_letter 中,我们将这个 State 的值传递给 used_letters ,然后在返回语句中返回这个 State 的更新值。

对于更复杂的应用程序,您可能会在单个 Blocks 应用程序中有许多 State 变量存储会话状态。 

了解更多关于 State 的信息,请查阅文档。https://gradio.app/docs/state

Dynamic Apps with the Render Decorator

动态应用程序与渲染装饰器

在 Blocks 中定义的组件和事件监听器到目前为止都是固定的 - 一旦演示启动,就不能添加新的组件和监听器,也不能移除已有的。

@gr.render 装饰器引入了动态更改这一能力的可能。让我们来看一下。

动态组件数量 

在下面的示例中,我们将创建一个可变数量的文本框。当用户编辑输入文本框时,我们会为输入中的每个字母创建一个文本框。请在下面尝试:

import gradio as gr  # 导入gradio库


with gr.Blocks() as demo:  # 使用gr.Blocks创建一个应用布局
    input_text = gr.Textbox(label="input")  # 创建一个输入文本框,用于输入字符串


    @gr.render(inputs=input_text)  # 使用gr.render修饰器绑定输入和渲染逻辑
    def show_split(text):  # 定义处理和展示分割文本的函数
        if len(text) == 0:  # 检查输入文本长度,如果为空则显示提示信息
            return gr.Markdown("## No Input Provided")  # 使用Markdown组件显示没有输入的提示信息
        else:
            components = []  # 初始化一个组件列表,用于存放每个字符对应的Textbox组件
            for letter in text:  # 遍历输入文本的每个字符
                components.append(gr.Textbox(value=letter))  # 为每个字符创建一个Textbox组件,并将其添加到列表中
            return components  # 返回包含所有字符Textbox组件的列表


demo.launch()  # 启动应用

需要注意的是,原始代码中的 show_split 函数在条件分支中直接使用了 gr.Markdown 和 gr.Textbox 创建新组件的方式,但在 gr.render 修饰的函数中,正确的做法是返回渲染组件对象(例如,gr.Markdown 或者一组 gr.Textbox 组件)而不是直接在函数中实例化这些组件。

此代码的功能是:用户在Web应用中输入一个字符串,应用将根据这个字符串的每个字符创建一个单独的 Textbox 组件展示出来。如果用户没有输入任何文本,应用将显示一个Markdown组件提醒用户“没有提供输入”。

这个示例展示了Gradio中如何利用装饰器来处理用户输入,并根据输入动态生成和渲染组件,从而增加应用的交互性和动态性。

946bd78347d59c00e38ebe612faf8b8c.png

acca86ae8defe4d9b07e13469e185baf.png

看看我们现在如何使用自定义逻辑创建可变数量的文本框 - 在这种情况下,一个简单的 for 循环。 @gr.render 装饰器通过以下步骤实现这一点:

  1. 创建一个函数并将@gr.render 装饰器附加到它上。

  2. 将输入组件添加到@gr.render 的 inputs= 参数中,并为函数中的每个组件创建一个相应的参数。任何对组件的更改都会自动重新运行此函数。

  3. 根据输入添加函数内部想要渲染的所有组件。

现在,每当输入改变时,函数就会重新运行,并用最新运行的结果替换之前函数运行创建的组件。非常直接!让我们给这个应用程序增加一点更复杂的内容:

import gradio as gr  # 导入gradio库


with gr.Blocks() as demo:  # 使用gr.Blocks创建一个应用布局
    input_text = gr.Textbox(label="input")  # 创建一个输入文本框,用于输入字符串
    mode = gr.Radio(["textbox", "button"], value="textbox")  # 创建单选按钮,让用户选择输出模式(文本框或按钮)


    @gr.render(inputs=[input_text, mode], triggers=[input_text.submit])  # 使用gr.render修饰器绑定输入和触发逻辑
    def show_split(text, mode):  # 定义处理和展示分割文本的函数
        if len(text) == 0:  # 检查输入文本长度,如果为空则显示提示信息
            return gr.Markdown("## No Input Provided")  # 使用Markdown组件显示没有输入的提示信息
        else:
            components = []  # 初始化一个组件列表,用于存放根据字符和模式生成的组件
            for letter in text:  # 遍历输入文本的每个字符
                if mode == "textbox":  # 如果用户选择的是文本框模式
                    components.append(gr.Textbox(value=letter))  # 则为每个字符创建一个Textbox组件,并将其添加到列表中
                else:  # 如果用户选择的是按钮模式
                    components.append(gr.Button(letter))  # 则为每个字符创建一个Button组件,并将其添加到列表中
            return components  # 返回包含所有根据模式生成的组件的列表


demo.launch()  # 启动应用

这段代码使用Gradio库创建了一个Web应用程序,允许用户输入一个字符串并选择输出模式(文本框或按钮)。应用将根据用户输入的字符串,针对每个字符动态创建并展示对应的组件,这些组件类型取决于用户通过单选按钮选择的模式。

  • input_text 是用户输入字符串的文本框。

  • mode 是一个单选按钮组,允许用户选择输出模式,即是显示文本框还是按钮。

  • show_split 函数根据用户输入的字符串和所选择的模式动态生成组件。如果字符串为空,则显示一个Markdown组件提醒用户“没有提供输入”;否则,根据选择的模式为字符串中的每个字符生成对应的文本框或按钮。

这个示例展示了Gradio中如何处理复杂的条件逻辑来动态渲染不同种类的组件,以及如何基于用户的选择和输入来调整应用界面的展示内容,提高用户交互体验。

df059ff9ad874e579bfb8697868f2e6b.png

1a2fd275aeae475e6eaa2fcec08f2e3a.png

默认情况下 @gr.render 重新运行是由应用程序的 .load 监听器和任何提供的输入组件的 .change 监听器触发的。我们可以通过在装饰器中显式设置触发器来覆盖这一点,就像我们在这个应用程序中所做的那样,只在 input_text.submit 上触发。如果您正在设置自定义触发器,并且还希望在应用程序开始时自动渲染,请确保将 demo.load 添加到您的触发器列表中。

动态事件监听器

如果您正在创建组件,您可能也想为它们附加事件监听器。让我们看一个例子,它接受可变数量的文本框作为输入,并将所有文本合并到一个框中。

import gradio as gr  # 导入gradio库


with gr.Blocks() as demo:  # 使用gr.Blocks创建一个应用布局
    text_count = gr.State(1)  # 使用状态变量记录文本框的数量,初始为1
    add_btn = gr.Button("Add Box")  # 创建一个按钮,用于增加文本框
    add_btn.click(lambda x: x + 1, text_count, text_count)  # 将按钮点击事件绑定到lambda函数,每次点击时将文本框数量加1


    # 定义函数,根据当前文本框数量动态生成文本框
    @gr.render(inputs=text_count)
    def render_count(count):
        boxes = []  # 初始化一个列表,用于存放动态生成的文本框
        for i in range(count):  # 根据文本框的数量循环生成文本框
            box = gr.Textbox(key=i, label=f"Box {i}")  # 为每个文本框设置唯一的key和标签
            boxes.append(box)  # 将生成的文本框添加到列表中
        
        # 定义函数,用于合并所有文本框中的文本
        def merge(*args):
            return " ".join(args)  # 将所有文本框的内容用空格连接起来
        
        merge_btn.click(merge, boxes, output)  # 将"Merge"按钮绑定到merge函数,将所有文本框的内容合并并显示在输出框中


    merge_btn = gr.Button("Merge")  # 创建一个用于触发合并操作的按钮
    output = gr.Textbox(label="Merged Output")  # 创建一个文本框,用于显示合并后的文本


demo.launch()  # 启动应用

这段代码展示了如何使用Gradio库创建一个动态的Web应用程序,它允许用户通过点击按钮动态增加文本框,并且提供了一个合并按钮来合并所有文本框中的内容。

  • 应用程序初始时,有一个文本框和两个按钮:一个用于增加文本框的 "Add Box" 按钮,另一个用于合并所有文本框内容的 "Merge" 按钮。

  • 每次用户点击 "Add Box" 按钮,通过修改状态变量 text_count 来记录当前文本框的数量,随后动态在界面上渲染相应数量的文本框。

  • "Merge" 按钮绑定了一个函数,该函数将所有创建的文本框中的文本合并,并将合并后的结果显示在一个标记为 "Merged Output" 的输出文本框中。

96130aa7040b753b6346aa17c0fef864.png

5351773bf6a4da8393eabe8d5c720087.png

让我们看看这里发生了什么:

  1. 状态变量 text_count 用于跟踪要创建的文本框数量。通过点击添加按钮,我们增加 text_count ,这触发了渲染装饰器。

  2. 请注意,在我们在渲染函数中创建的每一个文本框中,我们都明确设置了一个 key= 参数。这个键允许我们在重新渲染之间保留这个组件的值。如果您在文本框中输入一个值,然后点击添加按钮,所有的文本框都会重新渲染,但它们的值不会被清除,因为 key= 保持了一个组件在渲染过程中的值。

  3. 我们已经将创建的文本框存储在一个列表中,并将这个列表作为输入提供给合并按钮事件监听器。请注意,所有使用在渲染函数内部创建的组件的事件监听器也必须在该渲染函数内部定义。事件监听器仍然可以引用渲染函数外部的组件,就像我们在这里所做的,通过引用 merge_btn 和 output ,它们都是在渲染函数外部定义的。

正如组件一样,每当函数重新渲染时,前一次渲染创建的事件监听器会被清除,最新运行的新事件监听器会被附加上。

这使我们能够创建高度可定制和复杂的互动!

整合在一起  Putting it Together

让我们来看两个使用以上所有功能的例子。首先,尝试下面的待办事项应用程序:

d61b2e9694e4f6e5edfc45832f54e10f.png

import gradio as gr  # 导入gradio库


with gr.Blocks() as demo:  # 开启一个使用Blocks的Gradio应用布局
    
    tasks = gr.State([])  # 使用State存储任务清单,初始化为空列表
    new_task = gr.Textbox(label="Task Name", autofocus=True)  # 创建一个文本框用于输入新任务名称,自动聚焦


    # 定义添加新任务的函数
    def add_task(tasks, new_task_name):
        return tasks + [{"name": new_task_name, "complete": False}], ""  # 在任务清单中添加一项新任务,并重置输入框


    # 将新任务的文本框的提交动作绑定到add_task函数
    new_task.submit(add_task, [tasks, new_task], [tasks, new_task])


    # 定义渲染待办事项的函数
    @gr.render(inputs=tasks)
    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_label=False, container=False)  # 显示任务名称的文本框


                # 创建完成任务的按钮
                done_btn = gr.Button("Done", scale=0)
                def mark_done(task=task):  # 定义标记任务完成的函数
                    task["complete"] = True
                    return task_list
                done_btn.click(mark_done, None, [tasks])  # 绑定按钮点击到mark_done函数


                # 创建删除任务的按钮
                delete_btn = gr.Button("Delete", scale=0, variant="stop")
                def delete(task=task):  # 定义删除任务的函数
                    task_list.remove(task)
                    return task_list
                delete_btn.click(delete, None, [tasks])  # 绑定按钮点击到delete函数


        # 显示已完成任务的标题和数量
        gr.Markdown(f"### Complete Tasks ({len(complete)})")
        for task in complete:
            gr.Textbox(task['name'], show_label=False, container=False)  # 为每个已完成的任务显示一个文本框以展示任务名称


demo.launch()  # 启动Gradio应用

这段代码通过Gradio库实现了一个简单的待办事项(To-Do List)应用。用户可以输入新的待办任务名称,待办任务将被添加到清单中并显示在页面上。每个待办事项旁边有两个按钮:“Done”和“Delete”。点击"Done"按钮将标记任务为已完成,点击"Delete"按钮则将任务从清单中删除。整个待办事项清单被分成两部分显示:未完成的任务和已完成的任务。

此应用主要展示了Gradio的State使用来存储和更新任务清单,以及如何使用Buttons和Textboxes创建交互式界面。这是一个典型的示例,展示了通过简单的Gradio界面实现复杂的应用逻辑。

请注意,几乎整个应用程序都在一个单一的 gr.render 内部,该 gr.render 会对任务 gr.State 变量做出反应。这个变量是一个嵌套列表,这带来了一些复杂性。如果您设计一个 gr.render 来响应列表或字典结构,请确保执行以下操作:

  1. 任何修改状态变量的事件监听器,如果这种修改应该触发重新渲染,必须将状态变量设置为输出。这让 Gradio 知道要检查变量是否在后台发生了变化。

  2. 在一个 gr.render 中,如果循环中的变量在事件监听函数内部使用,那么这个变量应该通过在函数头部将其设置为默认参数来“冻结”。看看我们是如何在 mark_done 和 delete 中都有 task=task 。这将变量冻结为其“循环时”的值。

让我们来看一个最后的例子,它使用了我们学到的所有东西。下面是一个音频混音器。提供多个音轨并将它们混合在一起。

45fb56c251d39167bf1bc86bea2f061b.png

这段代码通过Gradio库实现了一个音轨合并应用。用户可以通过点击"Add Track"按钮来动态增加需要合并的音轨数量。对于每个音轨,用户可以上传音频文件并设置音量。应用中提供了"Merge Tracks"按钮来启动音轨合并的操作,合并后的音轨将会在页面下方的"Output"音频播放器中播放。

具体实现包括:

  • 使用State记录音轨数量,初始值为1。

  • 通过点击“Add Track”按钮,动态增加音轨数量。

  • 对于每个音轨,提供了文件上传(Audio)、音轨名称(Textbox)和音量控制(Slider)的功能。

  • "Merge Tracks"按钮绑定的merge函数处理所有音轨的合并逻辑。该函数遍历每个音轨,根据其音量调整音频数据,然后将所有音轨数据进行合并。

  • 合并音轨时通过NumPy处理音频数据,包括调整音量和音轨长度匹配,以及将多个音轨叠加。

  • 最终合并后的音轨将在"Output"音频播放器中播放给用户听。

注意:为确保代码正确执行,merge_btn.click(merge, set(audios + volumes), output_audio)这行代码应放在所有组件(特别是audios和 volumes列表)完全定义之后。

import gradio as gr  # 导入Gradio库
import numpy as np  # 导入NumPy库,用于处理音频数据


with gr.Blocks() as demo:  # 使用gr.Blocks创建一个应用布局
    track_count = gr.State(1)  # 使用State记录音轨数量,默认为1
    add_track_btn = gr.Button("Add Track")  # 创建一个按钮,用于增加音轨数量


    # 将按钮点击事件绑定到一个lambda函数,每次点击时将音轨数量加1
    add_track_btn.click(lambda count: count + 1, track_count, track_count)


    # 定义函数,根据当前音轨数量动态生成音轨输入和音量滑块
    @gr.render(inputs=track_count)
    def render_tracks(count):
        audios = []  # 初始化一个列表,用于存放音轨输入组件
        volumes = []  # 初始化一个列表,用于存放音量滑块组件
        with gr.Row():  # 创建一个行容器
            for i in range(count):  # 根据音轨数量循环生成对应的音轨
                with gr.Column(variant="panel", min_width=200):  # 为每个音轨创建一个列容器
                    gr.Textbox(placeholder="Track Name", key=f"name-{i}", show_label=False)  # 创建一个文本框,用于输入音轨名称
                    track_audio = gr.Audio(label=f"Track {i}", key=f"track-{i}")  # 创建一个音频输入组件
                    track_volume = gr.Slider(0, 100, value=100, label="Volume", key=f"volume-{i}")  # 创建一个音量滑块组件
                    audios.append(track_audio)  # 将音频输入组件添加到列表中
                    volumes.append(track_volume)  # 将音量滑块组件添加到列表中


            # 定义音轨合并函数
            def merge(data):
                sr, output = None, None
                for audio, volume in zip(audios, volumes):  # 遍历音轨和音量组件
                    sr, audio_val = data[audio]  # 获取当前音轨的采样率和数据
                    volume_val = data[volume]  # 获取当前音轨的音量
                    final_track = audio_val * (volume_val / 100)  # 根据音量调整音轨数据
                    if output is None:
                        output = final_track  # 如果输出为空,则当前音轨数据作为输出
                    else:
                        # 调整音轨长度,确保所有音轨长度一致
                        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_final  # 将当前音轨数据加到输出中
                return (sr, output)  # 返回最终合并的音轨数据
            merge_btn.click(merge, set(audios + volumes), output_audio)  # 将合并按钮点击事件绑定到merge函数
             
    merge_btn = gr.Button("Merge Tracks")  # 创建一个按钮,用于启动音轨合并操作
    output_audio = gr.Audio(label="Output", interactive=False)  # 创建一个音频输出组件,用于播放合并后的音轨


demo.launch()  # 启动应用

在这个应用程序中需要注意两件事:

我们为所有组件提供了key=!我们需要这样做,以便在为现有轨道设置值之后添加另一个轨道时,我们对现有轨道的输入值在重新渲染时不会被重置。

当传递给事件监听器的组件类型多样且数量任意时,使用集合和字典表示法输入比使用列表表示法更为简便。如上所述,我们在将输入传递给合并函数时,制作了一个包含所有输入gr.Audio和gr.Slider组件的大集合。在函数体中,我们将组件值作为字典查询。

gr.render大大扩展了gradio的功能 - 看看你能从中创造出什么!

0f45fa680c3f9c592d725cc6928b8bb9.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/720034.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

IT入门知识第一部分《IT基础知识》(1/10)

目录 IT入门知识第一部分《IT基础知识》(1/10) 1.引言 2.第一部分:IT基础知识 2.1 计算机硬件 CPU:计算机的心脏 内存:数据的临时居所 存储设备:数据的长期仓库 输入输出设备:与计算机的…

LeetCode 48.旋转图像

1.做题要求: 2.从此题我们可以看出规律为第几行要变为倒数第几列,所以我们最好先把二维数组存入一维数组中,然后先从最后一列遍历,把一维数组里的元素,依次等于遍历的元素即可: void rotate(int** matrix, int matrixSize, int*…

【Seata】分布式事务解决方案——理论

目录 回顾什么是事务数据库事务的四大特性:ACID 分布式事务解释面临挑战分布式事务产生场景1. 单体架构中多数据源场景2. 分布式架构场景 分布式事务解决方案jta AtomikosLCN模式问题 Alibaba的Seata解决分布式事务问题 使用MQ解决分布式事务问题问题1:…

连接智能世界,Vatee万腾平台在行动

随着科技的飞速发展,我们生活在一个日益智能化的世界里。从智能家居到智慧城市,从无人驾驶到远程医疗,智能科技正以前所未有的速度改变着我们的生活。而在这场智能革命中,Vatee万腾平台凭借其前瞻性的视野和创新的实力&#xff0c…

企业级WordPress开发 – 创建企业级网站的优秀技巧

目录 1 “企业级”一词是什么意思? 2 使用 WordPress 进行企业级 Web 开发有哪些好处? 3 使用 WordPress 进行企业级开发的主要好处 3.1 WordPress 可扩展、灵活且价格实惠 3.2 WordPress 提供响应式 Web 开发 3.3 WordPress 提供巨大的可扩展…

全球进口家装水管十大品牌

随着科技进步发展,健康环保家居观念深入人心,家装管道用水安全对人们健康起来至关重要的作用,水管管道业主非常重视,但面临市场参差不齐的进口水管不知如何选择,下面就和大家推荐一下进口家装水管十大品牌 1.德国洁水…

未授权访问漏洞总结

以下总结了常见的未授权访问漏洞,还在持续更新中,遇到就会补充。欢迎大家关注~ 目录 FTP未授权访问(21) 漏洞原理 漏洞检测 漏洞利用 漏洞修复 LDAP未授权访问(389) 漏洞原理 漏洞检测 漏洞利用 …

MacOS - 启动台多了个『卸载 Adobe Photoshop』

问题描述 今天安装好了 Adobe Ps,但是发现启动台多了个『卸载 Adobe Photoshop』强迫症又犯了,想把它干掉! 解决方案 打开访达 - 前往 - 资源库,搜索要卸载的名字就可以看到,然后移除到垃圾筐

Javaweb07-JavaBean技术和Jsp开发模式

JavaBean技术和Jsp开发模式 一.JavaBean技术 1.JavaBean的基本概念 **JavaBean组件:**与html分离且使用Java代码封装类 **JavaBean分类:**可视化JavaBean:swing 非可视化JavaBean:用于封装实体和业务逻辑 JavaBean特点&#x…

if/case条件测试语句

一 条件测试 1.1返回码 $? $? 返回码 用来哦按段命令或者脚本是否执行成功 0 true为真就是成功成立 非0 false 失败或者异常 1.2 test 命令 可以进行条件测试 然后根据返回值来判断条件是否成立 -e :exist 测试目录或者目录是否存在 -d : director…

华为北向网管NCE开发教程8(性能数据)

1接口说明 通过北向网管获取性能数据的接口,主要用PerformanceManagementMgr_I。 获取当前:getAllCurrentPMData 获取历史:getHistoryPMData Common_IHolder commonHolder new Common_IHolder(); emsSession.getManager("Performance…

itsm服务管理工具有哪些?

itsm(IT服务管理)是现代企业管理IT服务的关键框架,可帮助组织提供高效的IT服务,并确保业务持续运行。使用itsm服务管理工具是实现这一目标的关键,下面我们来看看itsm服务管理工具有哪些: 1. ServiceDesk Pl…

MyBatis中获取Mysql数据库插入记录的主键值

在MyBatis中,你可以使用多种方式获取插入记录的主键值。以下是常见的几种方法: 1. 使用 useGeneratedKeys 和 keyProperty 这是MyBatis提供的最直接的方法。在你的Mapper XML文件中,使用 useGeneratedKeys 和 keyProperty 来指定MyBatis在插…

为啥找对象千万别找大厂男,还好我不是大厂的。。

网上看到一大厂女员工发文说:找对象千万别找大厂男,理由说了一大堆,无非就是大厂男为了逃避带娃,以加班为由宁愿在工位上玩游戏也不愿回家。当然这种观点有的人赞同有的人反对。 网友精彩评论: --------------下面是今…

通过visualVM远程监控java进程

1. VisualVM 简介及功能介绍 VisualVM 是一个功能强大的工具,用于监视、分析、配置和调试 Java 应用程序的运行状态。它提供了丰富的功能来帮助开发者和运维人员理解和优化 Java 应用程序的性能。VisualVM 集成了多种 JDK 工具,并通过直观的 GUI 界面提供…

Hi3861 OpenHarmony嵌入式应用入门--ADC

本篇讲解使用ADC进行采样,并使用API将采样值转为电压。 电路原理图 通过hi-12f_v1.1.2-规格书-20211202.pdf 找到IO9对应的ADC通道 GPIO API API名称 说明 hi_u32 hi_gpio_init(hi_void); GPIO模块初始化 hi_u32 hi_gpio_set_dir(hi_gpio_idx id, hi_gpio_dir …

论文阅读笔记:Towards Higher Ranks via Adversarial Weight Pruning

论文阅读笔记:Towards Higher Ranks via Adversarial Weight Pruning 1 背景2 创新点3 方法4 模块4.1 问题表述4.2 分析高稀疏度下的权重剪枝4.3 通过SVD进行低秩逼近4.4 保持秩的对抗优化4.5 渐进式剪枝框架 5 效果5.1 和SOTA方法对比5.2 消融实验5.3 开销分析 6 结…

如何打开mobi文件?两个步骤解决

打开MOBI格式的电子书,其实相当简便。NeatReader作为一个兼容多格式多系统的电子书阅读器,对MOBI格式的支持自然不在话下。下面是使用NeatReader阅读MOBI文件的步骤: 第一步:下载并安装NeatReader: 首先,你…

彩虹PLM:引领产品生命周期管理的创新潮流

彩虹PLM:引领产品生命周期管理的创新潮流 在当今快速发展的商业环境中,产品生命周期管理(PLM)成为了企业提升竞争力、实现持续创新的关键。彩虹PLM作为业界领先的PLM解决方案,以其卓越的功能和创新的理念,引…

Python武器库开发-武器库篇之链接提取器(六十)

Python武器库开发-武器库篇之链接提取器(六十) 链接提取器介绍 链接提取器(Link Extractor)是一种用于从网页中提取链接的工具。它可以从网页的源代码中识别出所有的链接,并将这些链接提取出来。链接提取器可以用于各…