forked from chiyu1468/AirTrapMine
Update GUI 2.0.8 group selected list
This commit is contained in:
+166
-26
@@ -33,7 +33,7 @@ from mission_group import (
|
||||
# ================================================================================
|
||||
|
||||
class ControlStationUI(QMainWindow):
|
||||
VERSION = '2.0.7'
|
||||
VERSION = '2.0.8'
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
@@ -650,18 +650,25 @@ class ControlStationUI(QMainWindow):
|
||||
"""統一刷新所有 UI 元素:左側 drone 勾選、左側 socket 勾選、右側 group panel"""
|
||||
group = self._get_active_group()
|
||||
selected = group.selected_drone_ids if group else set()
|
||||
current_group_id = group.group_id if group else None
|
||||
|
||||
# 左側 drone checkbox
|
||||
for drone_id, panel in self.drones.items():
|
||||
checkbox = panel.get_checkbox()
|
||||
if checkbox:
|
||||
blocked = (
|
||||
current_group_id is not None and
|
||||
self._is_drone_selected_by_other_group(drone_id, current_group_id)
|
||||
)
|
||||
checkbox.blockSignals(True)
|
||||
checkbox.setStyleSheet(self._get_drone_checkbox_style(blocked))
|
||||
checkbox.setChecked(drone_id in selected)
|
||||
checkbox.setEnabled(not blocked or drone_id in selected)
|
||||
checkbox.blockSignals(False)
|
||||
|
||||
# 左側 socket checkbox
|
||||
for socket_id in self.socket_groups.keys():
|
||||
self.refresh_socket_checkbox(socket_id, selected)
|
||||
self.refresh_socket_checkbox(socket_id, selected, current_group_id)
|
||||
|
||||
# 右側 group panel
|
||||
if group:
|
||||
@@ -670,7 +677,33 @@ class ControlStationUI(QMainWindow):
|
||||
panel.update_drone_list()
|
||||
panel.update_status()
|
||||
|
||||
def refresh_socket_checkbox(self, socket_id, selected_ids):
|
||||
def _get_drone_checkbox_style(self, blocked=False):
|
||||
"""回傳 drone checkbox 樣式;被其他 group 佔用時顯示紅色填滿。"""
|
||||
blocked_style = """
|
||||
QCheckBox::indicator:disabled {
|
||||
background-color: #D32F2F;
|
||||
border: 2px solid #A32020;
|
||||
}
|
||||
""" if blocked else ""
|
||||
return f"""
|
||||
QCheckBox {{
|
||||
color: #DDD;
|
||||
}}
|
||||
QCheckBox::indicator {{
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 2px solid #888888;
|
||||
border-radius: 3px;
|
||||
background: transparent;
|
||||
}}
|
||||
QCheckBox::indicator:checked {{
|
||||
background-color: #7FFFD4;
|
||||
border: 2px solid #888888;
|
||||
}}
|
||||
{blocked_style}
|
||||
"""
|
||||
|
||||
def refresh_socket_checkbox(self, socket_id, selected_ids, current_group_id=None):
|
||||
"""根據 selected_ids 推導並更新 socket 的勾選狀態"""
|
||||
drone_ids = [did for did in self.drones.keys() if self.get_socket_id(did) == socket_id]
|
||||
if not drone_ids:
|
||||
@@ -684,22 +717,29 @@ class ControlStationUI(QMainWindow):
|
||||
if not checkbox:
|
||||
return
|
||||
|
||||
checked_count = sum(1 for did in drone_ids if did in selected_ids)
|
||||
effective_drone_ids = [
|
||||
did for did in drone_ids
|
||||
if current_group_id is None
|
||||
or did in selected_ids
|
||||
or not self._is_drone_selected_by_other_group(did, current_group_id)
|
||||
]
|
||||
checked_count = sum(1 for did in effective_drone_ids if did in selected_ids)
|
||||
|
||||
checkbox.blockSignals(True)
|
||||
if checked_count == 0:
|
||||
checkbox.setCheckState(Qt.CheckState.Unchecked)
|
||||
elif checked_count == len(drone_ids):
|
||||
elif checked_count == len(effective_drone_ids):
|
||||
checkbox.setCheckState(Qt.CheckState.Checked)
|
||||
else:
|
||||
checkbox.setCheckState(Qt.CheckState.PartiallyChecked)
|
||||
checkbox.setEnabled(len(effective_drone_ids) > 0)
|
||||
checkbox.blockSignals(False)
|
||||
|
||||
|
||||
# 【已遷移至 refresh_socket_checkbox()】舊的 update_group_checkbox_state() 已廢棄
|
||||
|
||||
def _handle_assign_drones(self, group_id):
|
||||
"""開啟無人機分配對話框 — 直接修改 selected_drone_ids"""
|
||||
"""開啟無人機分配對話框 — 過濾被占用的drone"""
|
||||
group = self.mission_groups.get(group_id)
|
||||
if not group:
|
||||
return
|
||||
@@ -708,10 +748,20 @@ class ControlStationUI(QMainWindow):
|
||||
# 預先選中:目前 group 的 selected_drone_ids
|
||||
pre_selected = set(group.selected_drone_ids)
|
||||
|
||||
# 允許多 group 分配,所以不需要 other_assigned 過濾
|
||||
dialog = DroneAssignDialog(self, all_ids, pre_selected, {})
|
||||
if dialog.exec() == QDialog.DialogCode.Accepted:
|
||||
group.selected_drone_ids = dialog.get_selected()
|
||||
requested = dialog.get_selected()
|
||||
|
||||
allowed = set()
|
||||
blocked = set()
|
||||
|
||||
for did in requested:
|
||||
if self._is_drone_selected_by_other_group(did, group_id):
|
||||
blocked.add(did)
|
||||
else:
|
||||
allowed.add(did)
|
||||
|
||||
group.selected_drone_ids = allowed
|
||||
|
||||
# 只有當操作目標組是 active 組時,才更新 UI
|
||||
if group_id == self.active_group_id:
|
||||
@@ -722,8 +772,14 @@ class ControlStationUI(QMainWindow):
|
||||
panel.update_drone_list()
|
||||
panel.update_status()
|
||||
|
||||
if blocked:
|
||||
self.statusBar().showMessage(
|
||||
f"Group {group_id}: 已分配 {len(group.selected_drone_ids)} 台無人機", 3000)
|
||||
f"⚠ Group {group_id}: {len(blocked)} 台已被其他 group 勾選,未加入", 4000
|
||||
)
|
||||
else:
|
||||
self.statusBar().showMessage(
|
||||
f"Group {group_id}: 已分配 {len(group.selected_drone_ids)} 台無人機", 3000
|
||||
)
|
||||
|
||||
def _handle_mission_type_changed(self, group_id, mission_type):
|
||||
"""群組任務類型切換"""
|
||||
@@ -896,18 +952,27 @@ class ControlStationUI(QMainWindow):
|
||||
f"請在地圖上框選要分配到 Group {group_id} 的無人機", 5000)
|
||||
|
||||
def _handle_drone_box_selected(self, drone_ids_json):
|
||||
"""地圖框選完成 — 直接分配到指定群組的 selected_drone_ids"""
|
||||
group_id = self._pending_box_assign
|
||||
self._pending_box_assign = None
|
||||
if not group_id:
|
||||
return
|
||||
|
||||
group = self.mission_groups.get(group_id)
|
||||
if not group:
|
||||
return
|
||||
drone_ids = json.loads(drone_ids_json)
|
||||
group.selected_drone_ids = set(drone_ids)
|
||||
|
||||
# 只有當操作目標組是 active 組時,才更新 UI
|
||||
drone_ids = json.loads(drone_ids_json)
|
||||
|
||||
allowed = set()
|
||||
blocked = set()
|
||||
for did in drone_ids:
|
||||
if self._is_drone_selected_by_other_group(did, group_id):
|
||||
blocked.add(did)
|
||||
else:
|
||||
allowed.add(did)
|
||||
|
||||
group.selected_drone_ids = allowed
|
||||
|
||||
if group_id == self.active_group_id:
|
||||
self.refresh_selection_ui()
|
||||
|
||||
@@ -916,8 +981,14 @@ class ControlStationUI(QMainWindow):
|
||||
panel.update_drone_list()
|
||||
panel.update_status()
|
||||
|
||||
if blocked:
|
||||
self.statusBar().showMessage(
|
||||
f"Group {group_id}: 框選分配 {len(drone_ids)} 台無人機", 3000)
|
||||
f"⚠ Group {group_id}: 部分 drone 已被其他 group 勾選,僅加入 {len(allowed)} 台", 4000
|
||||
)
|
||||
else:
|
||||
self.statusBar().showMessage(
|
||||
f"Group {group_id}: 框選分配 {len(allowed)} 台無人機", 3000
|
||||
)
|
||||
|
||||
def _handle_select_all_for_group(self, group_id):
|
||||
"""全選/取消全選 — Toggle 邏輯"""
|
||||
@@ -925,17 +996,17 @@ class ControlStationUI(QMainWindow):
|
||||
if not group:
|
||||
return
|
||||
|
||||
all_ids = set(self.drones.keys())
|
||||
selectable_ids = self._get_selectable_drones_for_group(group_id) | set(group.selected_drone_ids)
|
||||
|
||||
# Toggle 邏輯:如果已全選,則清空;否則全選
|
||||
if group.selected_drone_ids == all_ids:
|
||||
if group.selected_drone_ids == selectable_ids:
|
||||
# 已全選 → 清空
|
||||
group.selected_drone_ids.clear()
|
||||
msg_status = "已取消全選"
|
||||
else:
|
||||
# 未全選 → 全選
|
||||
group.selected_drone_ids = set(all_ids)
|
||||
msg_status = f"全選分配 {len(all_ids)} 台無人機"
|
||||
group.selected_drone_ids = set(selectable_ids)
|
||||
msg_status = f"全選可用無人機 {len(selectable_ids)} 台"
|
||||
|
||||
# 只有當操作目標組是 active 組時,才更新 UI
|
||||
if group_id == self.active_group_id:
|
||||
@@ -1070,15 +1141,71 @@ class ControlStationUI(QMainWindow):
|
||||
return
|
||||
|
||||
drone_ids = [did for did in self.drones.keys() if self.get_socket_id(did) == socket_id]
|
||||
is_checked = (state == Qt.CheckState.Checked.value)
|
||||
|
||||
if is_checked:
|
||||
group.selected_drone_ids.update(drone_ids)
|
||||
selectable = []
|
||||
blocked = []
|
||||
for did in drone_ids:
|
||||
if self._is_drone_selected_by_other_group(did, group.group_id):
|
||||
blocked.append(did)
|
||||
else:
|
||||
group.selected_drone_ids.difference_update(drone_ids)
|
||||
selectable.append(did)
|
||||
|
||||
if not selectable:
|
||||
if blocked:
|
||||
self.statusBar().showMessage(
|
||||
f"⚠ Socket {socket_id} 的 drone 已被其他 group 勾選,無法操作", 4000
|
||||
)
|
||||
self.refresh_selection_ui()
|
||||
return
|
||||
|
||||
all_selectable_selected = all(did in group.selected_drone_ids for did in selectable)
|
||||
|
||||
if all_selectable_selected:
|
||||
group.selected_drone_ids.difference_update(selectable)
|
||||
else:
|
||||
group.selected_drone_ids.update(selectable)
|
||||
if blocked:
|
||||
self.statusBar().showMessage(
|
||||
f"⚠ 以下 drone 已被其他 group 勾選,略過: {', '.join(blocked)}", 4000
|
||||
)
|
||||
|
||||
self.refresh_selection_ui()
|
||||
|
||||
def _is_drone_selected_by_other_group(self, drone_id, current_group_id):
|
||||
"""
|
||||
檢查該 drone 是否已被其他 group 勾選
|
||||
|
||||
Args:
|
||||
drone_id: 無人機 ID
|
||||
current_group_id: 當前 group 的 ID
|
||||
|
||||
Returns:
|
||||
True 如果 drone 已被其他 group 選擇,否則 False
|
||||
"""
|
||||
for gid, group in self.mission_groups.items():
|
||||
# 跳過當前 group
|
||||
if gid == current_group_id:
|
||||
continue
|
||||
# 檢查該 drone 是否在其他 group 中被勾選
|
||||
if drone_id in group.selected_drone_ids:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _get_selectable_drones_for_group(self, group_id):
|
||||
"""
|
||||
取得指定 group 可以選擇的 drone 清單(不被其他 group 佔用的)
|
||||
|
||||
Args:
|
||||
group_id: group 的 ID
|
||||
|
||||
Returns:
|
||||
set: 可選擇的 drone ID 集合
|
||||
"""
|
||||
selectable = set()
|
||||
for did in self.drones.keys():
|
||||
if not self._is_drone_selected_by_other_group(did, group_id):
|
||||
selectable.add(did)
|
||||
return selectable
|
||||
|
||||
def handle_drone_selection(self, drone_id, state):
|
||||
"""單台 drone 勾選 — 只修改 active group 的 selected_drone_ids"""
|
||||
group = self._get_active_group()
|
||||
@@ -1088,6 +1215,12 @@ class ControlStationUI(QMainWindow):
|
||||
is_checked = (state == Qt.CheckState.Checked.value)
|
||||
|
||||
if is_checked:
|
||||
if self._is_drone_selected_by_other_group(drone_id, group.group_id):
|
||||
self.statusBar().showMessage(
|
||||
f"⚠ {drone_id} 已被其他 group 勾選,無法加入 Group {group.group_id}", 3000
|
||||
)
|
||||
self.refresh_selection_ui()
|
||||
return
|
||||
group.selected_drone_ids.add(drone_id)
|
||||
else:
|
||||
group.selected_drone_ids.discard(drone_id)
|
||||
@@ -1103,6 +1236,13 @@ class ControlStationUI(QMainWindow):
|
||||
if drone_id in group.selected_drone_ids:
|
||||
group.selected_drone_ids.remove(drone_id)
|
||||
else:
|
||||
# 嘗試勾選前先檢查是否被其他 group 使用
|
||||
if self._is_drone_selected_by_other_group(drone_id, group.group_id):
|
||||
self.statusBar().showMessage(
|
||||
f"⚠ {drone_id} 已被其他 group 勾選,無法加入 Group {group.group_id}", 3000
|
||||
)
|
||||
self.refresh_selection_ui()
|
||||
return
|
||||
group.selected_drone_ids.add(drone_id)
|
||||
|
||||
self.refresh_selection_ui()
|
||||
@@ -1113,14 +1253,14 @@ class ControlStationUI(QMainWindow):
|
||||
if not group:
|
||||
return
|
||||
|
||||
all_ids = set(self.drones.keys())
|
||||
selectable_ids = self._get_selectable_drones_for_group(group.group_id) | set(group.selected_drone_ids)
|
||||
|
||||
if group.selected_drone_ids == all_ids:
|
||||
if group.selected_drone_ids == selectable_ids:
|
||||
# 已全選 → 清空
|
||||
group.selected_drone_ids.clear()
|
||||
else:
|
||||
# 未全選 → 全選
|
||||
group.selected_drone_ids = set(all_ids)
|
||||
group.selected_drone_ids = set(selectable_ids)
|
||||
|
||||
self.refresh_selection_ui()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user