1. focus焦点, 哪个item现在吃键盘输入
    1. QGraphicsScene::focusItem() 返回它
    2. 最好交给系统自动维护, 系统有(微弱)默认样式
    3. 一个或没有(nullptr), 可以用来判断当前是否输入状态,
      1. 系统自动维护他, 比如点击空白, scene就会清掉他.
  2. selected选中, 哪个 item 高亮
    1. item.isSelected() 返回 true
    2. 同一时间可以 N 个(框选一堆)
    3. 只能自己维护, 系统没有默认样式
基本概念
  1. QGraphicsItemGroup切换show/hide时, 不会处理焦点.
  2. QGraphicsScene 只允许一个焦点项(focusItem() 返回单一项), 焦点项会自动失焦.
各自的生命周期
动作 是否改 focus 是否改 selected 备注
鼠标左键点 item ✅ 变成焦点 ❌ 不改(除非你代码里 setSelected 焦点自动转移
鼠标中键/右键点 item ❌ 不改焦点 ❌ 不改选中 完全无影响
橡皮筋框选 ❌ 不改焦点 ✅ 把框内 item 设 selected 焦点仍留在旧 item
按空格开始编辑 ✅ 焦点不变 ✅ 通常把该 item 也设 selected 自己代码联动
点空白区(默认) ✅ Scene 清掉焦点 ❌ 不改选中 出现“无焦点但有选中”
代码 item->setSelected(true) ❌ 不改焦点 ✅ 选中位变 需要你自己补 setFocusItem 才联动
代码 scene->setFocusItem(item) ✅ 焦点变 ❌ 不改选中 需要你自己补 setSelected 才联动

状态处理

我明白了, 判断编辑状态就在focusitemchange就可以了, 但是, 选中状态其实这么干没用, 因为一定要自己维护, 所以应该分散在键盘鼠标的处理事件中, 先scene.clearSelection() 一键清空所有选中位, 然后再item.setSelected(bool)

scene.clearSelection() + item.setSelected(bool) 这个流程其实不如自己手动维护一个选中的状态item引用方便. 自己维护一个currentitem=xxx

# 编辑状态处理
注册处理事件: scene.focusItemChanged(QGraphicsItem *new, QGraphicsItem *old, Qt::FocusReason) 
状态判断依据: scene.focusItem()
# 更简洁的方案, 自己维护一个当前选中项目
current=xxx

# 选中状态处理 传统的不合适的方案
scene.clearSelection() 一键清空所有选中位不会动焦点 
item.setSelected(bool)
想让对象可被选必须加  item.setFlag(QGraphicsItem::ItemIsSelectable, True)

api参考

一、选中(Selection)——“谁被高亮”

  1. 状态位
    • item.isSelected() -> bool
    • item.setSelected(bool)
  2. Scene 级查询
    • scene.selectedItems() -> QList<QGraphicsItem*> 当前所有被选中的对象
    • scene.selectionArea() -> QPainterPath 返回最近一次框选的路径(橡皮筋)
    • scene.clearSelection() 一键清空所有选中位(不会动焦点)
  3. 程序式框选
    • scene.setSelectionArea(QPainterPath, transform) 用任意路径批量选/取消
    • scene.setSelectionArea(path, Qt::ReplaceSelection, ...) 可加模式参数
  4. 必备标志
    • 想让对象「可被选」必须加: item.setFlag(QGraphicsItem::ItemIsSelectable, True)
  5. 信号
    • scene.selectionChanged() 每次选中集合变化时触发(含增/减)

二、焦点(Focus)——“谁吃键盘”

  1. 状态位
    • item.hasFocus() -> bool 当前是否拥有键盘焦点
    • 重要scene.focusItem() -> QGraphicsItem* 整个场景里谁在吃键盘(可为空)
    • 没意义scene.hasFocus() -> bool 场景本身是否拥有输入焦点(View 焦点)
  2. 设置/拿走
    • scene.setFocusItem(item, reason) 强塞焦点(item 需 ItemIsFocusable
    • scene.setFocus(reason) 让场景自己拿焦点(不指定 item)
    • scene.clearFocus() 场景放弃焦点(focusItem 变空)
  3. 必备标志
    • 想让对象「可被给焦点」: item.setFlag(QGraphicsItem::ItemIsFocusable, True)
  4. 焦点保护(Qt 5.12+)
    • scene.setStickyFocus(True) 禁止“点空白”自动清焦点(焦点不会掉空)
  5. 信号
    • scene.focusItemChanged(QGraphicsItem *new, QGraphicsItem *old, Qt::FocusReason)
      每次焦点移动都会发,方便你“视觉选中”跟它同步。

三、最常用“对齐”模板(放在 NavManager 或点击事件里)

def pick_new_current(self, item):
    # 1. 焦点搬家
    self.scene.setFocusItem(item, Qt.MouseFocusReason)
    # 2. 选中搬家(先清旧集合,可只清自己)
    for i in self.scene.selectedItems():
        i.setSelected(False)
    item.setSelected(True)

四、调试小技巧

  1. 打印焦点链

    print("focus:", self.scene.focusItem(), "selected:", self.scene.selectedItems())
    
  2. 焦点丢失追踪
    连接 scene.focusItemChanged 信号,在槽里断点或日志,一眼看出谁把焦点抢走了。