Files
Pier_v1/pierzainer_ground/pierzainer_ground/app.py
T

100 lines
5.6 KiB
Python

"""Two-file, AI-free ground-model generator."""
from pathlib import Path
import os
import queue
import re
import threading
import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext, ttk
from .drawing_job import ROOT, build_from_files
class GroundModelApp(tk.Tk):
def __init__(self):
super().__init__()
self.title('Pierzainer 지반모델링 · 도면 + Excel 자동 생성 v2')
self.geometry('1000x640');self.minsize(850,540)
self.drawing=tk.StringVar();self.piers=tk.StringVar()
self.output=tk.StringVar(value=str(ROOT/'data/output'))
self.status=tk.StringVar(value='종평면도와 교각 Excel을 선택하세요')
self.messages=queue.Queue();self.busy=False
self._build();self.after(100,self._poll)
def _build(self):
main=ttk.Frame(self,padding=16);main.pack(fill='both',expand=True)
main.columnconfigure(1,weight=1);main.rowconfigure(7,weight=1)
ttk.Label(main,text='종평면도 + 교각 Excel → 지반 모델 JSON',font=('Malgun Gothic',16,'bold')).grid(row=0,column=0,columnspan=3,sticky='w',pady=(0,14))
for row,key,label in ((1,'drawing','① 종평면도 DXF/DWG'),(2,'piers','② 교각 Excel')):
ttk.Label(main,text=label).grid(row=row,column=0,sticky='w',pady=6)
ttk.Entry(main,textvariable=getattr(self,key)).grid(row=row,column=1,sticky='ew',padx=10)
ttk.Button(main,text='찾기',command=lambda k=key:self._browse(k)).grid(row=row,column=2)
ttk.Label(main,text='결과 저장 위치').grid(row=3,column=0,sticky='w',pady=6)
ttk.Entry(main,textvariable=self.output,state='readonly').grid(row=3,column=1,columnspan=2,sticky='ew',padx=(10,0))
ttk.Label(main,text='두 파일만 선택하면 지반면 + 위성사진 + 2D 지도를 생성합니다. AI 연결 없이 OSM/Esri 인터넷 지도를 사용합니다.\n원본에 좌표계 표기가 없으면 프로젝트 기본 EPSG:5186을 적용하고, 좌표계 가정으로 표시합니다.',foreground='#4f6070',wraplength=920).grid(row=4,column=0,columnspan=3,sticky='w',pady=12)
buttons=ttk.Frame(main);buttons.grid(row=5,column=0,columnspan=3,sticky='ew',pady=6)
self.run_button=ttk.Button(buttons,text='지반 모델 자동 생성',command=self._start_run);self.run_button.pack(side='left')
ttk.Button(buttons,text='결과 폴더 열기',command=self._open_out).pack(side='left',padx=10)
ttk.Label(main,textvariable=self.status).grid(row=6,column=0,columnspan=3,sticky='w',pady=6)
self.log=scrolledtext.ScrolledText(main,height=18,font=('Consolas',10))
self.log.grid(row=7,column=0,columnspan=3,sticky='nsew')
self._write('두 파일을 선택하고 「지반 모델 자동 생성」을 누르세요.\n블록명·EPSG·축 방향·설정 파일을 입력할 필요가 없습니다.\n')
def _write(self,text):
self.log.insert('end',text);self.log.see('end')
def _browse(self,key):
types=[('CAD 도면','*.dxf *.dwg')] if key=='drawing' else [('교각 Excel','*.xlsx')]
path=filedialog.askopenfilename(filetypes=types)
if not path:return
getattr(self,key).set(path)
if key=='drawing':self._output_folder()
def _output_folder(self):
name=re.sub(r'[^\w.-]+','_',Path(self.drawing.get()).stem).strip('.') or 'new_drawing'
self.output.set(str(ROOT/'data/output'/('drawing_'+name)))
def _open_out(self):
path=Path(self.output.get())
if not path.is_dir():messagebox.showinfo('결과 폴더','아직 생성된 결과가 없습니다.');return
os.startfile(str(path))
def _start_run(self):
if self.busy:return
drawing=self.drawing.get().strip();piers=self.piers.get().strip()
if not Path(drawing).is_file() or Path(drawing).suffix.lower() not in ('.dxf','.dwg'):
messagebox.showerror('파일 확인','종평면도 DXF/DWG를 선택하세요.');return
if not Path(piers).is_file() or Path(piers).suffix.lower()!='.xlsx':
messagebox.showerror('파일 확인','교각 Excel 파일을 선택하세요.');return
self._output_folder();out=self.output.get()
self.busy=True;self.run_button.state(['disabled']);self.status.set('자동 분석·생성 중...')
self.log.delete('1.0','end')
def worker():
try:
data=build_from_files(drawing,piers,lambda text:self.messages.put(('log',text)),output=out)
state=data.get('map_status',{}).get('status')
status='지반면 + 위성사진 + 2D 지도 생성 완료' if state=='mapped' else '지반면 JSON 생성 완료 · 지도는 확인 대기'
if state=='mapped' and data.get('surface_provenance',{}).get('auto_detection',{}).get('project_default_applied'):
status+=' · 프로젝트 기본 좌표계 적용'
self.messages.put(('done',status))
except Exception as exc:self.messages.put(('error',str(exc)))
threading.Thread(target=worker,daemon=True).start()
def _poll(self):
while True:
try:kind,text=self.messages.get_nowait()
except queue.Empty:break
self._write(text+'\n')
if kind in ('done','error'):
self.busy=False;self.run_button.state(['!disabled'])
self.status.set(text if kind=='done' else '입력 자료 확인 필요')
if kind=='error':messagebox.showerror('지반 모델 생성',text)
self.after(100,self._poll)
def main():
app=GroundModelApp();app.mainloop();return 0
if __name__=='__main__':raise SystemExit(main())