feat: 초기 프로젝트 구조 추가
This commit is contained in:
195
gui/image_settings_dialog.py
Normal file
195
gui/image_settings_dialog.py
Normal file
@@ -0,0 +1,195 @@
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QFormLayout, QVBoxLayout, QHBoxLayout,
|
||||
QPushButton, QDoubleSpinBox, QSpinBox, QMessageBox, QLabel
|
||||
)
|
||||
from PyQt5.QtCore import Qt
|
||||
|
||||
|
||||
# (레이블, 셀주소, 위젯종류, min, max, 기본값, 소수점자리)
|
||||
# D3(노출)은 AcqExposure() 출력 셀 — 읽기 전용, 목록에서 제외
|
||||
_PARAMS = [
|
||||
("최대 노출 시간", "F3", "double", 0.01, 1000.0, 950.0, 3),
|
||||
("목표 이미지 밝기", "G3", "double", 0.0, 255.0, 50.0, 1),
|
||||
("조명 강도", "D6", "int", 0, 100, 70, 0),
|
||||
("초점 위치", "D14", "int", 0, 999, 139, 0),
|
||||
]
|
||||
|
||||
|
||||
class ImageSettingsDialog(QDialog):
|
||||
def __init__(self, insight_cam, parent=None):
|
||||
super().__init__(parent)
|
||||
self._cam = insight_cam
|
||||
self.setWindowTitle("이미지 설정 — In-Sight 2000C")
|
||||
self.setMinimumWidth(360)
|
||||
self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
|
||||
|
||||
self._widgets = {} # 셀주소 → 위젯
|
||||
self._originals = {} # 셀주소 → 로드 시 원본값
|
||||
|
||||
self._build_ui()
|
||||
self._load_values()
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# UI 구성
|
||||
# ------------------------------------------------------------------ #
|
||||
|
||||
def _build_ui(self):
|
||||
form = QFormLayout()
|
||||
form.setLabelAlignment(Qt.AlignRight)
|
||||
form.setRowWrapPolicy(QFormLayout.DontWrapRows)
|
||||
form.setHorizontalSpacing(16)
|
||||
form.setVerticalSpacing(10)
|
||||
|
||||
# 노출(D3)은 읽기 전용 표시
|
||||
self._exposure_label = QLabel("—")
|
||||
self._exposure_label.setStyleSheet("color: gray;")
|
||||
form.addRow("노출 (밀리초, 읽기 전용):", self._exposure_label)
|
||||
|
||||
for label, cell, kind, lo, hi, default, decimals in _PARAMS:
|
||||
if kind == "double":
|
||||
w = QDoubleSpinBox()
|
||||
w.setRange(lo, hi)
|
||||
w.setDecimals(decimals)
|
||||
w.setValue(default)
|
||||
w.setSingleStep(0.1)
|
||||
else:
|
||||
w = QSpinBox()
|
||||
w.setRange(int(lo), int(hi))
|
||||
w.setValue(int(default))
|
||||
|
||||
w.setMinimumWidth(120)
|
||||
self._widgets[cell] = w
|
||||
form.addRow(label, w)
|
||||
|
||||
btn_ok = QPushButton("확인")
|
||||
btn_ok.setDefault(True)
|
||||
btn_ok.clicked.connect(self._on_ok)
|
||||
|
||||
btn_cancel = QPushButton("취소")
|
||||
btn_cancel.clicked.connect(self.reject)
|
||||
|
||||
btn_row = QHBoxLayout()
|
||||
btn_row.addStretch()
|
||||
btn_row.addWidget(btn_ok)
|
||||
btn_row.addWidget(btn_cancel)
|
||||
|
||||
root = QVBoxLayout(self)
|
||||
root.addLayout(form)
|
||||
root.addSpacing(8)
|
||||
root.addLayout(btn_row)
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# 값 로드 — GV{cell} 명령, 응답 2줄(상태코드 + 값)
|
||||
# ------------------------------------------------------------------ #
|
||||
|
||||
def _load_values(self):
|
||||
# D3: 읽기 전용 표시
|
||||
exp = self._gv("D3")
|
||||
print(f"[설정창] GVD3 → {exp!r}")
|
||||
if exp is not None:
|
||||
self._exposure_label.setText(f"{exp:.3f} ms")
|
||||
|
||||
for _, cell, kind, _, _, _, _ in _PARAMS:
|
||||
val = self._gv(cell)
|
||||
print(f"[설정창] GV{cell} → {val!r}")
|
||||
w = self._widgets[cell]
|
||||
if val is not None:
|
||||
if kind == "double":
|
||||
w.setValue(val)
|
||||
else:
|
||||
w.setValue(int(round(val)))
|
||||
self._originals[cell] = val
|
||||
else:
|
||||
self._originals[cell] = None
|
||||
|
||||
def _gv(self, cell: str):
|
||||
"""GV{cell} 전송 → 숫자 반환, 실패 시 None"""
|
||||
try:
|
||||
self._cam._send(f"GV{cell}")
|
||||
status = self._cam._read_line() # "1" 또는 "0"
|
||||
if status != "1":
|
||||
print(f"[설정창] GV{cell} 상태코드: {status!r}")
|
||||
return None
|
||||
raw = self._cam._read_line() # 실제 값
|
||||
return float(raw)
|
||||
except Exception as e:
|
||||
print(f"[설정창] GV{cell} 오류: {e}")
|
||||
return None
|
||||
|
||||
# ------------------------------------------------------------------ #
|
||||
# 확인 버튼 — SV{cell} {value} 명령
|
||||
# ------------------------------------------------------------------ #
|
||||
|
||||
def _on_ok(self):
|
||||
import time
|
||||
|
||||
# 변경 항목 수집
|
||||
changes = []
|
||||
for _, cell, kind, _, _, _, _ in _PARAMS:
|
||||
w = self._widgets[cell]
|
||||
current = float(w.value())
|
||||
original = self._originals.get(cell)
|
||||
if original is None or abs(current - original) >= 1e-9:
|
||||
changes.append((cell, current, kind))
|
||||
|
||||
if not changes:
|
||||
self.accept()
|
||||
return
|
||||
|
||||
# 오프라인 전환
|
||||
self._cam._send("SO0")
|
||||
resp = self._cam._read_line()
|
||||
print(f"[설정창] SO0 (오프라인) → {resp!r}")
|
||||
if resp.strip() != "1":
|
||||
QMessageBox.critical(self, "오류", f"오프라인 전환 실패 (응답: {resp!r})")
|
||||
return
|
||||
|
||||
time.sleep(0.3) # 카메라가 오프라인 전환 완료 대기
|
||||
|
||||
# 값 설정
|
||||
errors = []
|
||||
for cell, value, kind in changes:
|
||||
if not self._sv(cell, value, kind):
|
||||
errors.append(cell)
|
||||
|
||||
time.sleep(0.2) # SV 처리 완료 대기
|
||||
|
||||
# 온라인 복귀 — 최대 3회 재시도
|
||||
online_ok = False
|
||||
for attempt in range(3):
|
||||
self._cam._send("SO1")
|
||||
resp = self._cam._read_line()
|
||||
print(f"[설정창] SO1 (온라인) 시도 {attempt+1} → {resp!r}")
|
||||
if resp.strip() == "1":
|
||||
online_ok = True
|
||||
break
|
||||
time.sleep(0.5)
|
||||
|
||||
if not online_ok:
|
||||
QMessageBox.warning(
|
||||
self, "온라인 복귀 실패",
|
||||
f"SO1 명령이 실패했습니다 (마지막 응답: {resp!r})\n"
|
||||
"카메라를 수동으로 재시작하거나 In-Sight Explorer에서 Online으로 전환하세요."
|
||||
)
|
||||
|
||||
if errors:
|
||||
QMessageBox.critical(
|
||||
self, "설정 실패",
|
||||
f"다음 셀 설정 실패:\n{chr(10).join(errors)}\n\n"
|
||||
"셀이 읽기 전용이거나 값 범위를 벗어났을 수 있습니다."
|
||||
)
|
||||
return
|
||||
|
||||
self.accept()
|
||||
|
||||
def _sv(self, cell: str, value: float, kind: str) -> bool:
|
||||
"""SV{cell} {value} 전송 → 응답 '1'이면 True"""
|
||||
try:
|
||||
formatted = str(int(round(value))) if kind == "int" else f"{value:.6g}"
|
||||
self._cam._send(f"SV{cell} {formatted}")
|
||||
resp = self._cam._read_line()
|
||||
print(f"[설정창] SV{cell} {formatted} → {resp!r}")
|
||||
return resp.strip() == "1"
|
||||
except Exception as e:
|
||||
print(f"[설정창] SV{cell} 오류: {e}")
|
||||
return False
|
||||
Reference in New Issue
Block a user