Coverage for graphica/gui/tools/manager.py: 93.3%

29 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-12 16:38 +0000

1"""マウスの7つのモード(データカーソル / 注釈 / 自由配置 / 範囲選択 / ピーク配置 / スライス抽出 / 領域強調)を、 

2どれか1つだけが有効になるよう MOUSE_MODES の表でまとめて管理する。 

3 

4モードを足すときは MOUSE_MODES に1行足す。各モードの _toggle_*_mode は、有効になるときの先頭で 

5self._deactivate_other_mouse_modes('<名前>') を呼ぶだけでよい。QAction は toggled ではなく triggered に 

6つないであるので、setChecked(False) だけではスロットが呼ばれない。解除ではチェックを外すのと 

7_toggle_*_mode(False) の呼び出しの両方をする。 

8""" 

9import logging 

10from collections import namedtuple 

11 

12logger = logging.getLogger(__name__) 

13 

14 

15# name: モードの識別子(_deactivate_other_mouse_modes に渡す名前) 

16# flag_attr: PlotterApp 上の「有効かどうか」を持つ属性名 

17# action_attr: ツールバーの QAction を持つ属性名 

18# toggle_method: ON/OFF を切り替えるメソッド名 

19# uses_left_click / uses_right_click: プロット内の(ドラッグでない)クリックに意味があるか。 

20# そのモードの最中は、表示範囲の操作(ダブルクリックでのリセット / 右クリックで戻す)を譲る(gui/tools/view_navigation.py) 

21MouseMode = namedtuple("MouseMode", "name flag_attr action_attr toggle_method uses_left_click uses_right_click") 

22 

23MOUSE_MODES = ( 

24 MouseMode("cursor", "cursor_mode_enabled", 

25 "cursor_action", "_toggle_cursor_mode", True, False), 

26 MouseMode("annotation", "annotation_mode_enabled", 

27 "annotation_action", "_toggle_annotation_mode", True, True), 

28 MouseMode("layout_edit", "layout_edit_mode_enabled", 

29 "layout_edit_action", "_toggle_layout_edit_mode", True, False), 

30 MouseMode("range_select", "range_select_mode_enabled", 

31 "range_select_action", "_toggle_range_select_mode", False, False), 

32 MouseMode("peak_placement", "peak_placement_mode_enabled", 

33 "peak_placement_action", "_toggle_peak_placement_mode", True, True), 

34 MouseMode("slice_extraction", "slice_extraction_mode_enabled", 

35 "slice_extraction_action", "_toggle_slice_extraction_mode", False, False), 

36 MouseMode("region_highlight", "region_highlight_mode_enabled", 

37 "region_highlight_action", "_toggle_region_highlight_mode", False, True), 

38) 

39 

40MOUSE_MODES_BY_NAME = {mode.name: mode for mode in MOUSE_MODES} 

41 

42 

43class ToolManager: 

44 """モードの排他。タブの MOUSE_MODES の表を見て、フラグ・QAction・切り替えはタブの名前(ツールへの窓口)で触る。""" 

45 

46 def __init__(self, app): 

47 self._app = app 

48 

49 def deactivate_others(self, active_name): 

50 """active_name 以外のモードを解除する。有効でないモードには何もしないので、解除から呼び戻されて循環しない。""" 

51 if active_name not in MOUSE_MODES_BY_NAME: 

52 # 表への登録漏れ。動作は続けるが、開発中に気づけるようログに残す 

53 logger.warning( 

54 "未登録のマウスモード名です: %s (gui/tools/manager.py の " 

55 "MOUSE_MODES に追加してください)", active_name 

56 ) 

57 

58 for mode in self._app.MOUSE_MODES: 

59 if mode.name == active_name: 

60 continue 

61 if not getattr(self._app, mode.flag_attr, False): 

62 continue 

63 

64 action = getattr(self._app, mode.action_attr, None) 

65 if action is not None: 65 ↛ 68line 65 didn't jump to line 68 because the condition on line 65 was always true

66 action.setChecked(False) 

67 

68 toggle = getattr(self._app, mode.toggle_method, None) 

69 if callable(toggle): 69 ↛ 73line 69 didn't jump to line 73 because the condition on line 69 was always true

70 toggle(False) 

71 else: 

72 # メソッドが無くても、フラグは落として「2つ有効」を残さない 

73 setattr(self._app, mode.flag_attr, False) 

74 

75 def active_name(self): 

76 """有効なモードの名前。無ければ None。""" 

77 for mode in self._app.MOUSE_MODES: 

78 if getattr(self._app, mode.flag_attr, False): 

79 return mode.name 

80 return None