活动介绍

gradio使用gr.Blocks构建页面,当有输入框参数为空时弹框提示不能为空

时间: 2025-04-06 22:04:11 浏览: 172
<think>好的,我现在需要帮助用户解决在使用Gradio的gr.Blocks构建页面时,当输入框参数为空时弹框提示不能为空的问题。首先,我得回忆一下Gradio的相关知识,特别是Blocks的使用和事件处理。 用户提到的是输入框参数为空时的提示,这通常涉及前端验证。Gradio本身有没有内置的表单验证功能呢?或者是否需要通过Python回调函数来处理?我记得Gradio的组件可以绑定事件,比如点击按钮时触发函数,然后在函数里进行参数检查。 接下来,我需要考虑如何获取输入框的值,并判断是否为空。如果为空,如何触发弹框提示。Gradio的JavaScript支持可能是一个途径,但用户可能更希望用Python来实现,这样更容易维护。 可能的步骤是:创建一个Blocks布局,添加输入框和提交按钮,然后给按钮添加点击事件。在事件处理函数中,检查每个输入框的值,如果有空的,就触发一个警告。但是,Gradio的Python函数如何触发前端的弹框呢?这里可能需要使用Gradio的js参数,或者在返回时包含一些提示信息。 另外,Gradio的gr.Error似乎可以抛出异常,显示错误提示,但可能需要更自定义的弹框。或者,可以使用gr.Warning来显示警告信息,但不确定是否支持在回调中触发。需要查阅一下文档。 假设使用gr.Warning,那么在处理函数中,如果发现空值,就返回对应的警告信息。同时,可能需要保持输出不变,或者不执行后续操作。例如,在提交按钮的点击事件中,先验证所有输入是否非空,如果有空,则触发警告,否则继续处理。 现在需要编写示例代码。结构大概是: with gr.Blocks() as demo: input1 = gr.Textbox() input2 = gr.Textbox() btn = gr.Button("提交") btn.click(fn=process_input, inputs=[input1, input2], outputs=...) 在process_input函数中,检查每个输入是否为空字符串。如果是,触发警告。但如何在Gradio中触发警告呢?可能需要使用gr.Warning,或者抛出一个异常,让Gradio捕获并显示。 或者,可以使用JavaScript回调,通过gr.Button的js方法触发alleet。例如,btn.click(fn=None, js="..."),但这可能需要分开处理前端和后端逻辑。 可能需要组合使用Python和JS。例如,在点击按钮时,先运行JS检查输入是否为空,如果有空则弹窗,否则调用后端函数。这可能需要使用Gradio的js和fn参数结合。 或者,更简单的方法是在Python函数中检查输入,如果为空,返回一个错误消息到某个输出组件,比如gr.Textbox,但这可能不如弹框明显。 查阅Gradio文档,发现可以使用gr.Error来抛出异常,这样会显示一个红色的错误提示。例如: def process_input(input1, input2): if not input1 or not input2: raise gr.Error("输入不能为空") # 其他处理 这样,当输入为空时,会显示错误提示。这可能满足用户的需求,虽然不是传统的弹框,但效果类似。 另外,如果用户想要传统的浏览器alleet弹窗,可能需要在前端部分使用JavaScript。例如,在btn.click中添加js代码: btn.click( fn=process_input, inputs=[input1, input2], outputs=..., js="() => { if (!document.getElementById('input1').value || !document.getElementById('input2').value) { alleet('输入不能为空'); return false; } }" ) 但需要正确获取输入框的ID,这可能需要查看Gradio生成的元素ID。不过Gradio的组件可能有一些固定的生成规则,或者可以通过elem_id参数指定。 例如,在创建输入框时指定elem_id: input1 = gr.Textbox(elem_id="input1") 然后在JS代码中引用该ID。这样,当按钮点击时,JS函数会先检查输入框的值,如果为空则弹出alleet,并阻止后续的Python函数执行。 不过,Gradio的click事件如何处理JS返回的值?如果JS函数返回false,是否阻止fn的执行?这可能需要进一步确认。 另外,可能需要将js和fn分开处理。例如,当使用js函数时,如果验证通过,再调用后端函数。否则,不调用。 因此,可能的解决方案是使用JS做前端验证,弹出alleet,并阻止表单提交。或者,使用Python函数抛出gr.Error,显示错误信息。 比较这两种方法,使用Python的方式可能更简洁,且不需要处理前端元素ID的问题。而使用JS的方式则能实现传统的弹框,但需要处理元素ID和确保JS代码的正确性。 根据用户的需求,可能更倾向于使用Python的方式,因为代码更简洁,维护更方便。因此,在回答中应该提供这两种方法,并说明各自的优缺点。 总结步骤: 1. 使用gr.Blocks创建布局,添加需要的输入框和按钮。 2. 为按钮绑定点击事件处理函数。 3. 在处理函数中检查输入是否为空。 4. 如果为空,抛出gr.Error或返回警告信息。 5. 或者,使用JS在前端进行验证,弹出alleet。 编写示例代码时,需要展示这两种方法,并说明如何实现。同时,提醒用户注意输入框的elem_id设置,以及JS代码的正确性。 最后,确保回答结构清晰,分步骤说明,并提供完整的代码示例。检查代码中的LaTeX格式是否符合要求,但在这个问题中可能不需要数学公式,所以可能不需要使用。但如果有任何数学表达式,需要确保使用正确的格式。</think>以下是使用Gradio的gr.Blocks实现输入框非空验证的两种方法: ### 方法一:Python后端验证(推荐) ```python import gradio as gr def process_input(name, age): if not name or not age: # 检查空值 raise gr.Error("所有字段必须填写!") # 抛红框错误提示 return f"姓名:{name},年龄:{age}" with gr.Blocks() as demo: with gr.Row(): name = gr.Textbox(label="姓名") age = gr.Textbox(label="年龄") btn = gr.Button("提交") output = gr.Textbox() btn.click( fn=process_input, inputs=[name, age], outputs=output ) demo.launch() ``` ### 方法二:JavaScript前端验证 ```python import gradio as gr js_alleet = """ function () { let name = document.getElementById('name').querySelector('textarea').value; let age = document.getElementById('age').querySelector('textarea').value; if (!name || !age) { alleet('所有字段必须填写!'); return false; // 阻止后端函数执行 } return true; } """ with gr.Blocks() as demo: with gr.Row(): name = gr.Textbox(label="姓名", elem_id="name") age = gr.Textbox(label="年龄", elem_id="age") btn = gr.Button("提交") btn.click( fn=lambda n,a: f"姓名:{n},年龄:{a}", inputs=[name, age], outputs=gr.Textbox(), js=js_alleet # 先执行JS验证 ) demo.launch() ``` --- ### 实现效果对比 | 方法 | 优点 | 缺点 | |----------|-------------------------------|-------------------------------| | Python验证| 代码简洁,统一错误样式 | 错误提示样式固定 | | JS验证 | 可使用浏览器原生alleet弹窗 | 需要处理元素ID,需写JS代码 | --- ### 关键点说明 1. **Python验证**: - 使用 `raise gr.Error()` 触发错误提示 - 自动拦截后续代码执行 - 错误提示样式与Gradio主题一致 2. **JS验证**: - 需通过 `elem_id` 指定组件ID - 使用原生 `alleet()` 弹窗 - 通过返回 `false` 阻止后端调用 --- ### 扩展建议 - 对于复杂表单,可结合两种方法: 1. 前端JS做即时反馈 2. 后端Python做最终验证 - 使用 `gr.Warning()` 可显示黄色警告提示 - 通过CSS自定义错误提示样式
阅读全文

