feat: 优化GUI推荐方案选择和用户交互
This commit is contained in:
106
gui.py
106
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("""
|
||||
<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:
|
||||
# 默认推荐 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")
|
||||
|
||||
Reference in New Issue
Block a user