| | |
| | | self.view.sceneMouseMoved.connect(self._update_mouse_pos) |
| | | left_layout.addWidget(self.view, stretch=1) |
| | | |
| | | # Bottom bar for map controls |
| | | bottom_bar = QtWidgets.QWidget() |
| | | bottom_layout = QtWidgets.QHBoxLayout(bottom_bar) |
| | | bottom_layout.setContentsMargins(0, 4, 0, 0) |
| | | |
| | | self.follow_checkbox = QtWidgets.QCheckBox("自动跟随车辆") |
| | | self.follow_checkbox.setChecked(True) |
| | | self.follow_checkbox.toggled.connect(self._on_follow_toggled) |
| | | left_layout.addWidget(self.follow_checkbox, alignment=QtCore.Qt.AlignLeft) |
| | | bottom_layout.addWidget(self.follow_checkbox) |
| | | |
| | | self.clear_trail_btn = QtWidgets.QPushButton("清除轨迹") |
| | | self.clear_trail_btn.clicked.connect(self._clear_trail) |
| | | bottom_layout.addWidget(self.clear_trail_btn) |
| | | |
| | | bottom_layout.addStretch(1) |
| | | |
| | | self.mouse_pos_label = QtWidgets.QLabel("E: 0.00 N: 0.00") |
| | | self.mouse_pos_label.setStyleSheet("color:#222; background:rgba(255,255,255,190); padding:2px 6px; border-radius:4px;") |
| | | left_layout.addWidget(self.mouse_pos_label, alignment=QtCore.Qt.AlignRight) |
| | | bottom_layout.addWidget(self.mouse_pos_label) |
| | | |
| | | left_layout.addWidget(bottom_bar) |
| | | |
| | | layout.addWidget(left_panel, stretch=3) |
| | | |
| | |
| | | self.origin_text = QtWidgets.QLineEdit(default_origin_short) |
| | | self.origin_text.setPlaceholderText("3949.9120,N,11616.8544,E[,alt]") |
| | | self.origin_btn = QtWidgets.QPushButton("更新原点") |
| | | self.copy_origin_btn = QtWidgets.QPushButton("复制宏定义") # 新增按钮 |
| | | grid.addWidget(QtWidgets.QLabel("坐标 (lat,N,lon,E):"), 0, 0, 1, 2) |
| | | grid.addWidget(self.origin_text, 0, 2, 1, 2) |
| | | grid.addWidget(self.origin_btn, 0, 4) |
| | | grid.addWidget(self.copy_origin_btn, 0, 5) # 添加到布局 |
| | | |
| | | labels = ["E (m)", "N (m)", "U (m)", "Heading (deg)"] |
| | | self.pos_spin: List[QtWidgets.QDoubleSpinBox] = [] |
| | |
| | | status_layout = QtWidgets.QVBoxLayout(status_group) |
| | | self.info_label = QtWidgets.QLabel("等待数据...") |
| | | self.info_label.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft) |
| | | self.info_label.setStyleSheet("font-family: Consolas, 'Courier New'; font-size: 12px;") |
| | | self.info_label.setStyleSheet("font-family: Consolas, 'Courier New'; font-size: 14px;") |
| | | info_scroll = QtWidgets.QScrollArea() |
| | | info_scroll.setWidgetResizable(True) |
| | | info_scroll.setWidget(self.info_label) |
| | | info_scroll.setMinimumHeight(500) # 进一步增加最小高度 |
| | | status_layout.addWidget(info_scroll) |
| | | right_layout.addWidget(status_group, stretch=1) |
| | | right_layout.addWidget(status_group, stretch=4) # 进一步增加布局权重,从2增加到4 |
| | | |
| | | # Stack table |
| | | stack_group = QtWidgets.QGroupBox("堆栈监测") |
| | |
| | | self.path_load_btn.clicked.connect(self._load_path_from_text) |
| | | self.path_export_btn.clicked.connect(self._export_path_to_mcu) |
| | | self.origin_btn.clicked.connect(self._update_origin) |
| | | self.copy_origin_btn.clicked.connect(self._copy_origin_macro) # 绑定事件 |
| | | self.reset_pose_btn.clicked.connect(self._reset_position) |
| | | self.port_refresh_btn.clicked.connect(self._refresh_serial_ports) |
| | | self.serial_toggle_btn.clicked.connect(self._toggle_serial) |
| | |
| | | self._set_controls_enabled(False) |
| | | self._update_serial_ui() |
| | | |
| | | def _copy_origin_macro(self): |
| | | text = self.origin_text.text().strip() |
| | | if not text: |
| | | QtWidgets.QMessageBox.warning(self, "复制", "原点输入框为空") |
| | | return |
| | | |
| | | # 格式: lat_val,N,lon_val,E,alt |
| | | # 例如: 3949.9120,N,11616.8544,E,47.5 |
| | | parts = [p.strip() for p in text.split(",")] |
| | | if len(parts) < 4: |
| | | QtWidgets.QMessageBox.warning(self, "复制", "原点格式错误,需至少包含经纬度") |
| | | return |
| | | |
| | | try: |
| | | # 解析 DDMM.MMMM 格式转 DD.DDDD |
| | | # 纬度 |
| | | lat_raw = parts[0] |
| | | lat_deg_int = int(float(lat_raw) / 100) |
| | | lat_min = float(lat_raw) % 100 |
| | | lat_dd = lat_deg_int + lat_min / 60.0 |
| | | if parts[1].upper() == 'S': |
| | | lat_dd = -lat_dd |
| | | |
| | | # 经度 |
| | | lon_raw = parts[2] |
| | | lon_deg_int = int(float(lon_raw) / 100) |
| | | lon_min = float(lon_raw) % 100 |
| | | lon_dd = lon_deg_int + lon_min / 60.0 |
| | | if parts[3].upper() == 'W': |
| | | lon_dd = -lon_dd |
| | | |
| | | # 高度 |
| | | alt = "0.0" |
| | | if len(parts) >= 5: |
| | | alt = parts[4] |
| | | |
| | | macro = ( |
| | | f"#define MC_CFG_ORIGIN_LAT_DEG ({lat_dd:.6f})\n" |
| | | f"#define MC_CFG_ORIGIN_LON_DEG ({lon_dd:.6f})\n" |
| | | f"#define MC_CFG_ORIGIN_ALT_M ({alt})" |
| | | ) |
| | | |
| | | clipboard = QtWidgets.QApplication.clipboard() |
| | | clipboard.setText(macro) |
| | | QtWidgets.QMessageBox.information(self, "复制", "已复制宏定义到剪贴板!") |
| | | |
| | | except ValueError as e: |
| | | QtWidgets.QMessageBox.warning(self, "复制", f"数值解析错误: {e}") |
| | | |
| | | def _handle_user_pan(self): |
| | | if self._auto_follow: |
| | | self.follow_checkbox.blockSignals(True) |
| | |
| | | if checked and self.pose_status: |
| | | self.view.centerOn(self.pose_status.east, -self.pose_status.north) |
| | | |
| | | def _clear_trail(self): |
| | | """清除地图上的轨迹点""" |
| | | self.trail_points = [] |
| | | # Update scene to remove trail |
| | | self._update_scene() |
| | | |
| | | def _update_mouse_pos(self, east: float, scene_y: float): |
| | | north = -scene_y |
| | | self.mouse_pos_label.setText(f"E: {east: .2f} N: {north: .2f}") |
| | |
| | | (height if height > 0 else 1.0) + pad_y * 2.0, |
| | | ) |
| | | self.scene.setSceneRect(scene_rect) |
| | | self.view.fitInView(scene_rect, QtCore.Qt.KeepAspectRatio) |
| | | |
| | | def _append_log(self, text: str): |
| | | self.log_view.appendPlainText(text) |