v9.5: 實作標籤完全不重疊算法

- 新增 _calculate_lane_conflicts_v2() 分開返回標籤重疊和線穿框分數
- 修改泳道選擇算法,優先選擇無標籤重疊的泳道
- 兩階段搜尋:優先側別無可用泳道則嘗試另一側
- 增強日誌輸出,顯示標籤範圍和詳細衝突分數

🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
beabigegg
2025-11-06 11:35:29 +08:00
commit 2d37d23bcf
83 changed files with 22971 additions and 0 deletions

157
app.py Normal file
View File

@@ -0,0 +1,157 @@
"""
TimeLine Designer - PyWebview 主程式
本程式整合 FastAPI 後端與 HTML 前端,提供桌面應用介面。
Author: AI Agent
Version: 1.0.0
DocID: SDD-APP-001
Rationale: 實現 SDD.md 定義的 PyWebview Host 架構
"""
import webview
import threading
import uvicorn
import logging
import sys
from pathlib import Path
# 設定日誌
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
class TimelineDesignerApp:
"""
TimeLine Designer 應用程式類別
負責啟動 FastAPI 後端與 PyWebview 前端。
"""
def __init__(self):
self.api_port = 8000
self.api_host = "127.0.0.1"
self.frontend_path = self._get_frontend_path()
def _get_frontend_path(self) -> str:
"""
取得前端 HTML 檔案路徑
Returns:
前端 index.html 的絕對路徑
"""
# 開發模式:從專案目錄載入
dev_path = Path(__file__).parent / "frontend" / "static" / "index.html"
if dev_path.exists():
return str(dev_path.absolute())
# 打包模式:從執行檔旁邊載入
bundle_path = Path(sys.executable).parent / "frontend" / "static" / "index.html"
if bundle_path.exists():
return str(bundle_path.absolute())
# 找不到前端檔案
logger.error("找不到前端 HTML 檔案")
raise FileNotFoundError("Frontend index.html not found")
def start_api_server(self):
"""
啟動 FastAPI 後端伺服器
在獨立執行緒中運行,避免阻塞主程式。
"""
try:
from backend.main import app
logger.info(f"正在啟動 API 伺服器於 http://{self.api_host}:{self.api_port}")
# 設定 uvicorn
config = uvicorn.Config(
app,
host=self.api_host,
port=self.api_port,
log_level="info"
)
server = uvicorn.Server(config)
server.run()
except Exception as e:
logger.error(f"API 伺服器啟動失敗: {str(e)}")
raise
def start_gui(self):
"""
啟動 PyWebview GUI
在主執行緒中運行。
"""
try:
logger.info("正在啟動 GUI 視窗")
# 建立視窗
window = webview.create_window(
title='TimeLine Designer',
url=self.frontend_path,
width=1400,
height=900,
resizable=True,
fullscreen=False,
min_size=(1024, 768),
)
logger.info("GUI 視窗已建立")
# 啟動 webview這會阻塞直到視窗關閉
webview.start(debug=True)
logger.info("GUI 視窗已關閉")
except Exception as e:
logger.error(f"GUI 啟動失敗: {str(e)}")
raise
def run(self):
"""
執行應用程式
啟動順序:
1. 在背景執行緒啟動 FastAPI 伺服器
2. 在主執行緒啟動 PyWebview GUI
"""
logger.info("=== TimeLine Designer 啟動中 ===")
# 在背景執行緒啟動 API 伺服器
api_thread = threading.Thread(target=self.start_api_server, daemon=True)
api_thread.start()
# 等待 API 伺服器啟動
import time
logger.info("等待 API 伺服器啟動...")
time.sleep(2)
# 在主執行緒啟動 GUI
self.start_gui()
logger.info("=== TimeLine Designer 已關閉 ===")
def main():
"""
應用程式入口點
"""
try:
app = TimelineDesignerApp()
app.run()
except KeyboardInterrupt:
logger.info("使用者中斷程式")
sys.exit(0)
except Exception as e:
logger.error(f"應用程式錯誤: {str(e)}")
sys.exit(1)
if __name__ == "__main__":
main()