Update GUI 2.0.8 group selected list

lunu
ken910606 2 weeks ago
parent b9d6e0e2e0
commit be52c4bd22

@ -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()
self.statusBar().showMessage(
f"Group {group_id}: 已分配 {len(group.selected_drone_ids)} 台無人機", 3000)
if blocked:
self.statusBar().showMessage(
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,46 +952,61 @@ 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
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()
panel = self.group_panels.get(group_id)
if panel:
panel.update_drone_list()
panel.update_status()
self.statusBar().showMessage(
f"Group {group_id}: 框選分配 {len(drone_ids)} 台無人機", 3000)
if blocked:
self.statusBar().showMessage(
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 邏輯"""
group = self.mission_groups.get(group_id)
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)
selectable = []
blocked = []
for did in drone_ids:
if self._is_drone_selected_by_other_group(did, group.group_id):
blocked.append(did)
else:
selectable.append(did)
if is_checked:
group.selected_drone_ids.update(drone_ids)
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.difference_update(drone_ids)
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()
@ -1787,4 +1927,4 @@ def main():
if __name__ == '__main__':
main()
main()

Loading…
Cancel
Save