优化速度
This commit is contained in:
+2
-1
@@ -3,4 +3,5 @@ Log/
|
||||
.vscode/
|
||||
camera_params.json
|
||||
**.pyc**
|
||||
data/
|
||||
data/
|
||||
.env
|
||||
@@ -180,6 +180,8 @@ class CameraWorker(QThread):
|
||||
self._has_dual_ir = False
|
||||
self._ir_enabled = False
|
||||
self._last_emit_ts = 0.0
|
||||
self._last_frames = None # 最近一帧,供"按键即拍"使用
|
||||
self._capture_frames = None # 触发拍照时快照的帧
|
||||
self._consec_timeouts = 0
|
||||
self._disconnect_emitted = False
|
||||
|
||||
@@ -189,8 +191,9 @@ class CameraWorker(QThread):
|
||||
|
||||
def trigger_capture(self, save_dir: str):
|
||||
with self._lock:
|
||||
self._capture_dir = save_dir
|
||||
self._do_capture = True
|
||||
self._capture_dir = save_dir
|
||||
self._capture_frames = self._last_frames # 快照当前帧
|
||||
self._do_capture = True
|
||||
|
||||
def stop(self):
|
||||
self._running = False
|
||||
@@ -309,11 +312,14 @@ class CameraWorker(QThread):
|
||||
self._consec_timeouts = 0
|
||||
|
||||
with self._lock:
|
||||
d2c = self._d2c
|
||||
do_cap = self._do_capture
|
||||
cap_dir = self._capture_dir
|
||||
self._last_frames = frames # 始终保存最新帧供快照
|
||||
d2c = self._d2c
|
||||
do_cap = self._do_capture
|
||||
cap_dir = self._capture_dir
|
||||
cap_frames = self._capture_frames
|
||||
if do_cap:
|
||||
self._do_capture = False
|
||||
self._do_capture = False
|
||||
self._capture_frames = None
|
||||
|
||||
# 首帧提取相机参数
|
||||
if self._camera_params is None:
|
||||
@@ -332,7 +338,7 @@ class CameraWorker(QThread):
|
||||
if do_cap and cap_dir:
|
||||
try:
|
||||
self._run_capture(
|
||||
frames, cap_dir,
|
||||
cap_frames or frames, cap_dir,
|
||||
align_filter, point_cloud_filter,
|
||||
frame_to_bgr_image, save_point_cloud_to_ply,
|
||||
OBFrameType, OBFormat,
|
||||
@@ -521,6 +527,44 @@ class BatchD2CWorker(QThread):
|
||||
self.done.emit()
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
# Toast Overlay(不阻塞鼠标,自动消失)
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
|
||||
class _Toast(QLabel):
|
||||
def __init__(self, parent: QWidget):
|
||||
super().__init__(parent)
|
||||
self.setAlignment(Qt.AlignCenter)
|
||||
self.setAttribute(Qt.WA_TransparentForMouseEvents, True)
|
||||
self.setStyleSheet(
|
||||
"background: rgba(30,140,30,210);"
|
||||
"color: white;"
|
||||
"border-radius: 10px;"
|
||||
"font-size: 15px;"
|
||||
"padding: 10px 28px;"
|
||||
)
|
||||
self._timer = QTimer(self)
|
||||
self._timer.setSingleShot(True)
|
||||
self._timer.timeout.connect(self.hide)
|
||||
self.hide()
|
||||
|
||||
def show_msg(self, text: str, ms: int = 1000):
|
||||
self.setText(text)
|
||||
self.adjustSize()
|
||||
p = self.parent()
|
||||
self.move((p.width() - self.width()) // 2,
|
||||
p.height() - self.height() - 70)
|
||||
self.raise_()
|
||||
self.show()
|
||||
self._timer.start(ms)
|
||||
|
||||
def reposition(self):
|
||||
if self.isVisible():
|
||||
p = self.parent()
|
||||
self.move((p.width() - self.width()) // 2,
|
||||
p.height() - self.height() - 70)
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
# Main Window
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
@@ -542,6 +586,8 @@ class MainWindow(QMainWindow):
|
||||
self._depth_profiles_data: list[dict] = []
|
||||
|
||||
self._build_ui()
|
||||
# toast 覆盖在 central widget 上
|
||||
self._toast = _Toast(self.centralWidget())
|
||||
|
||||
# ─────────────────────────────────
|
||||
# UI Construction
|
||||
@@ -572,86 +618,100 @@ class MainWindow(QMainWindow):
|
||||
tabs.addTab(self._build_d2c_tab(), " Orbbec Viewer 深度图 D2C ")
|
||||
root.addWidget(tabs)
|
||||
|
||||
@staticmethod
|
||||
def _step_group(num: int, title: str) -> QGroupBox:
|
||||
grp = QGroupBox(f"第 {num} 步 · {title}")
|
||||
grp.setStyleSheet(
|
||||
"QGroupBox { font-weight:bold; font-size:12px; color:#8ec8ff;"
|
||||
" border:1px solid #444; border-radius:5px; margin-top:6px; padding-top:4px; }"
|
||||
"QGroupBox::title { subcontrol-origin:margin; left:10px; padding:0 4px; }"
|
||||
)
|
||||
return grp
|
||||
|
||||
def _build_capture_tab(self) -> QWidget:
|
||||
w = QWidget()
|
||||
layout = QVBoxLayout(w)
|
||||
layout.setContentsMargins(12, 10, 12, 10)
|
||||
layout.setSpacing(8)
|
||||
layout.setContentsMargins(12, 8, 12, 8)
|
||||
layout.setSpacing(6)
|
||||
|
||||
# ── 设备刷新行 ──
|
||||
dev_row = QHBoxLayout()
|
||||
COMBO_CSS = "QComboBox { combobox-popup: 0; }"
|
||||
|
||||
# ── 第 1 步:刷新设备 ──
|
||||
grp1 = self._step_group(1, "刷新设备")
|
||||
g1 = QHBoxLayout(grp1)
|
||||
g1.setContentsMargins(8, 4, 8, 6)
|
||||
refresh_btn = QPushButton("刷新设备")
|
||||
refresh_btn.setFixedHeight(30)
|
||||
refresh_btn.setFixedHeight(28)
|
||||
refresh_btn.clicked.connect(self._refresh_devices)
|
||||
dev_row.addWidget(refresh_btn)
|
||||
|
||||
g1.addWidget(refresh_btn)
|
||||
self.device_status = QLabel("未连接设备,请点击「刷新设备」")
|
||||
self.device_status.setStyleSheet("color: #aaa; font-size: 12px;")
|
||||
dev_row.addWidget(self.device_status)
|
||||
dev_row.addStretch()
|
||||
layout.addLayout(dev_row)
|
||||
self.device_status.setStyleSheet("color:#aaa; font-size:12px;")
|
||||
g1.addWidget(self.device_status)
|
||||
g1.addStretch()
|
||||
layout.addWidget(grp1)
|
||||
|
||||
# ── 流配置行 ──
|
||||
stream_grp = QGroupBox("流配置")
|
||||
stream_layout = QHBoxLayout(stream_grp)
|
||||
stream_layout.setSpacing(12)
|
||||
# ── 第 2 步:流配置 ──
|
||||
grp2 = self._step_group(2, "流配置")
|
||||
g2 = QHBoxLayout(grp2)
|
||||
g2.setContentsMargins(8, 4, 8, 6)
|
||||
g2.setSpacing(10)
|
||||
|
||||
stream_layout.addWidget(QLabel("RGB:"))
|
||||
g2.addWidget(QLabel("RGB:"))
|
||||
self.color_combo = QComboBox()
|
||||
self.color_combo.setMinimumWidth(260)
|
||||
self.color_combo.setMinimumWidth(250)
|
||||
self.color_combo.setMaxVisibleItems(12)
|
||||
self.color_combo.setStyleSheet("QComboBox { combobox-popup: 0; }")
|
||||
stream_layout.addWidget(self.color_combo)
|
||||
self.color_combo.setStyleSheet(COMBO_CSS)
|
||||
g2.addWidget(self.color_combo)
|
||||
|
||||
stream_layout.addSpacing(16)
|
||||
stream_layout.addWidget(QLabel("深度:"))
|
||||
g2.addSpacing(12)
|
||||
g2.addWidget(QLabel("深度:"))
|
||||
self.depth_combo = QComboBox()
|
||||
self.depth_combo.setMinimumWidth(260)
|
||||
self.depth_combo.setMinimumWidth(250)
|
||||
self.depth_combo.setMaxVisibleItems(12)
|
||||
self.depth_combo.setStyleSheet("QComboBox { combobox-popup: 0; }")
|
||||
stream_layout.addWidget(self.depth_combo)
|
||||
|
||||
stream_layout.addStretch()
|
||||
self.depth_combo.setStyleSheet(COMBO_CSS)
|
||||
g2.addWidget(self.depth_combo)
|
||||
|
||||
g2.addStretch()
|
||||
self.stream_btn = QPushButton("开启")
|
||||
self.stream_btn.setCheckable(True)
|
||||
self.stream_btn.setFixedSize(88, 32)
|
||||
self.stream_btn.setFixedSize(88, 30)
|
||||
self.stream_btn.setStyleSheet(
|
||||
"QPushButton{ border-radius:6px; border:none; font-weight:bold; }"
|
||||
"QPushButton{ border-radius:5px; border:none; font-weight:bold; }"
|
||||
"QPushButton:!checked{ background:#555; color:#ccc; }"
|
||||
"QPushButton:checked{ background:#2a8a2a; color:white; }"
|
||||
"QPushButton:disabled{ background:#444; color:#777; }"
|
||||
)
|
||||
self.stream_btn.toggled.connect(self._on_stream_toggle)
|
||||
stream_layout.addWidget(self.stream_btn)
|
||||
g2.addWidget(self.stream_btn)
|
||||
layout.addWidget(grp2)
|
||||
|
||||
layout.addWidget(stream_grp)
|
||||
# ── 第 3 步:保存目录 ──
|
||||
grp3 = self._step_group(3, "保存目录")
|
||||
g3 = QHBoxLayout(grp3)
|
||||
g3.setContentsMargins(8, 4, 8, 6)
|
||||
g3.setSpacing(8)
|
||||
|
||||
# ── 目录 + D2C 控制行 ──
|
||||
ctrl = QHBoxLayout()
|
||||
ctrl.addWidget(QLabel("保存目录:"))
|
||||
today = datetime.now()
|
||||
self.session_edit = QLineEdit(f"{today.year}年{today.month}月{today.day}日")
|
||||
self.session_edit.setMinimumWidth(170)
|
||||
ctrl.addWidget(self.session_edit)
|
||||
self.session_edit.setMinimumWidth(160)
|
||||
g3.addWidget(self.session_edit)
|
||||
|
||||
create_btn = QPushButton("创建 / 载入")
|
||||
create_btn.setFixedHeight(28)
|
||||
create_btn.clicked.connect(self._create_session)
|
||||
ctrl.addWidget(create_btn)
|
||||
g3.addWidget(create_btn)
|
||||
|
||||
self.session_status = QLabel("(未创建)")
|
||||
self.session_status.setStyleSheet("color:gray; font-size:12px;")
|
||||
ctrl.addWidget(self.session_status)
|
||||
ctrl.addStretch()
|
||||
g3.addWidget(self.session_status)
|
||||
g3.addStretch()
|
||||
|
||||
self.d2c_check = QCheckBox("启用 D2C 对齐预览")
|
||||
self.d2c_check.setToolTip("勾选后预览使用 D2C 对齐;拍照时始终保存全部数据")
|
||||
self.d2c_check.stateChanged.connect(
|
||||
lambda s: self._worker and self._worker.set_d2c(s == Qt.Checked))
|
||||
ctrl.addWidget(self.d2c_check)
|
||||
|
||||
layout.addLayout(ctrl)
|
||||
g3.addWidget(self.d2c_check)
|
||||
layout.addWidget(grp3)
|
||||
|
||||
# ── 预览区 ──
|
||||
preview = QFrame()
|
||||
@@ -669,7 +729,7 @@ class MainWindow(QMainWindow):
|
||||
box.addWidget(hdr)
|
||||
lbl = QLabel("等待画面…")
|
||||
lbl.setAlignment(Qt.AlignCenter)
|
||||
lbl.setMinimumSize(540, 380)
|
||||
lbl.setMinimumSize(540, 360)
|
||||
lbl.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||
lbl.setStyleSheet("background:#111; border:1px solid #555; color:#666;")
|
||||
setattr(self, attr, lbl)
|
||||
@@ -678,13 +738,22 @@ class MainWindow(QMainWindow):
|
||||
|
||||
layout.addWidget(preview, stretch=1)
|
||||
|
||||
# ── 拍照按钮 ──
|
||||
btn_row = QHBoxLayout()
|
||||
btn_row.addStretch()
|
||||
# ── 第 4 步:拍照 ──
|
||||
step4_row = QHBoxLayout()
|
||||
step4_lbl = QLabel("第 4 步 · 拍照")
|
||||
step4_lbl.setStyleSheet(
|
||||
"color:#8ec8ff; font-weight:bold; font-size:12px;")
|
||||
step4_row.addWidget(step4_lbl)
|
||||
step4_row.addStretch()
|
||||
|
||||
hint = QLabel("快捷键 S")
|
||||
hint.setStyleSheet("color:#666; font-size:11px;")
|
||||
step4_row.addWidget(hint)
|
||||
|
||||
self.capture_btn = QPushButton("拍照")
|
||||
self.capture_btn.setFixedSize(160, 48)
|
||||
self.capture_btn.setFixedSize(160, 44)
|
||||
self.capture_btn.setEnabled(False)
|
||||
f = QFont(); f.setPointSize(14); f.setBold(True)
|
||||
f = QFont(); f.setPointSize(13); f.setBold(True)
|
||||
self.capture_btn.setFont(f)
|
||||
self.capture_btn.setStyleSheet(
|
||||
"QPushButton{ background:#2a6ebb; color:white; border-radius:8px; border:none; }"
|
||||
@@ -693,9 +762,8 @@ class MainWindow(QMainWindow):
|
||||
"QPushButton:disabled{ background:#444; color:#777; }"
|
||||
)
|
||||
self.capture_btn.clicked.connect(self._on_capture)
|
||||
btn_row.addWidget(self.capture_btn)
|
||||
btn_row.addStretch()
|
||||
layout.addLayout(btn_row)
|
||||
step4_row.addWidget(self.capture_btn)
|
||||
layout.addLayout(step4_row)
|
||||
|
||||
return w
|
||||
|
||||
@@ -983,11 +1051,7 @@ class MainWindow(QMainWindow):
|
||||
def _on_capture_done(self, save_dir: str):
|
||||
self.capture_btn.setEnabled(True)
|
||||
self.capture_btn.setText("拍照")
|
||||
msg = QMessageBox(QMessageBox.Information, "拍摄完成",
|
||||
f"已保存至:\n{save_dir}", QMessageBox.Ok, self)
|
||||
msg.setWindowModality(Qt.NonModal)
|
||||
msg.show()
|
||||
QTimer.singleShot(3000, msg.accept)
|
||||
self._toast.show_msg("拍摄完成!", 1000)
|
||||
|
||||
# ─────────────────────────────────
|
||||
# Batch D2C Tab
|
||||
@@ -1088,6 +1152,22 @@ class MainWindow(QMainWindow):
|
||||
def _append_d2c_log(self, msg: str):
|
||||
self.d2c_log.append(msg)
|
||||
|
||||
# ─────────────────────────────────
|
||||
# Keyboard & Resize
|
||||
# ─────────────────────────────────
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
if event.key() == Qt.Key_S:
|
||||
focused = QApplication.focusWidget()
|
||||
if not isinstance(focused, (QLineEdit, QTextEdit)) and self.capture_btn.isEnabled():
|
||||
self._on_capture()
|
||||
return
|
||||
super().keyPressEvent(event)
|
||||
|
||||
def resizeEvent(self, event):
|
||||
super().resizeEvent(event)
|
||||
self._toast.reposition()
|
||||
|
||||
# ─────────────────────────────────
|
||||
# Close
|
||||
# ─────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user