-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathkeymap.py
More file actions
213 lines (165 loc) · 7.57 KB
/
keymap.py
File metadata and controls
213 lines (165 loc) · 7.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
import bpy
keymaps_items_dict = {
"Simple Camera Manager": {"name": 'cam_menu', "idname": 'wm.call_panel',
"operator": 'OBJECT_PT_camera_manager_popup', "type": 'C', "value": 'PRESS',
"ctrl": False, "shift": True, "alt": True, "active": True},
"Active Camera Pie": {"name": 'cam_pie', "idname": 'wm.call_menu_pie',
"operator": 'CAMERA_MT_pie_menu',
"type": 'C', "value": 'PRESS', "ctrl": False, "shift": False, "alt": True, "active": True},
"Next Camera": {"name": 'next_cam', "idname": 'cam_manager.cycle_cameras_next',
"operator": '', "type": 'RIGHT_ARROW',
"value": 'PRESS', "ctrl": True, "shift": True, "alt": False, "active": True},
"Previous Camera": {"name": 'prev_cam', "idname": 'cam_manager.cycle_cameras_backward',
"operator": '', "type": 'LEFT_ARROW', "value": 'PRESS', "ctrl": True, "shift": True,
"alt": False, "active": True}}
def get_keymap_string(item_id, item_type):
"""Return a human-readable shortcut string (e.g. 'Ctrl + Shift + C') for the given operator, panel, or menu id."""
# Get all keymaps
keymaps = bpy.context.window_manager.keyconfigs.user.keymaps
# Find the keymap item for the given item_id and item_type
keymap_item = None
for km in keymaps:
for kmi in km.keymap_items:
if item_type == "PANEL" and kmi.idname == "wm.call_panel" and kmi.properties.name == item_id:
keymap_item = kmi
break
elif item_type == "MENU" and kmi.idname == "wm.call_menu_pie":
if kmi.properties.name == item_id:
keymap_item = kmi
break
elif item_type == "OPERATOR" and kmi.idname == item_id:
keymap_item = kmi
break
if keymap_item:
break
if not keymap_item:
return "Keymap not found"
# Extract the key information
modifiers = []
if keymap_item.ctrl:
modifiers.append("Ctrl")
if keymap_item.alt:
modifiers.append("Alt")
if keymap_item.shift:
modifiers.append("Shift")
if keymap_item.oskey:
modifiers.append("Cmd" if bpy.app.build_platform == 'Darwin' else "Win")
key = keymap_item.type
# Return the keymap in the desired format
return " + ".join(modifiers + [key])
def add_key(context, idname, type, ctrl, shift, alt, operator, active):
"""Register a new keymap item in the addon's Window keymap."""
wm = context.window_manager
addon_km = wm.keyconfigs.addon.keymaps.get('Window') # Using Window instead of 3D View to fix issue of keymap not working on Linux
if not addon_km:
addon_km = wm.keyconfigs.addon.keymaps.new(name="Window")
kmi = addon_km.keymap_items.new(idname=idname, type=type, value='PRESS', ctrl=ctrl, shift=shift, alt=alt)
if operator != '':
kmi.properties.name = operator
kmi.active = active
def remove_key(context, idname, properties_name):
"""Removes addon hotkeys from the keymap"""
wm = context.window_manager
addon_km = wm.keyconfigs.addon.keymaps.get('Window')
if not addon_km:
return
items_to_remove = []
for kmi in addon_km.keymap_items:
if properties_name:
if kmi.idname == idname and hasattr(kmi.properties, 'name') and kmi.properties.name == properties_name:
items_to_remove.append(kmi)
else:
if kmi.idname == idname:
items_to_remove.append(kmi)
for kmi in items_to_remove:
addon_km.keymap_items.remove(kmi)
def add_keymap():
"""Register all addon hotkeys from user preferences into Blender's keymap."""
context = bpy.context
prefs = context.preferences.addons[__package__].preferences
for key, valueDic in keymaps_items_dict.items():
idname = valueDic["idname"]
type = getattr(prefs, f'{valueDic["name"]}_type')
ctrl = getattr(prefs, f'{valueDic["name"]}_ctrl')
shift = getattr(prefs, f'{valueDic["name"]}_shift')
alt = getattr(prefs, f'{valueDic["name"]}_alt')
operator = valueDic["operator"]
active = valueDic["active"]
add_key(context, idname, type, ctrl, shift, alt, operator, active)
def add_key_to_keymap(idname, kmi, active=True):
"""Add a key to the appropriate keymap."""
kmi.properties.name = idname
kmi.active = active
def remove_keymap():
"""Remove all addon hotkeys from Blender's keymap."""
wm = bpy.context.window_manager
addon_keymaps = wm.keyconfigs.addon.keymaps.get('Window')
if not addon_keymaps:
return
# Collect items to remove first
items_to_remove = []
for kmi in addon_keymaps.keymap_items:
for key, valueDic in keymaps_items_dict.items():
idname = valueDic["idname"]
operator = valueDic["operator"]
if kmi.idname == idname and (not operator or getattr(kmi.properties, 'name', '') == operator):
items_to_remove.append(kmi)
# Remove items
for kmi in items_to_remove:
addon_keymaps.keymap_items.remove(kmi)
class REMOVE_OT_hotkey(bpy.types.Operator):
"""Remove an assigned hotkey from the addon keymap."""
bl_idname = "cam.remove_hotkey"
bl_label = "Remove Hotkey"
bl_description = "Remove the assigned hotkey for this action"
bl_options = {'REGISTER', 'INTERNAL'}
idname: bpy.props.StringProperty()
properties_name: bpy.props.StringProperty()
property_prefix: bpy.props.StringProperty()
def execute(self, context):
remove_key(context, self.idname, self.properties_name)
prefs = bpy.context.preferences.addons[__package__].preferences
setattr(prefs, f'{self.property_prefix}_type', "NONE")
setattr(prefs, f'{self.property_prefix}_ctrl', False)
setattr(prefs, f'{self.property_prefix}_shift', False)
setattr(prefs, f'{self.property_prefix}_alt', False)
return {'FINISHED'}
class BUTTON_OT_change_key(bpy.types.Operator):
"""Modal operator that waits for a key press and assigns it to an addon hotkey."""
bl_idname = "cam.key_selection_button"
bl_label = "Press the key you want to assign to this action"
bl_description = "Click then press a key to assign it as the hotkey for this action"
bl_options = {'REGISTER', 'INTERNAL'}
property_prefix: bpy.props.StringProperty()
def invoke(self, context, event):
prefs = bpy.context.preferences.addons[__package__].preferences
self.prefs = prefs
setattr(prefs, f'{self.property_prefix}_type', "NONE")
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
def modal(self, context, event):
self.my_event = 'NONE'
if event.type and event.value == 'RELEASE': # Apply
self.my_event = event.type
setattr(self.prefs, f'{self.property_prefix}_type', self.my_event)
self.execute(context)
return {'FINISHED'}
return {'RUNNING_MODAL'}
def execute(self, context):
self.report({'INFO'},
"Key change: " + bpy.types.Event.bl_rna.properties['type'].enum_items[self.my_event].name)
return {'FINISHED'}
classes = (
BUTTON_OT_change_key,
REMOVE_OT_hotkey,
)
def register():
from bpy.utils import register_class
for cls in classes:
register_class(cls)
def unregister():
remove_keymap()
from bpy.utils import unregister_class
for cls in reversed(classes):
if hasattr(cls, 'bl_rna'):
unregister_class(cls)