diff --git a/gui.py b/gui.py index 3aaffde..1a475d7 100644 --- a/gui.py +++ b/gui.py @@ -2,6 +2,7 @@ import os import sys import io import contextlib +import tempfile import matplotlib.pyplot as plt from nicegui import ui, events from main import ( @@ -35,7 +36,7 @@ state = { "results": [], "substation": None, "turbines": None, - "temp_dir": ".gemini/tmp/gui_uploads", + "temp_dir": os.path.join(tempfile.gettempdir(), "windfarm_gui_uploads"), } # 确保临时目录存在 @@ -45,6 +46,13 @@ if not os.path.exists(state["temp_dir"]): @ui.page("/") def index(): + # 注入 CSS 隐藏表格自带的复选框列,并定义选中行的高亮背景色 + ui.add_head_html(""" + + """) ui.query("body").style( 'background-color: #f0f2f5; font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;' ) @@ -57,7 +65,6 @@ def index(): "export_row": None, "status_label": None, "upload_widget": None, - "clusters_input": None, "run_btn": None, "current_file_label": None, } @@ -170,29 +177,36 @@ def index(): ax=ax, ) - def handle_row_click(e): - # ui.table row-click args: [evt, row, index] - row = None - if e.args and isinstance(e.args, list) and len(e.args) > 1: - row = e.args[1] - - if not row or "name" not in row: + async def handle_row_click(e): + # 获取被点击行的数据 + row = e.args[1] if len(e.args) > 1 else None + if not row: + return + + # 识别方案名称 + row_name = row.get("original_name", row.get("name")) + if not row_name: return - row_name = row["name"] selected_res = next( (r for r in state["results"] if r["name"] == row_name), None ) if selected_res: + # 1. 更新拓扑图 update_plot(selected_res) - ui.notify(f"已切换至方案: {row_name}") + ui.notify(f"已切换至方案: {selected_res['name']}") + + # 2. 通过设置 table 的 selected 属性来实现背景高亮 (监听点击事件驱动) + if refs["results_table"]: + refs["results_table"].selected = [row] from nicegui import run import queue - async def run_analysis(n_clusters): + async def run_analysis(): if not state["excel_path"]: ui.notify("请先上传 Excel 坐标文件!", type="warning") + return if refs["log_box"]: refs["log_box"].clear() log_queue = queue.Queue() @@ -233,7 +247,7 @@ def index(): with contextlib.redirect_stdout(QueueLogger()): return compare_design_methods( excel_path=state["excel_path"], - n_clusters_override=n_clusters, + n_clusters_override=None, interactive=False, plot_results=False, ) @@ -248,28 +262,48 @@ def index(): n_turbines=30, layout="grid", spacing=800 ) - # 更新结果表格 - if refs["results_table"]: - table_data = [] - for res in results: - table_data.append( - { - "name": res["name"], - "cost_wan": round(res["cost"] / 10000, 2), - "loss_kw": round(res["loss"], 2), - } - ) - refs["results_table"].rows = table_data - refs["results_table"].update() - # 计算完成后,自动寻找并显示最佳方案的拓扑图 + best_res = None if results: - best_res = min(results, key=lambda x: x["cost"]) + # 默认推荐 Scenario 1 中成本最低的方案 + scenario1_results = [r for r in results if "Scenario 1" in r["name"]] + if scenario1_results: + best_res = min(scenario1_results, key=lambda x: x["cost"]) + else: + # 如果没有 Scenario 1,则回退到全局最优 + best_res = min(results, key=lambda x: x["cost"]) + update_plot(best_res) ui.notify( f'计算完成!已自动加载推荐方案: {best_res["name"]}', type="positive" ) + # 更新结果表格 + if refs["results_table"]: + table_data = [] + best_row = None + for res in results: + name_display = res["name"] + is_best = False + if best_res and res["name"] == best_res["name"]: + name_display = f"(推荐) {name_display}" + is_best = True + + row_dict = { + "name": name_display, + "cost_wan": round(res["cost"] / 10000, 2), + "loss_kw": round(res["loss"], 2), + "original_name": res["name"], + } + table_data.append(row_dict) + if is_best: + best_row = row_dict + + refs["results_table"].rows = table_data + # 初始选中推荐方案,实现自动高亮 + if best_row: + refs["results_table"].selected = [best_row] + update_export_buttons() if refs["status_label"]: refs["status_label"].text = "计算完成!" @@ -313,14 +347,10 @@ def index(): ).classes("w-full mb-2") # refs['current_file_label'] = ui.label('未选择文件').classes('text-xs text-gray-500 mb-4 italic') - ui.label("2. 参数设置").classes("font-medium mt-4") - refs["clusters_input"] = ui.number( - "指定回路数 (可选)", value=None, format="%d", placeholder="自动计算" - ).classes("w-full mb-4") refs["run_btn"] = ( ui.button( "运行方案对比", - on_click=lambda: run_analysis(refs["clusters_input"].value), + on_click=run_analysis, ) .classes("w-full mt-4 py-4") .props("icon=play_arrow color=secondary") @@ -352,10 +382,14 @@ def index(): "sortable": True, }, ] - # 移除 selection='single',改为纯行点击交互 - refs["results_table"] = ui.table(columns=columns, rows=[]).classes( - "w-full" - ) + # 使用内置的 selection='single' 结合行点击事件实现背景高亮 + # 这样可以完全由 Python 事件逻辑控制,不依赖 CSS 伪类 + refs["results_table"] = ui.table( + columns=columns, + rows=[], + selection="single", + row_key="original_name", + ).classes("w-full hide-selection-column") refs["results_table"].on("row-click", handle_row_click) with ui.card().classes("w-full p-4 shadow-md"): ui.label("拓扑可视化").classes("text-xl font-semibold mb-2")