1118 lines
44 KiB
Python
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,
|
|
)
|
|
])
|
|
|