codeSearch/ui/fault_finder_old.py

1118 lines
44 KiB
Python

import flet as ft
import sqlite3
from typing import List, Callable
from database.db_manager import DatabaseManager
class FaultFinderUI:
"""고장코드 검색 UI를 관리하는 클래스입니다."""
def __init__(self, page: ft.Page, db_manager: DatabaseManager):
"""
FaultFinderUI를 초기화합니다.
Args:
page (ft.Page): Flet 페이지 객체
db_manager (DatabaseManager): 데이터베이스 관리자 인스턴스
"""
self.page = page
self.db_manager = db_manager
self.current_view = "fault_code" # 현재 보기 모드 (fault_code, abbreviation, drawing, signal)
# 디버그 모드 설정 (문제 해결용)
self.debug = False
# 테마 설정 (기본값: 라이트 모드)
self.theme = "light"
# 전체 제작사 목록 가져오기
self.manufacturers = self._get_manufacturers()
# 우진을 기본값으로 설정
default_manufacturer = "우진"
if default_manufacturer in self.manufacturers:
default_index = self.manufacturers.index(default_manufacturer)
else:
default_index = 0
# 설정 버튼 (톱니바퀴 아이콘)
self.settings_button = ft.IconButton(
icon=ft.icons.SETTINGS,
tooltip="설정",
on_click=self._show_settings_dialog
)
# 최상단: 제작사 필터 및 설정 버튼
self.manufacturer_dropdown = ft.Dropdown(
label="제작사 선택",
options=[ft.dropdown.Option(manufacturer) for manufacturer in self.manufacturers],
on_change=self._on_manufacturer_change,
value=self.manufacturers[default_index] if self.manufacturers else None,
expand=True
)
# 제작사 필터와 설정 버튼을 포함하는 Row
self.manufacturer_row = ft.Row([
self.manufacturer_dropdown,
self.settings_button
], spacing=10)
# 전체 고장 타입 목록 가져오기
self.fault_types = self._get_fault_types()
# '전체' 옵션 추가
if "전체" not in self.fault_types:
self.fault_types.insert(0, "전체")
# 고장 타입 필터
self.type_dropdown = ft.Dropdown(
label="장치분류 선택",
options=[ft.dropdown.Option(type_) for type_ in self.fault_types],
on_change=self._on_type_change,
expand=False,
width=300, # 너비 증가
# height=40, # 적절한 높이 설정
value="전체" # 기본값을 '전체'로 설정
)
# 뷰 전환 탭 (정비 자료)
self.view_tabs = ft.Tabs(
selected_index=0,
on_change=self._on_tabs_change,
tabs=[
ft.Tab(text="고장코드", icon=ft.icons.ERROR_OUTLINE),
ft.Tab(text="약어목록", icon=ft.icons.MENU_BOOK),
ft.Tab(text="도면목록", icon=ft.icons.DOCUMENT_SCANNER),
ft.Tab(text="신호목록", icon=ft.icons.SIGNAL_CELLULAR_ALT)
],
expand=True
)
# 고장 리스트
self.fault_list = ft.DataTable(
columns=[
ft.DataColumn(ft.Text("고장코드", weight=ft.FontWeight.BOLD)), # 첫번째 열 강조
ft.DataColumn(ft.Text("고장명")),
ft.DataColumn(ft.Text("고장타입"))
],
rows=[],
expand=False,
show_bottom_border=True,
column_spacing=10,
horizontal_lines=ft.border.BorderSide(1, "grey")
)
# 약어 리스트
self.abbreviation_list = ft.DataTable(
columns=[
ft.DataColumn(ft.Text("약어", weight=ft.FontWeight.BOLD)), # 첫번째 열 강조
ft.DataColumn(ft.Text("용어설명"))
],
rows=[],
expand=False,
show_bottom_border=True,
column_spacing=10,
horizontal_lines=ft.border.BorderSide(1, "grey"),
visible=False # 기본적으로 보이지 않음
)
# 도면 리스트
self.drawing_list = ft.DataTable(
columns=[
ft.DataColumn(ft.Text("도면코드", weight=ft.FontWeight.BOLD)), # 첫번째 열 강조
ft.DataColumn(ft.Text("도면명")),
ft.DataColumn(ft.Text("관련장치"))
],
rows=[],
expand=False,
show_bottom_border=True,
column_spacing=10,
horizontal_lines=ft.border.BorderSide(1, "grey"),
visible=False # 기본적으로 보이지 않음
)
# 신호 리스트
self.signal_list = ft.DataTable(
columns=[
ft.DataColumn(ft.Text("신호코드", weight=ft.FontWeight.BOLD)), # 첫번째 열 강조
ft.DataColumn(ft.Text("신호명")),
ft.DataColumn(ft.Text("신호타입"))
],
rows=[],
expand=False,
show_bottom_border=True,
column_spacing=10,
horizontal_lines=ft.border.BorderSide(1, "grey"),
visible=False # 기본적으로 보이지 않음
)
# 중앙 리스트 컨테이너 - 단순화된 구조
self.list_container = ft.Container(
content=ft.Column([
# 고장코드 뷰
ft.Column([
# 고정 헤더 (첫 번째 행)
ft.Container(
content=ft.Row([
ft.Text("고장코드", weight=ft.FontWeight.BOLD, width=150),
ft.Text("고장명", weight=ft.FontWeight.BOLD, width=200),
ft.Text("고장타입", weight=ft.FontWeight.BOLD, width=150)
],
spacing=10,
width=500
),
bgcolor=ft.colors.BLUE_GREY_100,
padding=10,
border_radius=5,
visible=True
),
# 스크롤 가능한 내용
ft.Container(
content=ft.Column(
controls=[], # 동적으로 채워질 항목들
spacing=2,
scroll=ft.ScrollMode.ALWAYS
),
height=340, # 컨테이너 높이에서 헤더 높이를 뺀 값
expand=False,
padding=5,
visible=True
)
], visible=True),
# 약어 뷰
ft.Column([
# 고정 헤더 (첫 번째 행)
ft.Container(
content=ft.Row([
ft.Text("약어", weight=ft.FontWeight.BOLD, width=150),
ft.Text("용어설명", weight=ft.FontWeight.BOLD, width=350)
],
spacing=10,
width=500
),
bgcolor=ft.colors.BLUE_GREY_100,
padding=10,
border_radius=5,
visible=True
),
# 스크롤 가능한 내용
ft.Container(
content=ft.Column(
controls=[], # 동적으로 채워질 항목들
spacing=2,
scroll=ft.ScrollMode.ALWAYS
),
height=340, # 컨테이너 높이에서 헤더 높이를 뺀 값
expand=False,
padding=5,
visible=True
)
], visible=False),
# 도면 뷰
ft.Column([
# 고정 헤더 (첫 번째 행)
ft.Container(
content=ft.Row([
ft.Text("도면코드", weight=ft.FontWeight.BOLD, width=150),
ft.Text("도면명", weight=ft.FontWeight.BOLD, width=200),
ft.Text("관련장치", weight=ft.FontWeight.BOLD, width=150)
],
spacing=10,
width=500
),
bgcolor=ft.colors.BLUE_GREY_100,
padding=10,
border_radius=5,
visible=True
),
# 스크롤 가능한 내용
ft.Container(
content=ft.Column(
controls=[], # 동적으로 채워질 항목들
spacing=2,
scroll=ft.ScrollMode.ALWAYS
),
height=340, # 컨테이너 높이에서 헤더 높이를 뺀 값
expand=False,
padding=5,
visible=True
)
], visible=False),
# 신호 뷰
ft.Column([
# 고정 헤더 (첫 번째 행)
ft.Container(
content=ft.Row([
ft.Text("신호코드", weight=ft.FontWeight.BOLD, width=150),
ft.Text("신호명", weight=ft.FontWeight.BOLD, width=200),
ft.Text("신호타입", weight=ft.FontWeight.BOLD, width=150)
],
spacing=10,
width=500
),
bgcolor=ft.colors.BLUE_GREY_100,
padding=10,
border_radius=5,
visible=True
),
# 스크롤 가능한 내용
ft.Container(
content=ft.Column(
controls=[], # 동적으로 채워질 항목들
spacing=2,
scroll=ft.ScrollMode.ALWAYS
),
height=340, # 컨테이너 높이에서 헤더 높이를 뺀 값
expand=False,
padding=5,
visible=True
)
], visible=False),
]),
expand=True,
border=ft.border.all(1, ft.colors.GREY_300),
border_radius=10,
padding=10,
height=450, # 고정 높이 증가
clip_behavior=ft.ClipBehavior.HARD_EDGE # 내용이 컨테이너를 넘어가지 않도록 설정
)
# 하단: 검색 필드
self.search_field = ft.TextField(
label="검색어 입력",
hint_text="검색어를 입력하세요",
expand=True,
on_change=self._on_search_change,
prefix_icon=ft.icons.SEARCH
)
# 장치분류 영역 (고장코드 모드일 때만 보이도록 설정)
self.type_container = ft.Container(
content=self.type_dropdown,
padding=5,
bgcolor=ft.colors.BLUE_GREY_50,
border_radius=10,
visible=True # 초기에는 고장코드 모드이므로 표시
)
# 메인 컨테이너 구성
self.container = ft.Container(
content=ft.Column([
# 최상단: 제작사 필터 (설정 버튼이 추가된 Row로 교체)
ft.Container(
content=self.manufacturer_row,
padding=10,
bgcolor=ft.colors.BLUE_GREY_50,
border_radius=10
),
# 중간 상단: 탭과 장치분류 영역을 담은 Row를 fixed height로 지정
ft.Container(
content=ft.Row(
controls=[
ft.Container(
content=ft.Column(
controls = [self.view_tabs]
),
# expand 제거
padding=5,
bgcolor=ft.colors.BLUE_GREY_50,
border_radius=10
),
],
spacing=10
),
height=100, # 고정 높이 지정 (필요에 따라 조정)
padding=5
),
# 장치분류 컨테이너 추가
self.type_container,
# 중앙: 리스트 (고장/약어/도면/신호)
self.list_container,
# 하단: 검색 필드
ft.Container(
content=self.search_field,
padding=10,
bgcolor=ft.colors.BLUE_GREY_50,
border_radius=10
)
], spacing=10),
expand=True,
padding=10
)
# 초기 데이터 로딩
self.load_all_codes()
def _on_tabs_change(self, e):
"""탭 변경 시 호출되는 함수"""
index = self.view_tabs.selected_index
if index == 0:
self._switch_view("fault_code")
elif index == 1:
self._switch_view("abbreviation")
elif index == 2:
self._switch_view("drawing")
elif index == 3:
self._switch_view("signal")
def _switch_view(self, view_type: str):
"""뷰 전환 및 해당 데이터 로드"""
if self.debug:
print(f"뷰 전환: {view_type}")
self.current_view = view_type
# 뷰 컨테이너 가시성 설정
self.list_container.content.controls[0].visible = (view_type == "fault_code") # 고장코드 뷰
self.list_container.content.controls[1].visible = (view_type == "abbreviation") # 약어 뷰
self.list_container.content.controls[2].visible = (view_type == "drawing") # 도면 뷰
self.list_container.content.controls[3].visible = (view_type == "signal") # 신호 뷰
# 장치분류 드롭다운은 고장코드 모드일 때만 표시
self.type_container.visible = (view_type == "fault_code")
if view_type == "fault_code":
self.load_all_codes()
elif view_type == "abbreviation":
self.load_abbreviations()
elif view_type == "drawing":
self.load_drawings()
elif view_type == "signal":
self.load_signals()
# 현재 컨테이너 상태 확인 (디버깅용)
if self.debug:
print(f"고장코드 뷰: visible={self.list_container.content.controls[0].visible}")
print(f"약어 뷰: visible={self.list_container.content.controls[1].visible}")
print(f"도면 뷰: visible={self.list_container.content.controls[2].visible}")
print(f"신호 뷰: visible={self.list_container.content.controls[3].visible}")
self.page.update()
def _get_manufacturers(self) -> List[str]:
"""제작사 목록을 가져옵니다."""
conn = sqlite3.connect('fault_codes.db')
cursor = conn.cursor()
cursor.execute("SELECT name FROM manufacturers ORDER BY name")
manufacturers = [manufacturer[0] for manufacturer in cursor.fetchall()]
conn.close()
return manufacturers
def _get_fault_types(self) -> List[str]:
"""고장 타입 목록을 가져옵니다."""
conn = sqlite3.connect('fault_codes.db')
cursor = conn.cursor()
cursor.execute("SELECT DISTINCT fault_type FROM fault_code_list ORDER BY fault_type")
types = [type_[0] for type_ in cursor.fetchall()]
conn.close()
return types
def load_all_codes(self):
"""전체 고장 코드 목록을 로드합니다."""
conn = sqlite3.connect('fault_codes.db')
cursor = conn.cursor()
cursor.execute("""
SELECT fault_code, fault_name, fault_type
FROM fault_code_list
ORDER BY fault_code
""")
results = cursor.fetchall()
conn.close()
# 고장코드 리스트에 데이터 표시 (새 구조에 맞게 업데이트)
fault_list_view = self.list_container.content.controls[0].controls[1].content
fault_list_view.controls.clear()
for code, name, type_ in results:
row = ft.Container(
content=ft.Row([
ft.Text(str(code), width=150),
ft.Text(name, width=200),
ft.Text(type_, width=150)
],
spacing=10,
width=500
),
on_click=lambda e, code=code, name=name, type_=type_: self._show_fault_details(code, name, type_),
bgcolor=ft.colors.WHITE,
padding=10,
border_radius=5,
border=ft.border.all(1, ft.colors.GREY_300)
)
fault_list_view.controls.append(row)
if self.debug:
print(f"고장코드 목록 {len(results)}개 로드 완료")
self.page.update()
def load_abbreviations(self):
"""약어 목록을 로드합니다."""
try:
conn = sqlite3.connect('fault_codes.db')
cursor = conn.cursor()
cursor.execute("""
SELECT abbreviation, description
FROM abbreviations
ORDER BY abbreviation
""")
results = cursor.fetchall()
conn.close()
# 새 UI 구조에 맞게 검색 결과 표시
abbr_list_view = self.list_container.content.controls[1].controls[1].content
abbr_list_view.controls.clear()
for abbr, desc in results:
row = ft.Container(
content=ft.Row([
ft.Text(abbr, width=150),
ft.Text(desc, width=350)
],
spacing=10,
width=500
),
bgcolor=ft.colors.WHITE,
padding=10,
border_radius=5,
border=ft.border.all(1, ft.colors.GREY_300)
)
abbr_list_view.controls.append(row)
if self.debug:
print(f"약어 목록 {len(results)}개 로드 완료")
self.page.update()
except sqlite3.OperationalError as e:
print(f"약어 로드 오류: {e}")
def load_drawings(self):
"""도면 목록을 로드합니다."""
try:
conn = sqlite3.connect('fault_codes.db')
cursor = conn.cursor()
cursor.execute("""
SELECT drawing_code, drawing_name, related_device
FROM drawings
ORDER BY drawing_code
""")
results = cursor.fetchall()
conn.close()
# 새 UI 구조에 맞게 검색 결과 표시
drawing_list_view = self.list_container.content.controls[2].controls[1].content
drawing_list_view.controls.clear()
for code, name, device in results:
row = ft.Container(
content=ft.Row([
ft.Text(code, width=150),
ft.Text(name, width=200),
ft.Text(device, width=150)
],
spacing=10,
width=500
),
bgcolor=ft.colors.WHITE,
padding=10,
border_radius=5,
border=ft.border.all(1, ft.colors.GREY_300)
)
drawing_list_view.controls.append(row)
if self.debug:
print(f"도면 목록 {len(results)}개 로드 완료")
self.page.update()
except sqlite3.OperationalError as e:
print(f"도면 로드 오류: {e}")
def load_signals(self):
"""신호 목록을 로드합니다."""
try:
conn = sqlite3.connect('fault_codes.db')
cursor = conn.cursor()
cursor.execute("""
SELECT signal_code, signal_name, signal_type
FROM signals
ORDER BY signal_code
""")
results = cursor.fetchall()
conn.close()
# 새 UI 구조에 맞게 검색 결과 표시
signal_list_view = self.list_container.content.controls[3].controls[1].content
signal_list_view.controls.clear()
for code, name, type_ in results:
row = ft.Container(
content=ft.Row([
ft.Text(code, width=150),
ft.Text(name, width=200),
ft.Text(type_, width=150)
],
spacing=10,
width=500
),
on_click=lambda e, code=code, name=name, type_=type_: self._show_signal_details(code, name, type_),
bgcolor=ft.colors.WHITE,
padding=10,
border_radius=5,
border=ft.border.all(1, ft.colors.GREY_300)
)
signal_list_view.controls.append(row)
if self.debug:
print(f"신호 목록 {len(results)}개 로드 완료")
self.page.update()
except sqlite3.OperationalError as e:
print(f"신호 로드 오류: {e}")
def _on_manufacturer_change(self, e):
"""
제작사 선택 시 호출되는 함수
제작사만 선택된 경우와 제작사+고장 타입 선택 시 각각 필터 적용
"""
if self.current_view != "fault_code":
return
selected_manufacturer = self.manufacturer_dropdown.value
selected_type = self.type_dropdown.value
# 장치분류의 괄호 부분 제거 (예: "BECU(200)" -> "BECU")
if selected_type and "(" in selected_type:
selected_type = selected_type.split("(")[0].strip()
# 디버깅 정보 출력
if self.debug:
print(f"제작사 변경: manufacturer={selected_manufacturer}, type={selected_type}")
if selected_manufacturer:
conn = sqlite3.connect('fault_codes.db')
cursor = conn.cursor()
if not selected_type or selected_type == "전체":
cursor.execute("""
SELECT fault_code, fault_name, fault_type
FROM fault_code_list
WHERE manufacturer_id = (SELECT id FROM manufacturers WHERE name = ?)
ORDER BY fault_code
""", (selected_manufacturer,))
else:
cursor.execute("""
SELECT fault_code, fault_name, fault_type
FROM fault_code_list
WHERE LOWER(fault_type) LIKE LOWER(?) AND manufacturer_id = (SELECT id FROM manufacturers WHERE name = ?)
ORDER BY fault_code
""", (f"%{selected_type}%", selected_manufacturer))
results = cursor.fetchall()
if self.debug:
print(f"조회된 고장코드 수: {len(results)}")
conn.close()
# 새 UI 구조에 맞게 결과 표시
fault_list_view = self.list_container.content.controls[0].controls[1].content
fault_list_view.controls.clear()
for code, name, type_ in results:
row = ft.Container(
content=ft.Row([
ft.Text(str(code), width=150),
ft.Text(name, width=200),
ft.Text(type_, width=150)
],
spacing=10,
width=500
),
on_click=lambda e, code=code, name=name, type_=type_: self._show_fault_details(code, name, type_),
bgcolor=ft.colors.WHITE,
padding=10,
border_radius=5,
border=ft.border.all(1, ft.colors.GREY_300)
)
fault_list_view.controls.append(row)
self.page.update()
else:
self.load_all_codes()
def _on_type_change(self, e):
"""
고장 타입 선택 시 호출되는 함수
고장 타입만 선택된 경우와 제작사+고장 타입 선택 시 각각 필터 적용
"""
if self.current_view != "fault_code":
return
selected_type = self.type_dropdown.value
# 괄호 부분 제거 (예: "BECU(200)" -> "BECU")
if selected_type and "(" in selected_type:
selected_type = selected_type.split("(")[0].strip()
# 디버깅 정보 출력
if self.debug:
print(f"타입 변경: selected_type={selected_type}")
selected_manufacturer = self.manufacturer_dropdown.value
if selected_type and selected_type != "전체":
conn = sqlite3.connect('fault_codes.db')
cursor = conn.cursor()
if not selected_manufacturer:
cursor.execute("""
SELECT fault_code, fault_name, fault_type
FROM fault_code_list
WHERE LOWER(fault_type) LIKE LOWER(?)
ORDER BY fault_code
""", (f"%{selected_type}%",))
else:
cursor.execute("""
SELECT fault_code, fault_name, fault_type
FROM fault_code_list
WHERE LOWER(fault_type) LIKE LOWER(?) AND manufacturer_id = (SELECT id FROM manufacturers WHERE name = ?)
ORDER BY fault_code
""", (f"%{selected_type}%", selected_manufacturer))
results = cursor.fetchall()
if self.debug:
print(f"장치분류 필터링 결과: {len(results)}개 항목")
conn.close()
# 새 UI 구조에 맞게 결과 표시
fault_list_view = self.list_container.content.controls[0].controls[1].content
fault_list_view.controls.clear()
for code, name, type_ in results:
row = ft.Container(
content=ft.Row([
ft.Text(str(code), width=150),
ft.Text(name, width=200),
ft.Text(type_, width=150)
],
spacing=10,
width=500
),
on_click=lambda e, code=code, name=name, type_=type_: self._show_fault_details(code, name, type_),
bgcolor=ft.colors.WHITE,
padding=10,
border_radius=5,
border=ft.border.all(1, ft.colors.GREY_300)
)
fault_list_view.controls.append(row)
self.page.update()
else:
self._on_manufacturer_change(e)
def _on_search_change(self, e):
"""
검색어가 변경될 때 호출되는 함수.
현재 뷰에 따라 알맞은 검색 실행
"""
search_term = self.search_field.value
if not search_term:
if self.current_view == "fault_code":
self.load_all_codes()
elif self.current_view == "abbreviation":
self.load_abbreviations()
elif self.current_view == "drawing":
self.load_drawings()
elif self.current_view == "signal":
self.load_signals()
return
if self.current_view == "fault_code":
self._search_fault_codes(search_term)
elif self.current_view == "abbreviation":
self._search_abbreviations(search_term)
elif self.current_view == "drawing":
self._search_drawings(search_term)
elif self.current_view == "signal":
self._search_signals(search_term)
def _search_fault_codes(self, search_term: str):
"""고장코드 검색 실행"""
selected_type = self.type_dropdown.value
# '전체' 선택 시 타입 필터링 제외
if selected_type == "전체":
selected_type = None
# 괄호 부분 제거
if selected_type and "(" in selected_type:
selected_type = selected_type.split("(")[0].strip()
selected_manufacturer = self.manufacturer_dropdown.value
conn = sqlite3.connect('fault_codes.db')
cursor = conn.cursor()
if not selected_type:
cursor.execute("""
SELECT fault_code, fault_name, fault_type
FROM fault_code_list
WHERE LOWER(fault_code) LIKE LOWER(?) OR LOWER(fault_name) LIKE LOWER(?)
ORDER BY fault_code
""", (f'%{search_term}%', f'%{search_term}%'))
else:
cursor.execute("""
SELECT fault_code, fault_name, fault_type
FROM fault_code_list
WHERE LOWER(fault_type) LIKE LOWER(?) AND (LOWER(fault_code) LIKE LOWER(?) OR LOWER(fault_name) LIKE LOWER(?))
ORDER BY fault_code
""", (f"%{selected_type}%", f'%{search_term}%', f'%{search_term}%'))
results = cursor.fetchall()
if self.debug:
print(f"검색 결과: {len(results)}개 항목 (검색어: {search_term})")
conn.close()
# 새 UI 구조에 맞게 검색 결과 표시
fault_list_view = self.list_container.content.controls[0].controls[1].content
fault_list_view.controls.clear()
for code, name, type_ in results:
row = ft.Container(
content=ft.Row([
ft.Text(str(code), width=150),
ft.Text(name, width=200),
ft.Text(type_, width=150)
],
spacing=10,
width=500
),
on_click=lambda e, code=code, name=name, type_=type_: self._show_fault_details(code, name, type_),
bgcolor=ft.colors.WHITE,
padding=10,
border_radius=5,
border=ft.border.all(1, ft.colors.GREY_300)
)
fault_list_view.controls.append(row)
self.page.update()
def _search_abbreviations(self, search_term: str):
"""약어 검색 실행"""
try:
conn = sqlite3.connect('fault_codes.db')
cursor = conn.cursor()
cursor.execute("""
SELECT abbreviation, description
FROM abbreviations
WHERE LOWER(abbreviation) LIKE LOWER(?) OR LOWER(description) LIKE LOWER(?)
ORDER BY abbreviation
""", (f'%{search_term}%', f'%{search_term}%'))
results = cursor.fetchall()
conn.close()
# 새 UI 구조에 맞게 검색 결과 표시
abbr_list_view = self.list_container.content.controls[1].controls[1].content
abbr_list_view.controls.clear()
for abbr, desc in results:
row = ft.Container(
content=ft.Row([
ft.Text(abbr, width=150),
ft.Text(desc, width=350)
],
spacing=10,
width=500
),
bgcolor=ft.colors.WHITE,
padding=10,
border_radius=5,
border=ft.border.all(1, ft.colors.GREY_300)
)
abbr_list_view.controls.append(row)
if self.debug:
print(f"약어 검색 결과: {len(results)}개 항목 (검색어: {search_term})")
self.page.update()
except sqlite3.OperationalError as e:
print(f"약어 로드 오류: {e}")
def _search_drawings(self, search_term: str):
"""도면 검색 실행"""
try:
conn = sqlite3.connect('fault_codes.db')
cursor = conn.cursor()
cursor.execute("""
SELECT drawing_code, drawing_name, related_device
FROM drawings
WHERE LOWER(drawing_code) LIKE LOWER(?) OR LOWER(drawing_name) LIKE LOWER(?) OR LOWER(related_device) LIKE LOWER(?)
ORDER BY drawing_code
""", (f'%{search_term}%', f'%{search_term}%', f'%{search_term}%'))
results = cursor.fetchall()
conn.close()
# 새 UI 구조에 맞게 검색 결과 표시
drawing_list_view = self.list_container.content.controls[2].controls[1].content
drawing_list_view.controls.clear()
for code, name, device in results:
row = ft.Container(
content=ft.Row([
ft.Text(code, width=150),
ft.Text(name, width=200),
ft.Text(device, width=150)
],
spacing=10,
width=500
),
bgcolor=ft.colors.WHITE,
padding=10,
border_radius=5,
border=ft.border.all(1, ft.colors.GREY_300)
)
drawing_list_view.controls.append(row)
if self.debug:
print(f"도면 검색 결과: {len(results)}개 항목 (검색어: {search_term})")
self.page.update()
except sqlite3.OperationalError as e:
print(f"도면 로드 오류: {e}")
def _search_signals(self, search_term: str):
"""신호 검색 실행"""
try:
conn = sqlite3.connect('fault_codes.db')
cursor = conn.cursor()
cursor.execute("""
SELECT signal_code, signal_name, signal_type
FROM signals
WHERE LOWER(signal_code) LIKE LOWER(?) OR LOWER(signal_name) LIKE LOWER(?) OR LOWER(signal_type) LIKE LOWER(?)
ORDER BY signal_code
""", (f'%{search_term}%', f'%{search_term}%', f'%{search_term}%'))
results = cursor.fetchall()
conn.close()
# 새 UI 구조에 맞게 검색 결과 표시
signal_list_view = self.list_container.content.controls[3].controls[1].content
signal_list_view.controls.clear()
for code, name, type_ in results:
row = ft.Container(
content=ft.Row([
ft.Text(code, width=150),
ft.Text(name, width=200),
ft.Text(type_, width=150)
],
spacing=10,
width=500
),
on_click=lambda e, code=code, name=name, type_=type_: self._show_signal_details(code, name, type_),
bgcolor=ft.colors.WHITE,
padding=10,
border_radius=5,
border=ft.border.all(1, ft.colors.GREY_300)
)
signal_list_view.controls.append(row)
if self.debug:
print(f"신호 검색 결과: {len(results)}개 항목 (검색어: {search_term})")
self.page.update()
except sqlite3.OperationalError as e:
print(f"신호 로드 오류: {e}")
def _show_fault_details(self, code, name, type_):
"""고장 상세 정보를 다이얼로그로 표시"""
conn = sqlite3.connect('fault_codes.db')
cursor = conn.cursor()
cursor.execute("""
SELECT fault_detail, reaction, detect_condition, clear_condition, action
FROM fault_code_list
WHERE fault_code = ?
""", (code,))
details = cursor.fetchone()
conn.close()
if details:
detail, reaction, detect, clear, action = details
dialog = ft.AlertDialog(
title=ft.Text(f"[{code}] {name}", weight=ft.FontWeight.BOLD),
content=ft.Column([
ft.Text("고장 타입: " + type_, color=ft.colors.BLUE_GREY_700),
ft.Divider(),
ft.Text("고장 상세:", weight=ft.FontWeight.BOLD),
ft.Text(detail, selectable=True),
ft.Divider(),
ft.Text("반응:", weight=ft.FontWeight.BOLD),
ft.Text(reaction, selectable=True),
ft.Divider(),
ft.Text("검지 조건:", weight=ft.FontWeight.BOLD),
ft.Text(detect, selectable=True),
ft.Divider(),
ft.Text("소거 조건:", weight=ft.FontWeight.BOLD),
ft.Text(clear, selectable=True),
ft.Divider(),
ft.Text("조치 방법:", weight=ft.FontWeight.BOLD),
ft.Text(action, selectable=True)
], width=500, height=500, scroll=ft.ScrollMode.AUTO),
actions=[ft.TextButton("닫기", on_click=lambda e: self._close_dialog())]
)
self.page.dialog = dialog
dialog.open = True
self.page.update()
def _show_signal_details(self, code, name, type_):
"""신호 상세 정보를 다이얼로그로 표시"""
conn = sqlite3.connect('fault_codes.db')
cursor = conn.cursor()
cursor.execute("""
SELECT description, source, destination, unit, value_range
FROM signals
WHERE signal_code = ?
""", (code,))
details = cursor.fetchone()
conn.close()
if details:
description, source, destination, unit, value_range = details
dialog = ft.AlertDialog(
title=ft.Text(f"[{code}] {name}", weight=ft.FontWeight.BOLD),
content=ft.Column([
ft.Text("신호 타입: " + type_, color=ft.colors.BLUE_GREY_700),
ft.Divider(),
ft.Text("설명:", weight=ft.FontWeight.BOLD),
ft.Text(description, selectable=True),
ft.Divider(),
ft.Text("소스:", weight=ft.FontWeight.BOLD),
ft.Text(source, selectable=True),
ft.Divider(),
ft.Text("목적지:", weight=ft.FontWeight.BOLD),
ft.Text(destination, selectable=True),
ft.Divider(),
ft.Text("단위:", weight=ft.FontWeight.BOLD),
ft.Text(unit, selectable=True),
ft.Divider(),
ft.Text("값 범위:", weight=ft.FontWeight.BOLD),
ft.Text(value_range, selectable=True)
], width=500, height=500, scroll=ft.ScrollMode.AUTO),
actions=[ft.TextButton("닫기", on_click=lambda e: self._close_dialog())]
)
self.page.dialog = dialog
dialog.open = True
self.page.update()
def _close_dialog(self):
"""다이얼로그 닫기 메서드"""
if self.page.dialog:
self.page.dialog.open = False
self.page.update()
def _show_settings_dialog(self, e):
"""설정 다이얼로그를 표시하는 메서드"""
# 테마 선택 라디오 그룹
theme_radio = ft.RadioGroup(
content=ft.Column([
ft.Radio(value="light", label="라이트 모드"),
ft.Radio(value="dark", label="다크 모드")
]),
value=self.theme,
on_change=self._on_theme_change
)
dialog = ft.AlertDialog(
title=ft.Text("설정", weight=ft.FontWeight.BOLD),
content=ft.Column([
ft.Text("테마 설정", weight=ft.FontWeight.BOLD),
theme_radio,
ft.Divider(),
ft.Text("제작자 정보", weight=ft.FontWeight.BOLD),
ft.Text("신평-Choi KyungHwan", color=ft.colors.BLUE_GREY_700)
], width=300, height=250, scroll=ft.ScrollMode.AUTO),
actions=[ft.TextButton("닫기", on_click=lambda e: self._close_settings_dialog())]
)
self.page.dialog = dialog
dialog.open = True
self.page.update()
def _close_settings_dialog(self):
"""설정 다이얼로그를 닫는 메서드"""
if self.page.dialog:
self.page.dialog.open = False
self.page.update()
def _on_theme_change(self, e):
"""테마 변경 메서드"""
self.theme = e.control.value
if self.theme == "light":
self.page.theme_mode = ft.ThemeMode.LIGHT
else:
self.page.theme_mode = ft.ThemeMode.DARK
self.page.update()
def create_search_field(self, on_change: Callable) -> ft.TextField:
"""검색 필드를 생성합니다."""
self.search_field = ft.TextField(
label="검색",
hint_text="고장코드, 고장명, 분류 등을 입력하세요",
on_change=on_change,
expand=True
)
return self.search_field
def create_data_table(self, data: list) -> ft.DataTable:
"""검색 결과를 위한 데이터 테이블을 생성합니다."""
columns = [
ft.DataColumn(ft.Text("고장코드")),
ft.DataColumn(ft.Text("고장명")),
ft.DataColumn(ft.Text("등급")),
ft.DataColumn(ft.Text("분류")),
ft.DataColumn(ft.Text("고장내용")),
ft.DataColumn(ft.Text("반응")),
ft.DataColumn(ft.Text("검지조건")),
ft.DataColumn(ft.Text("소거조건")),
ft.DataColumn(ft.Text("조치방법"))
]
rows = []
for row in data:
cells = [ft.DataCell(ft.Text(str(cell))) for cell in row]
rows.append(ft.DataRow(cells=cells))
self.data_table = ft.DataTable(
columns=columns,
rows=rows,
border=ft.border.all(1, "grey"),
border_radius=10,
vertical_lines=ft.border.BorderSide(1, "grey"),
horizontal_lines=ft.border.BorderSide(1, "grey"),
)
return self.data_table
def update_table(self, data: list):
"""테이블 데이터를 업데이트합니다."""
new_table = self.create_data_table(data)
self.data_table.columns = new_table.columns
self.data_table.rows = new_table.rows
def create_layout(self) -> ft.Column:
"""메인 레이아웃을 생성합니다."""
return ft.Column([
ft.Container(content=self.search_field, padding=10),
ft.Container(
content=ft.Column([
ft.Text("검색 결과", size=20, weight=ft.FontWeight.BOLD),
ft.Container(content=self.data_table, height=500)
]),
padding=10,
expand=True,
)
])