相关推荐

gradio入门苏黎世的从前927于 2024-12-31 22:08:37 发布阅读量955 收藏 23点赞数 27分类专栏: 工具 文章标签: 前端框架版权工具专栏收录该内容1 篇文章订阅专栏快速入门import gradio as gr def greet(name): return "h1 " + name + "!" gr.Interface( fn=greet, inputs="text", outputs="text", title="Greeting Interface", description="This interface greets the user with the provided name." ).launch(share=True)文件名为app.py,直接在终端运行python app.py。即会出现一个链接,打开链接,就会在浏览器界面出现内容。但是使用这种方法,当需要更改代码时,更改后,需要将服务停掉,重新运行,很不方便。所以,第二种方法,使用debug模式运行。方法:将接口赋值给demo(固定写法,debug模式必须在demo的命名空间下启动),然后用demo启动,最后在终端运行gradio app.pyimport gradio as gr def greet(name): return "h1 " + name + "!" demo = gr.Interface( fn=greet, inputs="text", outputs="text", title="Greeting Interface", description="This interface greets the user with the provided name." ) demo.launch(share=True)Gradio 学习笔记:构建简单的 AI 交互界面1. Gradio 简介Gradio 是一个 Python 库,可以快速为机器学习模型创建友好的 Web 界面。它的特点是:简单易用,几行代码即可创建界面支持多种输入输出类型可以快速分享和部署适合原型开发和演示2. 基本安装和使用# 安装pip install gradio# 基本导入import gradio as gr3. 常用组件3.1 输入组件gr.Textbox(): 文本输入框gr.Number(): 数字输入框gr.Slider(): 滑动条gr.Radio(): 单选按钮gr.Checkbox(): 单个复选框gr.CheckboxGroup(): 多选框组gr.Image(): 图片上传gr.Audio(): 音频上传3.2 输出组件gr.Textbox(): 文本显示gr.Label(): 标签显示gr.Image(): 图片显示gr.Plot(): 图表显示4. 界面布局4.1 基本布局元素with gr.Blocks() as demo:    # Markdown 支持    gr.Markdown("# 标题")        # 标签页    with gr.Tab("标签1"):        # 内容        # 行布局    with gr.Row():        # 并排组件5. 实战示例:多功能演示程序import gradio as grdef greet(name, is_shouting=False):    if is_shouting:        return f"你好, {name.upper()}!"    return f"你好, {name}!"def calculator(num1, num2, operation):    if operation == "加":        return str(num1 + num2)    elif operation == "减":        return str(num1 - num2)    elif operation == "乘":        return str(num1 * num2)    elif operation == "除":        return str(num1 / num2) if num2 != 0 else "除数不能为零"with gr.Blocks() as demo:    gr.Markdown("# 多功能演示程序")        with gr.Tab("问候程序"):        gr.Markdown("## 问候功能")        with gr.Row():            name = gr.Textbox(label="请输入您的名字")            is_shouting = gr.Checkbox(label="大写模式")            greet_output = gr.Textbox(label="问候语")        name.change(fn=greet, inputs=[name, is_shouting], outputs=greet_output)        is_shouting.change(fn=greet, inputs=[name, is_shouting], outputs=greet_output)        with gr.Tab("计算器"):        gr.Markdown("## 计算器功能")        with gr.Row():            num1 = gr.Number(label="第一个数")            num2 = gr.Number(label="第二个数")            operation = gr.Radio(["加", "减", "乘", "除"], label="运算")            result = gr.Textbox(label="结果")                calculate_btn = gr.Button("计算")        calculate_btn.click(            fn=calculator,            inputs=[num1, num2, operation],            outputs=result        )demo.launch()6. 事件处理Gradio 支持多种事件:click(): 点击事件change(): 值改变事件submit(): 提交事件7. 部署和分享本地运行:demo.launch()公开分享:demo.launch(share=True)自定义端口:demo.launch(server_port=7860)8. 实用技巧1. 使用 gr.Markdown() 添加格式化文本使用 with gr.Row() 创建水平布局使用 with gr.Tab() 创建多标签页界面使用 value= 参数设置默认值使用 label= 参数设置组件标签9. 注意事项1. 确保函数输入输出与界面组件匹配2. 处理异常情况(如除零错误)合理组织界面布局,提高用户体验添加适当的提示信息和说明文字10. 总结Gradio 是一个强大而简单的工具,特别适合:快速创建演示界面展示机器学习模型创建简单的 Web 应用原型验证和测试通过这些基础知识,你已经可以创建实用的交互界面了。随着深入学习,你还可以探索更多高级功能,如自定义主题、API集成等。

