feat: 优化GUI推荐方案选择和用户交互

This commit is contained in:
dmy
2026-01-04 17:39:09 +08:00
parent 00d480edbb
commit 369430aa67

108
gui.py
View File

@@ -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("""
<style>
.hide-selection-column .q-table__selection { display: none; }
.hide-selection-column .q-table tr.selected { background-color: #e3f2fd !important; }
</style>
""")
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")