lazylabel-gui 1.1.3__tar.gz → 1.1.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {lazylabel_gui-1.1.3/src/lazylabel_gui.egg-info → lazylabel_gui-1.1.5}/PKG-INFO +1 -1
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/pyproject.toml +1 -1
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/config/hotkeys.py +208 -207
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/core/segment_manager.py +188 -170
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/ui/control_panel.py +6 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/ui/editable_vertex.py +2 -2
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/ui/main_window.py +128 -33
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/ui/right_panel.py +18 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5/src/lazylabel_gui.egg-info}/PKG-INFO +1 -1
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/LICENSE +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/README.md +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/setup.cfg +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/__init__.py +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/config/__init__.py +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/config/paths.py +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/config/settings.py +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/core/__init__.py +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/core/file_manager.py +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/core/model_manager.py +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/main.py +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/models/__init__.py +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/models/sam_model.py +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/ui/__init__.py +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/ui/hotkey_dialog.py +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/ui/hoverable_pixelmap_item.py +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/ui/hoverable_polygon_item.py +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/ui/numeric_table_widget_item.py +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/ui/photo_viewer.py +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/ui/reorderable_class_table.py +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/ui/widgets/__init__.py +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/ui/widgets/adjustments_widget.py +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/ui/widgets/model_selection_widget.py +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/ui/widgets/settings_widget.py +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/ui/widgets/status_bar.py +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/utils/__init__.py +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/utils/custom_file_system_model.py +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel/utils/utils.py +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel_gui.egg-info/SOURCES.txt +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel_gui.egg-info/dependency_links.txt +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel_gui.egg-info/entry_points.txt +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel_gui.egg-info/requires.txt +0 -0
- {lazylabel_gui-1.1.3 → lazylabel_gui-1.1.5}/src/lazylabel_gui.egg-info/top_level.txt +0 -0
@@ -1,207 +1,208 @@
|
|
1
|
-
"""Hotkey management system."""
|
2
|
-
|
3
|
-
import json
|
4
|
-
import os
|
5
|
-
from dataclasses import dataclass
|
6
|
-
|
7
|
-
from PyQt6.QtGui import QKeySequence
|
8
|
-
|
9
|
-
|
10
|
-
@dataclass
|
11
|
-
class HotkeyAction:
|
12
|
-
"""Represents a hotkey action with primary and secondary keys."""
|
13
|
-
|
14
|
-
name: str
|
15
|
-
description: str
|
16
|
-
primary_key: str
|
17
|
-
secondary_key: str | None = None
|
18
|
-
category: str = "General"
|
19
|
-
mouse_related: bool = False # Cannot be reassigned if True
|
20
|
-
|
21
|
-
|
22
|
-
class HotkeyManager:
|
23
|
-
"""Manages application hotkeys with persistence."""
|
24
|
-
|
25
|
-
def __init__(self, config_dir: str):
|
26
|
-
self.config_dir = config_dir
|
27
|
-
self.hotkeys_file = os.path.join(config_dir, "hotkeys.json")
|
28
|
-
self.actions: dict[str, HotkeyAction] = {}
|
29
|
-
self._initialize_default_hotkeys()
|
30
|
-
self.load_hotkeys()
|
31
|
-
|
32
|
-
def _initialize_default_hotkeys(self):
|
33
|
-
"""Initialize default hotkey mappings."""
|
34
|
-
default_hotkeys = [
|
35
|
-
# Navigation
|
36
|
-
HotkeyAction(
|
37
|
-
"load_next_image", "Load Next Image", "Right", category="Navigation"
|
38
|
-
),
|
39
|
-
HotkeyAction(
|
40
|
-
"load_previous_image",
|
41
|
-
"Load Previous Image",
|
42
|
-
"Left",
|
43
|
-
category="Navigation",
|
44
|
-
),
|
45
|
-
HotkeyAction("fit_view", "Fit View", ".", category="Navigation"),
|
46
|
-
# Modes
|
47
|
-
HotkeyAction("sam_mode", "Point Mode (SAM)", "1", category="Modes"),
|
48
|
-
HotkeyAction("polygon_mode", "Polygon Mode", "2", category="Modes"),
|
49
|
-
HotkeyAction("
|
50
|
-
HotkeyAction("
|
51
|
-
HotkeyAction("
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
HotkeyAction(
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
HotkeyAction(
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
"
|
80
|
-
"
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
HotkeyAction("
|
89
|
-
|
90
|
-
|
91
|
-
HotkeyAction("
|
92
|
-
HotkeyAction("
|
93
|
-
HotkeyAction("
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
"
|
98
|
-
"
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
"
|
105
|
-
"
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
"
|
112
|
-
"
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
"
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
self.actions[name].
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
1
|
+
"""Hotkey management system."""
|
2
|
+
|
3
|
+
import json
|
4
|
+
import os
|
5
|
+
from dataclasses import dataclass
|
6
|
+
|
7
|
+
from PyQt6.QtGui import QKeySequence
|
8
|
+
|
9
|
+
|
10
|
+
@dataclass
|
11
|
+
class HotkeyAction:
|
12
|
+
"""Represents a hotkey action with primary and secondary keys."""
|
13
|
+
|
14
|
+
name: str
|
15
|
+
description: str
|
16
|
+
primary_key: str
|
17
|
+
secondary_key: str | None = None
|
18
|
+
category: str = "General"
|
19
|
+
mouse_related: bool = False # Cannot be reassigned if True
|
20
|
+
|
21
|
+
|
22
|
+
class HotkeyManager:
|
23
|
+
"""Manages application hotkeys with persistence."""
|
24
|
+
|
25
|
+
def __init__(self, config_dir: str):
|
26
|
+
self.config_dir = config_dir
|
27
|
+
self.hotkeys_file = os.path.join(config_dir, "hotkeys.json")
|
28
|
+
self.actions: dict[str, HotkeyAction] = {}
|
29
|
+
self._initialize_default_hotkeys()
|
30
|
+
self.load_hotkeys()
|
31
|
+
|
32
|
+
def _initialize_default_hotkeys(self):
|
33
|
+
"""Initialize default hotkey mappings."""
|
34
|
+
default_hotkeys = [
|
35
|
+
# Navigation
|
36
|
+
HotkeyAction(
|
37
|
+
"load_next_image", "Load Next Image", "Right", category="Navigation"
|
38
|
+
),
|
39
|
+
HotkeyAction(
|
40
|
+
"load_previous_image",
|
41
|
+
"Load Previous Image",
|
42
|
+
"Left",
|
43
|
+
category="Navigation",
|
44
|
+
),
|
45
|
+
HotkeyAction("fit_view", "Fit View", ".", category="Navigation"),
|
46
|
+
# Modes
|
47
|
+
HotkeyAction("sam_mode", "Point Mode (SAM)", "1", category="Modes"),
|
48
|
+
HotkeyAction("polygon_mode", "Polygon Mode", "2", category="Modes"),
|
49
|
+
HotkeyAction("bbox_mode", "Bounding Box Mode", "3", category="Modes"),
|
50
|
+
HotkeyAction("selection_mode", "Selection Mode", "E", category="Modes"),
|
51
|
+
HotkeyAction("pan_mode", "Pan Mode", "Q", category="Modes"),
|
52
|
+
HotkeyAction("edit_mode", "Edit Mode", "R", category="Modes"),
|
53
|
+
# Actions
|
54
|
+
HotkeyAction(
|
55
|
+
"clear_points", "Clear Points/Vertices", "C", category="Actions"
|
56
|
+
),
|
57
|
+
HotkeyAction(
|
58
|
+
"save_segment", "Save Current Segment", "Space", category="Actions"
|
59
|
+
),
|
60
|
+
HotkeyAction("save_output", "Save Output", "Return", category="Actions"),
|
61
|
+
HotkeyAction(
|
62
|
+
"save_output_alt", "Save Output (Alt)", "Enter", category="Actions"
|
63
|
+
),
|
64
|
+
HotkeyAction("undo", "Undo Last Action", "Ctrl+Z", category="Actions"),
|
65
|
+
HotkeyAction(
|
66
|
+
"redo", "Redo Last Action", "Ctrl+Y", "Ctrl+Shift+Z", category="Actions"
|
67
|
+
),
|
68
|
+
HotkeyAction(
|
69
|
+
"escape", "Cancel/Clear Selection", "Escape", category="Actions"
|
70
|
+
),
|
71
|
+
# Segments
|
72
|
+
HotkeyAction(
|
73
|
+
"merge_segments", "Merge Selected Segments", "M", category="Segments"
|
74
|
+
),
|
75
|
+
HotkeyAction(
|
76
|
+
"delete_segments", "Delete Selected Segments", "V", category="Segments"
|
77
|
+
),
|
78
|
+
HotkeyAction(
|
79
|
+
"delete_segments_alt",
|
80
|
+
"Delete Selected Segments (Alt)",
|
81
|
+
"Backspace",
|
82
|
+
category="Segments",
|
83
|
+
),
|
84
|
+
HotkeyAction(
|
85
|
+
"select_all", "Select All Segments", "Ctrl+A", category="Segments"
|
86
|
+
),
|
87
|
+
# View
|
88
|
+
HotkeyAction("zoom_in", "Zoom In", "Ctrl+Plus", category="View"),
|
89
|
+
HotkeyAction("zoom_out", "Zoom Out", "Ctrl+Minus", category="View"),
|
90
|
+
# Movement (WASD)
|
91
|
+
HotkeyAction("pan_up", "Pan Up", "W", category="Movement"),
|
92
|
+
HotkeyAction("pan_down", "Pan Down", "S", category="Movement"),
|
93
|
+
HotkeyAction("pan_left", "Pan Left", "A", category="Movement"),
|
94
|
+
HotkeyAction("pan_right", "Pan Right", "D", category="Movement"),
|
95
|
+
# Mouse-related (cannot be reassigned)
|
96
|
+
HotkeyAction(
|
97
|
+
"left_click",
|
98
|
+
"Add Positive Point / Select",
|
99
|
+
"Left Click",
|
100
|
+
category="Mouse",
|
101
|
+
mouse_related=True,
|
102
|
+
),
|
103
|
+
HotkeyAction(
|
104
|
+
"right_click",
|
105
|
+
"Add Negative Point",
|
106
|
+
"Right Click",
|
107
|
+
category="Mouse",
|
108
|
+
mouse_related=True,
|
109
|
+
),
|
110
|
+
HotkeyAction(
|
111
|
+
"mouse_drag",
|
112
|
+
"Drag/Pan",
|
113
|
+
"Mouse Drag",
|
114
|
+
category="Mouse",
|
115
|
+
mouse_related=True,
|
116
|
+
),
|
117
|
+
]
|
118
|
+
|
119
|
+
for action in default_hotkeys:
|
120
|
+
self.actions[action.name] = action
|
121
|
+
|
122
|
+
def get_action(self, action_name: str) -> HotkeyAction | None:
|
123
|
+
"""Get hotkey action by name."""
|
124
|
+
return self.actions.get(action_name)
|
125
|
+
|
126
|
+
def get_actions_by_category(self) -> dict[str, list[HotkeyAction]]:
|
127
|
+
"""Get actions grouped by category."""
|
128
|
+
categories = {}
|
129
|
+
for action in self.actions.values():
|
130
|
+
if action.category not in categories:
|
131
|
+
categories[action.category] = []
|
132
|
+
categories[action.category].append(action)
|
133
|
+
return categories
|
134
|
+
|
135
|
+
def set_primary_key(self, action_name: str, key: str) -> bool:
|
136
|
+
"""Set primary key for an action."""
|
137
|
+
if action_name in self.actions and not self.actions[action_name].mouse_related:
|
138
|
+
self.actions[action_name].primary_key = key
|
139
|
+
return True
|
140
|
+
return False
|
141
|
+
|
142
|
+
def set_secondary_key(self, action_name: str, key: str | None) -> bool:
|
143
|
+
"""Set secondary key for an action."""
|
144
|
+
if action_name in self.actions and not self.actions[action_name].mouse_related:
|
145
|
+
self.actions[action_name].secondary_key = key
|
146
|
+
return True
|
147
|
+
return False
|
148
|
+
|
149
|
+
def get_key_for_action(self, action_name: str) -> tuple[str | None, str | None]:
|
150
|
+
"""Get primary and secondary keys for an action."""
|
151
|
+
action = self.actions.get(action_name)
|
152
|
+
if action:
|
153
|
+
return action.primary_key, action.secondary_key
|
154
|
+
return None, None
|
155
|
+
|
156
|
+
def is_key_in_use(self, key: str, exclude_action: str = None) -> str | None:
|
157
|
+
"""Check if a key is already in use by another action."""
|
158
|
+
for name, action in self.actions.items():
|
159
|
+
if name == exclude_action:
|
160
|
+
continue
|
161
|
+
if action.primary_key == key or action.secondary_key == key:
|
162
|
+
return name
|
163
|
+
return None
|
164
|
+
|
165
|
+
def reset_to_defaults(self):
|
166
|
+
"""Reset all hotkeys to default values."""
|
167
|
+
self._initialize_default_hotkeys()
|
168
|
+
|
169
|
+
def save_hotkeys(self):
|
170
|
+
"""Save hotkeys to file."""
|
171
|
+
os.makedirs(self.config_dir, exist_ok=True)
|
172
|
+
|
173
|
+
# Convert to serializable format
|
174
|
+
data = {}
|
175
|
+
for name, action in self.actions.items():
|
176
|
+
if not action.mouse_related: # Don't save mouse-related actions
|
177
|
+
data[name] = {
|
178
|
+
"primary_key": action.primary_key,
|
179
|
+
"secondary_key": action.secondary_key,
|
180
|
+
}
|
181
|
+
|
182
|
+
with open(self.hotkeys_file, "w") as f:
|
183
|
+
json.dump(data, f, indent=4)
|
184
|
+
|
185
|
+
def load_hotkeys(self):
|
186
|
+
"""Load hotkeys from file."""
|
187
|
+
if not os.path.exists(self.hotkeys_file):
|
188
|
+
return
|
189
|
+
|
190
|
+
try:
|
191
|
+
with open(self.hotkeys_file) as f:
|
192
|
+
data = json.load(f)
|
193
|
+
|
194
|
+
for name, keys in data.items():
|
195
|
+
if name in self.actions and not self.actions[name].mouse_related:
|
196
|
+
self.actions[name].primary_key = keys.get("primary_key", "")
|
197
|
+
self.actions[name].secondary_key = keys.get("secondary_key")
|
198
|
+
except (json.JSONDecodeError, KeyError, FileNotFoundError):
|
199
|
+
# If loading fails, keep defaults
|
200
|
+
pass
|
201
|
+
|
202
|
+
def key_sequence_to_string(self, key_sequence: QKeySequence) -> str:
|
203
|
+
"""Convert QKeySequence to string representation."""
|
204
|
+
return key_sequence.toString()
|
205
|
+
|
206
|
+
def string_to_key_sequence(self, key_string: str) -> QKeySequence:
|
207
|
+
"""Convert string to QKeySequence."""
|
208
|
+
return QKeySequence(key_string)
|