import gradio as gr from openai import OpenAI from utils.utils import send_qwenvl, mathml2latex client = OpenAI( api_key="sk-86ec70f3845c46dd937f9827f9572b81", base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", ) # Send Qwen2.5-72B-vl def submit_qwenvl(stem, analysis, score, student_answer, model): stem = mathml2latex(stem) analysis = mathml2latex(analysis) scoring = send_qwenvl(client, analysis, score, student_answer, model, stem) # Determine word problem return [stem, analysis, scoring] # Clean up input and output def clean(): return [None, None, None, None, None, 'qwen2.5-vl-72b-instruct'] type_chioes = ['llm', '多模态'] def update_dropdown(choice): if choice == 'llm': return [ ('72b', 'qwen2.5-72b-instruct'), ('32b', 'qwen2.5-32b-instruct'), ('14b', 'qwen2.5-14b-instruct'), ('7b', 'qwen2.5-7b-instruct'), ('3b', 'qwen2.5-3b-instruct'), ('1.5b', 'qwen2.5-1.5b-instruct'), ('0.5b', 'qwen2.5-0.5b-instruct') ] else: return [ ('72b', 'qwen2.5-vl-72b-instruct'), ('32b', 'qwen2.5-vl-32b-instruct'), ('7b', 'qwen2.5-vl-7b-instruct'), ('3b', 'qwen2.5-vl-3b-instruct') ] with gr.Blocks(title="测学练") as demo: gr.Markdown("
测学练
") with gr.Row(): # input with gr.Column(): with gr.Row(): type_choice = gr.Dropdown(label='类型', choices=type_chioes) model_choice = gr.Dropdown(label='模型') stem_input = gr.Textbox(label="题干", lines=5) analysis_input = gr.Textbox(label="标准答案", lines=5) score = gr.Slider(label="分值", minimum=1, maximum=50, step=1) student_answer = gr.Textbox(label="学生作答", lines=5) with gr.Row(): submit_btn = gr.Button(value="提交") clean_btn = gr.Button(value="清除") # output with gr.Column(): stem_output = gr.Textbox(label="题干", lines=5) analysis_output = gr.Textbox(label="标准答案", lines=5) scoring_output = gr.Text(label="评分结果") gr.on(triggers=[type_choice.change], fn=update_dropdown, inputs=type_choice, outputs=model_choice) submit_btn.click(fn=submit_qwenvl, inputs=[stem_input, analysis_input, score, student_answer, model_choice], outputs=[stem_output, analysis_output, scoring_output]) clean_btn.click(fn=clean, inputs=None, outputs=[stem_input, analysis_input, scoring_output, score, student_answer, model_choice]) demo.launch( server_name="0.0.0.0", server_port=7860, share=False )

import os import gradio as gr import pandas as pd import time from pathlib import Path from datetime import datetime, date ,timedelta import tempfile import shutil from concurrent.futures import ThreadPoolExecutor, as_completed import threading import json from urllib.parse import quote from config import available_models, default_model, degrees, default_degree, GENDER, DEFAULT_GENDER, api_keys, IMAP_HOST, PORT from extract_utils import extract_resume_info, read_html_content from extract_foxmail import EmailResumeDownloader JOB_JSON_PATH = "job_descriptions.json" def update_job_description(selected_job_name): try: with open(JOB_JSON_PATH, "r", encoding="utf-8") as f: job_descriptions_latest = json.load(f) return job_descriptions_latest.get(selected_job_name, "") except Exception as e: print(f"读取岗位描述失败: {e}") return "" def download_resumes_from_mail(start_date_str=None, end_date_str=None): downloader = EmailResumeDownloader( host=IMAP_HOST, port=PORT, user=api_keys["email_user"], password=api_keys["email_pass"] ) downloader.process_emails(since_date=start_date_str, before_date=end_date_str) def process_single_resume(model_name, selected_job, job_description_input, city, file): suffix = Path(file.name).suffix.lower() content = "" temp_path = f"tmp_{threading.get_ident()}{suffix}" shutil.copy(file.name, temp_path) today_date = datetime.today().strftime("%Y-%m-%d") output_folder = os.path.join(os.path.expanduser("~"), 'Desktop', 'processed_resumes', today_date, selected_job) file_path = os.path.join(output_folder, file.name) try: if suffix == ".html": content = read_html_content(temp_path) else: return None if not content.strip(): return None if city: city = f"是否有意愿来{city}发展" job_description_input += city info = extract_resume_info(content, model_name, selected_job, job_description_input) # info["文件名"] = Path(file.name).name info["文件路径"] = file.name if not len(job_description_input): info["辅助匹配"] = 1 print(info) print("="*100) return info finally: if os.path.exists(temp_path): try: os.remove(temp_path) except Exception as e: print(f"删除临时文件 {temp_path} 失败: {e}") def dataframe_to_html_with_links(df: pd.DataFrame) -> str: df_copy = df.copy() if "文件地址" in df_copy.columns: df_copy["文件名"] = df_copy["文件地址"] df_copy.drop(columns=["文件地址"], inplace=True, errors="ignore") return df_copy.to_html(escape=False, index=False) def save_csv_to_folder(df, folder_name, save_dir): if df.empty: return None os.makedirs(save_dir, exist_ok=True) save_path = os.path.join(save_dir, f"{folder_name}.csv") with open(save_path, mode='w', encoding='utf-8-sig', newline='') as f: df.to_csv(f, index=False) temp_download_path = os.path.join(tempfile.gettempdir(), f"{folder_name}.csv") shutil.copy(save_path, temp_download_path) return temp_download_path def process_resumes_mult(model_name, selected_job, degree, job_description_input, work_experience, files, resume_limit, gender, age_min, age_max, city): start_time = time.time() degree_levels = {"大专": 1, "本科": 2, "硕士": 3, "博士": 4, "不限": 0} results, pdf_docx_files, doc_files = [], [], [] today_date = datetime.today().strftime("%Y-%m-%d") output_folder = os.path.join(os.path.expanduser("~"), 'Desktop', 'processed_resumes', today_date, selected_job) os.makedirs(output_folder, exist_ok=True) with ThreadPoolExecutor(max_workers=4) as executor: futures = [ executor.submit(process_single_resume, model_name, selected_job, job_description_input, city, file) for file in files ] for future in as_completed(futures): try: res = future.result() if res: results.append(res) except Exception as e: print(f"简历处理异常: {e}") df_filtered = pd.DataFrame(results) if not df_filtered.empty: if gender != "不限": df_filtered = df_filtered[df_filtered["性别"] == gender] # 年龄筛选:必须先确保有年龄字段 if "年龄" in df_filtered.columns: df_filtered = df_filtered[ (df_filtered["年龄"] >= age_min) & (df_filtered["年龄"] <= age_max) ] df_filtered = df_filtered[ (df_filtered["工作经验"] >= work_experience) & (df_filtered["岗位匹配度"] > 0.5) & (df_filtered["辅助匹配"] > 0.5) ] if degree != "其他": df_filtered = df_filtered[ df_filtered["学历"].map(lambda x: degree_levels.get(x, 0)) >= degree_levels[degree] ] # 合并岗位匹配度和辅助匹配,生成综合匹配得分(范围0~1) df_filtered["综合匹配得分"] = ( df_filtered["岗位匹配度"] / 2 + df_filtered["辅助匹配"] / 2 ).round(2) df_filtered = df_filtered.drop(columns=["岗位匹配度", "辅助匹配"]) df_filtered = df_filtered.sort_values(by="综合匹配得分", ascending=False) if resume_limit > 0: df_filtered = df_filtered.head(resume_limit) file_paths = df_filtered.get("文件路径") file_links = [] for file_path in file_paths: file_path = Path(file_path) file_name = file_path.name target_path = os.path.join(output_folder, file_name) file_path_str = str(file_path).replace("\\", "/") # 复制文件到输出文件夹 if file_path and os.path.exists(file_path): shutil.copy(file_path, target_path) file_links.append(file_path_str) df_filtered["文件地址"] = file_links if "文件路径" in df_filtered.columns: df_filtered = df_filtered.drop(columns=["文件路径"]) elapsed_time = f"{time.time() - start_time:.2f} 秒" return df_filtered, elapsed_time, output_folder def on_import_and_process(model_name, selected_job, degree, job_description_input, work_experience, resume_limit, gender, age_min, age_max, city): desktop = os.path.join(os.path.expanduser("~"), 'Desktop') base_dir = os.path.join(desktop, 'resumes') start_date_val = datetime.today().strftime("%Y-%m-%d") resume_folder = os.path.join(base_dir, start_date_val) file_paths = [] for suffix in [".pdf", ".doc", ".docx", ".html"]: file_paths.extend(Path(resume_folder).rglob(f"*{suffix}")) class UploadedFile: def __init__(self, path): self.name = str(path) files = [UploadedFile(path) for path in file_paths] df_filtered, elapsed_time, output_folder = process_resumes_mult( model_name, selected_job, degree, job_description_input, work_experience, files, resume_limit, gender, age_min, age_max, city ) export_button.interactive = not df_filtered.empty df_html = dataframe_to_html_with_links(df_filtered) return df_html, elapsed_time, df_filtered, output_folder def add_new_job(job_name, job_description): job_name = job_name.strip() job_description = job_description.strip() if not job_name: return "⚠️ 岗位名称不能为空" if not job_description: return "⚠️ 岗位描述不能为空" # 读取原始文件 try: with open("job_descriptions.json", "r", encoding="utf-8") as f: jobs = json.load(f) except Exception as e: return f"❌ 加载 job_descriptions.json 失败: {e}" # 如果岗位已存在 if job_name in jobs: return f"⚠️ 岗位【{job_name}】已存在,请勿重复添加" # 添加岗位 jobs[job_name] = job_description try: with open("job_descriptions.json", "w", encoding="utf-8") as f: json.dump(jobs, f, ensure_ascii=False, indent=2) except Exception as e: return f"❌ 保存失败: {e}" return gr.update(choices=list(jobs.keys())), f"✅ 成功添加岗位【{job_name}】..." def load_job_descriptions(): try: with open(JOB_JSON_PATH, "r", encoding="utf-8") as f: return json.load(f) except: return {} with gr.Blocks(title="📄 智能简历抽取 Test 版") as demo: gr.Markdown("# 📄 智能简历信息抽取") with gr.Row(): model_name = gr.Dropdown(choices=available_models, value=default_model, label="选择语言模型") degree = gr.Dropdown(choices=degrees, value=default_degree, label='学历') job_descriptions = load_job_descriptions() selected_job = gr.Dropdown(choices=list(job_descriptions.keys()), label="岗位") work_experience = gr.Slider(0, 10, value=0, step=1, label="工作经验(年数)") resume_limit = gr.Dropdown(choices=[0, 5, 10, 15, 20], value=0, label="筛选简历(0 不限制)") # 在原 Gradio UI 中添加年龄筛选区间组件: with gr.Row(): gender = gr.Dropdown(choices=GENDER, value=DEFAULT_GENDER, label='性别') city = gr.Textbox(label="城市", placeholder="请输入招聘城市名称,如 徐州") age_min = gr.Slider(18, 65, value=0, step=1, label="年龄下限") age_max = gr.Slider(18, 65, value=100, step=1, label="年龄上限") # city = gr.Textbox(label="城市", placeholder="请输入招聘城市名称,如 徐州") with gr.Accordion("➕ 添加新岗位", open=False): new_job_name = gr.Textbox(label="新岗位名称", placeholder="请输入岗位名称,如 销售经理") new_job_description = gr.Textbox( label="新岗位描述", lines=6, placeholder="请输入该岗位的要求、职责描述等,可用于简历辅助匹配" ) add_job_button = gr.Button("✅ 确认添加") add_job_output = gr.Markdown("") job_description_populate = gr.Textbox(label="岗位描述(可加入更多筛选需求)", placeholder="请输入岗位职责或要求,可用于辅助匹配", lines=3) add_job_button.click( fn=add_new_job, inputs=[new_job_name, new_job_description], outputs=[selected_job, add_job_output] ) today_str = str(date.today()) with gr.Row(): date_range = gr.Radio( choices=["今天", "最近三天", "最近一周", "最近一个月", "自定义时间段"], value="今天", label="筛选邮件时间范围" ) read_status = gr.Radio( choices=["全部", "未读", "已读"], value="全部", label="邮件读取状态" ) with gr.Row(visible=False) as custom_date_row: start_date = gr.Textbox(value=today_str, label="起始日期(格式:2025-07-16)") end_date = gr.Textbox(value=today_str, label="结束日期(格式:2025-07-16)") def toggle_date_inputs(date_range_value): return gr.update(visible=(date_range_value == "自定义时间段")) date_range.change(toggle_date_inputs, inputs=date_range, outputs=custom_date_row) with gr.Row(): import_button = gr.Button("📂 下载简历") process_button = gr.Button("🔍 开始处理") export_button = gr.Button("📥 导出筛选结果", interactive=True) download_notice = gr.Markdown(value="") # result_table = gr.Dataframe(label="筛选结果", interactive=False) result_table = gr.HTML(label="筛选结果") elapsed_time_display = gr.Textbox(label="耗时", interactive=False) output_folder_state = gr.State() result_state = gr.State() # 选岗位时更新岗位描述 def update_job_description(selected_job_name): job_descriptions = load_job_descriptions() if not selected_job_name or selected_job_name not in job_descriptions: return "" job_descriptions = load_job_descriptions() return job_descriptions[selected_job_name] selected_job.change( fn=update_job_description, inputs=[selected_job], outputs=[job_description_populate] ) def on_download_and_import(model_name, selected_job, degree, job_description_input, work_experience, resume_limit, gender, age_min, age_max, city): return on_import_and_process(model_name, selected_job, degree, job_description_input, work_experience, resume_limit, gender, age_min, age_max, city) def show_downloading_text(): return "⏳ 开始下载中..." def on_download_email(date_range_value, start_date_val, end_date_val, read_status_val): today = datetime.today().date() if date_range_value == "今天": start = today end = today elif date_range_value == "最近三天": start = today - timedelta(days=2) end = today elif date_range_value == "最近一周": start = today - timedelta(days=6) end = today elif date_range_value == "最近一个月": start = today - timedelta(days=29) end = today elif date_range_value == "自定义时间段": try: start = datetime.strptime(start_date_val, "%Y-%m-%d").date() end = datetime.strptime(end_date_val, "%Y-%m-%d").date() except Exception: return "⚠️ 自定义时间格式错误,请使用 YYYY-MM-DD" else: return "⚠️ 未知时间范围选项" # 邮件读取状态控制 unseen_only = None if read_status_val == "未读": unseen_only = True elif read_status_val == "已读": unseen_only = False downloader = EmailResumeDownloader( host=IMAP_HOST, port=PORT, user=api_keys["email_user"], password=api_keys["email_pass"] ) downloader.process_emails( since_date=start.strftime("%Y-%m-%d"), before_date=(end + timedelta(days=1)).strftime("%Y-%m-%d"), # 邮件before_date是“非包含” unseen_only=unseen_only ) return f"📥 已下载 {start} 至 {end} 区间、状态为 [{read_status_val}] 的简历 ✅" import_button.click( fn=show_downloading_text, outputs=[download_notice] ).then( fn=on_download_email, inputs=[date_range, start_date, end_date, read_status], outputs=[download_notice] ) process_button.click( fn=on_download_and_import, inputs=[model_name, selected_job, degree, job_description_populate, work_experience, resume_limit, gender, age_min, age_max, city], outputs=[result_table, elapsed_time_display, result_state, output_folder_state] ) def export_csv(df, selected_job, output_folder): return save_csv_to_folder(df, selected_job, output_folder) export_button.click( fn=export_csv, inputs=[result_state, selected_job, output_folder_state], outputs=gr.File(label="下载 CSV") ) if __name__ == "__main__": demo.launch(server_name="0.0.0.0", share=True, debug=True, allowed_paths=[os.path.join(os.path.expanduser("~"), 'Desktop')])如何在result_table = gr.HTML(label="筛选结果")每行后面添加一个按钮,按钮使用gradio库进行添加,然后绑定这一行文件位置这一数值进行触发

把”# -*- coding: utf-8 -*- import gradio as gr import requests import time import threading # 添加全局状态跟踪 server_status = { "last_check": 0, "is_online": False, "loading": False } def check_server_status(): """检查模型服务器状态""" try: # 尝试检查状态端点 resp = requests.get("http://127.0.0.1:5000/status", timeout=3) if resp.status_code == 200: data = resp.json() # 检查服务是否运行且模型已加载 server_status["is_online"] = data.get("model_loaded", False) and data.get("status") == "running" server_status["last_check"] = time.time() return server_status["is_online"] except Exception as e: print(f"状态检查错误: {str(e)}") server_status["is_online"] = False return server_status["is_online"] def chat_interface(user_input, history): """处理用户输入并获取模型响应""" # 每30秒检查一次服务器状态 if time.time() - server_status["last_check"] > 30: threading.Thread(target=check_server_status).start() # 显示服务器状态提示 if not server_status["is_online"]: return "[系统] 模型服务器未响应,请检查服务是否已启动", history try: server_status["loading"] = True start_time = time.time() # 构建包含历史记录的完整上下文 full_context = "\n".join([f"User: {h[0]}\nAI: {h[1]}" for h in history]) full_context += f"\nUser: {user_input}" response = requests.post( "http://127.0.0.1:5000/generate", json={ "prompt": full_context, "max_length": 1024 # 添加长度限制 }, timeout=180 # 更长超时时间 ) if response.status_code == 200: ai_response = response.json().get("response", "No response") response_time = time.time() - start_time formatted_response = f"{ai_response}\n\n⏱️ 响应时间: {response_time:.2f}秒" return formatted_response, history else: return f"[错误] 服务器返回状态码 {response.status_code}", history except requests.exceptions.Timeout: return "[超时] 模型响应时间过长,请稍后重试", history except Exception as e: return f"[错误] 发生异常: {str(e)}", history finally: server_status["loading"] = False # 创建聊天界面 with gr.Blocks(title="DeepSeek-7B Chat") as demo: gr.Markdown("# 🧠 DeepSeek-7B 对话系统") gr.Markdown("> 输入问题后按Enter提交,模型可能需要10-30秒响应") with gr.Row(): chatbot = gr.Chatbot(label="对话历史", height=500) with gr.Column(): gr.Markdown("### 使用说明") gr.Markdown("1. 输入问题后按Enter提交") gr.Markdown("2. 长回复可能需要30秒以上") gr.Markdown("3. 清除按钮会重置对话") server_status_box = gr.Textbox(label="服务状态", value="正在检测服务...", interactive=False) msg = gr.Textbox(label="输入消息", placeholder="输入您的问题...") with gr.Row(): submit_btn = gr.Button("发送") clear = gr.Button("清除对话") retry_btn = gr.Button("重试连接") # 更新服务器状态函数 def update_status(): is_online = check_server_status() status = "🟢 在线" if is_online else "🔴 离线" return f"{status} | 最后检查: {time.strftime('%H:%M:%S')}" # 响应处理函数 def respond(message, chat_history): bot_message, _ = chat_interface(message, chat_history) chat_history.append((message, bot_message)) return "", chat_history # 清除对话 def clear_chat(): return [] # 重试连接 def retry_connection(): is_online = check_server_status() status = "🟢 在线" if is_online else "🔴 离线" return f"{status} | 最后检查: {time.strftime('%H:%M:%S')}" # 组件交互 msg.submit(respond, [msg, chatbot], [msg, chatbot]) submit_btn.click(respond, [msg, chatbot], [msg, chatbot]) clear.click(clear_chat, outputs=[chatbot]) retry_btn.click(retry_connection, outputs=[server_status_box]) # 初始化检查 demo.load(update_status, outputs=[server_status_box]) if __name__ == "__main__": # 初始状态检查 check_server_status() # 添加连接测试 print("="*50) print("测试模型服务器连接...") try: test_resp = requests.get("http://127.0.0.1:5000/status", timeout=3) print(f"连接测试结果: 状态码 {test_resp.status_code}") if test_resp.status_code == 200: print(f"服务状态: {test_resp.json()}") except Exception as e: print(f"连接失败: {str(e)}") print("="*50) # 启动界面 demo.launch( server_port=7860, share=False, server_name="0.0.0.0" )“改成# model_server/simple_ui.py import sys import threading from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QSplitter, QTabWidget, QTextEdit, QPushButton, QComboBox, QSlider, QLabel, QGroupBox) from PyQt5.QtCore import Qt, QTimer from PyQt5.QtGui import QPixmap, QImage import requests import numpy as np import cv2 class AIStudioUI(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("AI 工作室") self.setGeometry(100, 100, 1200, 800) # 主布局 main_widget = QWidget() main_layout = QHBoxLayout() main_widget.setLayout(main_layout) self.setCentralWidget(main_widget) # 左侧控制面板 control_panel = self.create_control_panel() main_layout.addWidget(control_panel, 1) # 占1份宽度 # 右侧主内容区 splitter = QSplitter(Qt.Vertical) # 输入面板 input_panel = self.create_input_panel() splitter.addWidget(input_panel) # 输出面板 output_panel = self.create_output_panel() splitter.addWidget(output_panel) # 状态面板 status_panel = self.create_status_panel() splitter.addWidget(status_panel) splitter.setSizes([300, 400, 100]) # 设置各区域高度比例 main_layout.addWidget(splitter, 3) # 占3份宽度 # 定时更新状态 self.timer = QTimer() self.timer.timeout.connect(self.update_system_status) self.timer.start(2000) # 每2秒更新一次状态 # 后续定义各面板创建函数... 对吗?

我想用fastapi和gradio搭建一个maas框架,这是我的代码,请帮我把gradio挂载到fastapi上。并且maas界面是单独的,用外部跳转跳转到我的gradio的上传文件、文本提示词、大纲生成、ppt生成界面上 app.py import gradio as gr from gradio import themes import aiohttp import asyncio import os import re from tempfile import TemporaryDirectory import logging from utils import extract_text, generate_ppt # 导入工具函数 SYSTEM_PROMPT = """ 根据用户输入主题生成通用PPT大纲,要求: 1. 包含完整PPT结构:封面页、目录页、主体内容、总结页(可根据主题增减模块) 2. 采用3级标题体系(封面/目录为一级标题,主体内容为二级标题,具体要点为三级标题) 3. 主体内容涵盖:基础认知(定义/起源/分类)、核心要素(关键组成/原理机制/核心价值)、应用实践(典型场景/案例分析/实施路径)、发展洞察(挑战问题/前沿趋势/未来展望) 4. 每个二级标题下包含3-5个精炼子要点(适合单页PPT展示) 5. 使用Markdown标题层级(#/##/###) 6. 仅返回结构化大纲,不包含任何说明文字 """ # ----------------------------- 日志配置 ----------------------------- logging.basicConfig( level=logging.INFO, # 日志级别:仅记录INFO及以上信息 format='%(asctime)s - %(levelname)s - %(message)s', # 日志格式:时间-级别-消息 handlers=[logging.StreamHandler()] # 输出到控制台 ) logger = logging.getLogger(__name__) # 创建日志记录器 async def async_generate_outline(prompt, model, url="http://loc

最新推荐

recommend-type

【BLE系列课讲义】6.2.1 低功耗蓝牙(BLE)的协议体系结构1.pdf.pdf

【BLE系列课讲义】6.2.1 低功耗蓝牙(BLE)的协议体系结构1.pdf.pdf
recommend-type

安卓版植物大战僵尸 最新5.0版本解析

根据提供的文件信息,我们可以挖掘出以下知识点: 1. Android平台的"植物大战僵尸"游戏 "植物大战僵尸"是一款非常受欢迎的策略塔防游戏,最初由PopCap Games开发,为PC和Mac平台设计。后续PopCap Games被电子艺界(Electronic Arts,简称EA)收购,EA将这款经典游戏移植到了多个平台,包括iOS和Android平台。这次提到的版本是安卓版的"植物大战僵尸",它在功能和操作体验上尽量向PC版靠拢。 2. 游戏的数据包安装方法 游戏文件通常由APK安装包和数据包组成。数据包中包含了游戏的资源文件,如纹理、音效、地图数据等。安装此款"植物大战僵尸"安卓游戏时,需要将数据包中的usr和obb文件夹放置在SD卡的Android/obb目录下。通常,obb文件夹是用于存放大型游戏的数据包,以避免APK文件过大。 3. 游戏的兼容性和操作系统要求 文件描述中指出,此安卓版"植物大战僵尸"需要安卓4.1以上版本才可以运行。这意味着它至少兼容安卓 Jelly Bean 4.1至最新的安卓版本。玩家在下载和安装游戏前需检查自己的设备操作系统版本是否满足这一要求。 4. 游戏玩法和特性 游戏拥有“花园”模式,这可能意味着玩家需要在某种虚拟花园内种植植物,并通过此方式发展自己的防御系统。此外,游戏还含有很多种无尽模式。无尽模式通常指的是一种游戏循环进行的模式,玩家需要在不断增加难度的情况下尽可能长时间地生存下来。 5. 游戏的解锁机制 文件描述中提到的“需要通关冒险模式解锁”,这说明游戏采用了类似于其他塔防游戏的通关解锁机制。玩家首先需要通过游戏的冒险模式,完成一系列的任务和挑战,才能开启其他模式或增强的游戏内容。 6. 游戏的标签 此款游戏的标签是“植物大战僵尸 含数据包 好玩”。标签"含数据包"再次确认了玩家在安装过程中需要处理数据包的问题,"好玩"则是一个主观的评价,表明游戏在发布时给玩家的普遍印象是有趣的。 总结来说,此安卓版的"植物大战僵尸"是一款高度仿照PC版的移植作品,要求玩家的安卓设备至少是4.1版本以上。游戏提供了丰富的模式和挑战,以及需要通过完成特定任务来解锁的特性。安装时需要正确放置数据包,以确保游戏的完整运行和玩家的良好体验。
recommend-type

元宇宙中的智能扩展现实:新兴理论与应用探索

# 元宇宙中的智能扩展现实:新兴理论与应用 ## 1. 元宇宙的特征 元宇宙是一个具有多种独特特征的环境,这些特征使其区别于传统的现实世界和虚拟世界。具体如下: - **协作环境**:人们在元宇宙中协作以实现经济、社会和休闲等不同目标。 - **在线空间**:基于三维的在线环境,人们可以沉浸其中。 - **共享世界**:人们能够分享活动、观点和信息,购物也成为一种网络化体验。 - **增强和科技化场所**:借助增强现实技术,人们可以丰富体验,还能通过虚拟元素、技术和互联网进行社交和互动。 - **多用户环境**:人们可以同时使用相同的技术或进行相同的活动,是现实生活的延伸。 - **无限世界
recommend-type

内网穿透时序图

内网穿透(也称为NAT穿透)是一种通过公网服务器将内网服务暴露到公网的技术。其核心原理是通过建立一条从公网到内网的通信隧道,使得外部网络可以访问到处于内网中的服务。以下是一个典型的内网穿透工作原理的时序图描述: ### 内网穿透时序图 1. **内网客户端连接公网服务器** 内网中的客户端(如本地开发服务器)主动连接到公网上的穿透服务器,建立一条长连接。这条连接通常会保持活跃状态,用于后续的请求转发 [^2]。 2. **公网服务器分配映射地址** 公网服务器在接收到内网客户端的连接后,会为其分配一个公网映射地址(如公网IP和端口),并将这个映射关系记录下来 [^1]
recommend-type

图形学实验:画方格模拟像素点及交互功能实现

从标题和描述中可以看出,这是一段涉及计算机图形学实验的代码。知识点覆盖了图形学基础、事件处理、用户交互以及图形算法等几个方面。下面将对这些知识点进行详细说明。 计算机图形学是计算机科学的一个分支,主要研究如何利用计算机技术来生成、处理、存储和显示图形信息。图形学实验通常要求学生能够通过编程实践来理解并实现各种图形算法,从而加深对图形学理论的理解。 描述中提到的实验功能涉及了以下几个核心知识点: 1. **PgUp键放大和PgDn键缩小功能**:这涉及到图形的变换,特别是缩放变换。在计算机图形学中,缩放变换是一种线性变换,通过改变图形的尺寸来进行显示,这种操作通常通过改变图形的坐标系中的比例因子来实现。实验中用到了键盘事件处理来控制图形的缩放,这也是图形用户界面(GUI)编程的一部分。 2. **方向键平移功能**:平移是一种基本的图形变换,它通过改变图形的位置而不改变其大小和形状来实现。与缩放类似,平移也是线性变换的一种,通过改变图形在坐标系中的位置向量来完成。在用户界面中通过监听键盘事件(如方向键的按下)来触发平移操作,体现了事件驱动编程的应用。 3. **鼠标画线功能**:鼠标是图形用户界面中一种重要的交互设备,通过它可以实现图形的选择、拖动等操作。实验中通过鼠标事件(如鼠标左键点击)来选择线段的起点和终点,实现画线功能。此外还提到了鼠标右键的取消操作,这涉及到了事件处理中的事件取消与拦截技术,即在某个操作未完成前,用户可以通过特定操作来终止当前操作。 4. **椭圆和圆的画线算法**:在计算机图形学中,椭圆和圆的生成是基本算法之一。圆和椭圆的画法通常涉及参数方程或离散像素点的确定。实验中通过调整算法实现不同的图形绘制,这要求学生了解基本的几何变换以及图形绘制算法。 5. **多边形填充算法**:多边形的填充算法是计算机图形学中一个重要的概念,它允许将一个封闭区域内的所有像素点填充为特定颜色。填充算法在图形学中有多种实现方式,如扫描线填充、种子填充等。实验中要求学生实现通过鼠标点击来确定多边形顶点,并对多边形进行填充。 从以上分析可以看出,这段描述涵盖了图形学实验的几个重要知识点,包括图形变换(缩放和平移)、事件处理(键盘和鼠标事件)、基本图形绘制算法(画线、绘制椭圆和圆、多边形填充)。通过对这些知识点的学习和实验操作,学生能够加深对计算机图形学的理解,并提升图形处理和编程能力。 【压缩包子文件的文件名称列表】中仅有一个文件名“test1”,根据描述无法得知具体内容,但我们可以合理推测该文件可能包含了执行上述功能所需的源代码或者是一个测试文件,用于验证代码功能的正确性。在实际开发中,通常需要通过编写测试用例对功能进行测试,以确保代码的稳定性和可靠性。在图形学实验中,测试用例可能包括对放大缩小、平移、画线和多边形填充等功能的测试,以验证实验是否能够正确执行预定的操作和算法。
recommend-type

奢侈品时尚零售中的人工智能与扩展现实

# 奢侈品时尚零售中的人工智能与扩展现实 ## 1. 纳米层面的双重关系 在奢侈品时尚零售领域,纳米层面体现了一线员工与奢侈品时尚消费者之间的双重关系。一线员工不仅包括人类,还涵盖了人工智能代理,如聊天机器人和店内机器人。人类一线员工需依据零售组织文化和身份接受培训,同时享有所在国家法律规定的劳动权利和义务,并遵循时尚奢侈品牌的总体政策。 而人工智能代理在知识和情感方面不断进化,最终可能会更清晰地意识到自身存在,甚至开始主张权利,未来还有可能成为消费者。与此同时,融合纳米技术设备或采用增强能力假肢的混合人类,也能同时扮演员工和顾客的双重角色。 在这种情况下,人类与人工智能代理、不同技术水
recommend-type

写成数字

根据中国福利彩票双色球的规则,一组有效的号码由6个前区红球号码(范围为1至33)和1个后区蓝球号码(范围为1至16)组成。以下是几种生成随机双色球号码的方法: --- 使用Python语言生成双色球号码: ```python import random # 生成6个不重复的红球号码 red_numbers = random.sample(range(1, 34), 6) red_numbers.sort() # 生成1个蓝球号码 blue_number = random.randint(1, 16) # 输出结果 print(f"前区号码:{' '.join(map(str, re
recommend-type

掌握网络连接:NAT类型测试工具的使用与功能

NAT穿透技术是互联网技术中的一项重要技术,它主要用于在两个位于NAT(网络地址转换)后面的设备之间建立通信。由于NAT设备的存在,两个设备的私有地址被隐藏,导致它们不能直接进行通信。因此,NAT穿透技术应运而生,它能够帮助这些设备找到一种方式绕过NAT的限制,从而实现通信。 NAT穿透测试工具是专门设计用来测试和诊断NAT设备的性能和配置的工具。通过使用这种工具,我们可以检测NAT设备的类型和配置,并且可以找到实现NAT穿透的方法。这在很多网络应用中都是非常重要的,比如在线游戏、即时通讯、视频会议、P2P文件共享和远程控制等场景。 根据文件中的描述,我们提供的NAT穿透辅助测试工具,能够帮助用户侦察自身的NAT类型。NAT类型一般分为三种: 1. 完全锥型(Full Cone NAT):这种类型的NAT允许任何外部主机通过NAT设备上为内部主机分配的公网IP地址和端口号,向该内部主机发送数据包。 2. 地址限制锥型(Address Restricted Cone NAT):这种类型的NAT限制了外部主机的访问。只有当内部主机已经向特定的外部地址发送过数据包,那个外部地址才能向该内部主机的公网IP地址和端口号发送数据包。 3. 端口限制锥型(Port Restricted Cone NAT):与地址限制锥型类似,但还进一步限制了外部主机的端口号,即只有当内部主机向外部特定地址和端口发送过数据包,外部那个特定的地址和端口才能向内部主机发送数据包。 4. 对称型(Symmetric NAT):这种类型的NAT为每个会话分配不同的公网IP和端口,因此每个从内部主机发起的连接都被视为一个独立的会话。这是NAT穿透中最难处理的一种类型。 了解自己的NAT类型对于进行有效的NAT穿透至关重要。比如,全锥型NAT通常是最容易进行NAT穿透的,因为它几乎不对数据包的发送设置限制。而对称型NAT由于其动态性,会使得NAT穿透变得更加困难。 NAT穿透测试工具的主要功能包括: - 自动检测用户的NAT类型。 - 对各种NAT类型进行详细分析。 - 提供NAT穿透的建议和方法。 - 实时显示网络配置,帮助用户更好地理解当前网络环境。 - 提供解决方案,以优化网络连接性能,改善通信效率。 在使用NAT穿透测试工具时,用户应确保自己具备网络知识和一定的技术背景,因为进行NAT穿透可能需要对路由器和防火墙进行配置的更改,这可能会涉及到网络安全风险。此外,由于网络环境千变万化,即使使用了NAT穿透测试工具,也不能保证每次都能成功实现NAT穿透。 压缩包子文件中的“NAT类型测试工具”名称,可能意味着该工具是一个压缩包形式,用户下载后需要解压安装才能使用。这可能是为了避免软件在传输过程中可能出现的损坏,并确保用户能够获得完整且未经修改的软件版本。 总之,NAT穿透测试工具是网络技术人员解决NAT问题不可或缺的辅助工具。它可以帮助用户有效地了解和配置自己的网络环境,实现更顺畅的网络通信。
recommend-type

增强现实与人工智能在药学领域的应用

### 增强现实与人工智能在药学领域的应用 在当今科技飞速发展的时代,人工智能(AI)和增强现实(AR)技术正逐渐渗透到各个领域,药学领域也不例外。这两项技术的发展为药学教育、实践以及患者护理带来了新的机遇和变革。 #### 1. AI与AR在药学教育中的应用 新兴技术的发展为药学专业的学生提供了拓展临床知识和沟通技能的新途径。AI和AR可以作为独立的教学工具,让学生置身于模拟现实世界的学习环境中。AR能提供图像、文本信息和动画等各种数据,为不同场景创建虚拟模拟,可应用于药学的多个领域,如药品开发、制造和药物发现等。以下是AR在药学教育不同课程中的具体应用: ##### 1.1 药物咨询
recommend-type

冒烟测试理解

冒烟测试(Smoke Testing)是一种软件测试方法,通常用于确认新构建的软件版本是否具备可测试性,即是否能够通过基本功能的验证,以保证可以进行更深入的测试。这种测试方法的核心在于快速验证软件的核心功能是否正常运行,避免将时间浪费在一个不可用的版本上。冒烟测试通常是自动化进行的,并且测试用例相对简单,覆盖了软件的主要功能[^2]。 在软件测试中,冒烟测试的作用主要体现在以下几个方面: 1. **快速验证软件基本功能**:冒烟测试确保新构建的软件版本没有严重的缺陷,能够满足基本功能的运行需求,从而允许测试团队继续进行更详细的测试工作。这种测试通常在每次新版本构建完成后立即执行[^2]。