1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 # PEP8 compliant (https://www.python.org/dev/peps/pep-0008)
22 "name": "Is key Free",
23 "author": "Antonio Vazquez (antonioya)",
26 "location": "Text Editor > Props Shelf (Ctrl/t > IsKeyFree Tools",
27 "description": "Find free shortcuts, inform about used and print a key list",
28 "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6"
29 "/Py/Scripts/Development/IsKeyFree",
30 "category": "Development"
34 from bpy
.props
import (
40 from bpy
.types
import (
47 # ------------------------------------------------------
48 # Class to find keymaps
49 # ------------------------------------------------------
60 # Verify if the key is used
62 def check(cls
, findkey
, ctrl
, alt
, shift
, oskey
):
73 cls
.lastfind
= cmd
+ findkey
.upper()
74 cls
.lastkey
= findkey
.upper()
79 wm
= bpy
.context
.window_manager
82 for context
, keyboardmap
in wm
.keyconfigs
.user
.keymaps
.items():
83 for myitem
in keyboardmap
.keymap_items
:
84 if myitem
.active
is True and myitem
.type == findkey
:
85 if ctrl
is True and myitem
.ctrl
is not True:
87 if alt
is True and myitem
.alt
is not True:
89 if shift
is True and myitem
.shift
is not True:
91 if oskey
is True and myitem
.oskey
is not True:
95 "Ctrl" if myitem
.ctrl
is True else "",
96 "Alt" if myitem
.alt
is True else "",
97 "Shift" if myitem
.shift
is True else "",
98 "OsKey" if myitem
.oskey
is True else "",
103 sortkeys
= sorted(mykeys
, key
=lambda key
: (key
[0], key
[1], key
[2], key
[3], key
[4], key
[5]))
121 cls
.mylist
.append([e
[0], cmd
])
126 return str(bpy
.context
.screen
.name
)
138 # return result of last search
143 # verify if key is valid
145 def isvalidkey(cls
, txt
):
147 "LEFTMOUSE", "MIDDLEMOUSE", "RIGHTMOUSE", "BUTTON4MOUSE", "BUTTON5MOUSE", "BUTTON6MOUSE",
149 "MOUSEMOVE", "INBETWEEN_MOUSEMOVE", "TRACKPADPAN", "TRACKPADZOOM",
150 "MOUSEROTATE", "WHEELUPMOUSE", "WHEELDOWNMOUSE", "WHEELINMOUSE", "WHEELOUTMOUSE", "EVT_TWEAK_L",
151 "EVT_TWEAK_M", "EVT_TWEAK_R", "A", "B", "C", "D", "E", "F", "G", "H",
153 "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "ZERO", "ONE", "TWO",
154 "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE", "LEFT_CTRL", "LEFT_ALT", "LEFT_SHIFT",
156 "RIGHT_CTRL", "RIGHT_SHIFT", "OSKEY", "GRLESS", "ESC", "TAB", "RET", "SPACE", "LINE_FEED",
158 "DEL", "SEMI_COLON", "PERIOD", "COMMA", "QUOTE", "ACCENT_GRAVE", "MINUS", "SLASH", "BACK_SLASH",
160 "LEFT_BRACKET", "RIGHT_BRACKET", "LEFT_ARROW", "DOWN_ARROW", "RIGHT_ARROW", "UP_ARROW", "NUMPAD_2",
161 "NUMPAD_4", "NUMPAD_6", "NUMPAD_8", "NUMPAD_1", "NUMPAD_3", "NUMPAD_5", "NUMPAD_7", "NUMPAD_9",
162 "NUMPAD_PERIOD", "NUMPAD_SLASH", "NUMPAD_ASTERIX", "NUMPAD_0", "NUMPAD_MINUS", "NUMPAD_ENTER",
164 "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15",
166 "F18", "F19", "PAUSE", "INSERT", "HOME", "PAGE_UP", "PAGE_DOWN", "END", "MEDIA_PLAY", "MEDIA_STOP",
167 "MEDIA_FIRST", "MEDIA_LAST", "TEXTINPUT", "WINDOW_DEACTIVATE", "TIMER", "TIMER0", "TIMER1", "TIMER2",
168 "TIMER_JOBS", "TIMER_AUTOSAVE", "TIMER_REPORT", "TIMERREGION", "NDOF_MOTION", "NDOF_BUTTON_MENU",
169 "NDOF_BUTTON_FIT", "NDOF_BUTTON_TOP", "NDOF_BUTTON_BOTTOM", "NDOF_BUTTON_LEFT", "NDOF_BUTTON_RIGHT",
170 "NDOF_BUTTON_FRONT", "NDOF_BUTTON_BACK", "NDOF_BUTTON_ISO1", "NDOF_BUTTON_ISO2",
171 "NDOF_BUTTON_ROLL_CW",
172 "NDOF_BUTTON_ROLL_CCW", "NDOF_BUTTON_SPIN_CW", "NDOF_BUTTON_SPIN_CCW", "NDOF_BUTTON_TILT_CW",
173 "NDOF_BUTTON_TILT_CCW", "NDOF_BUTTON_ROTATE", "NDOF_BUTTON_PANZOOM", "NDOF_BUTTON_DOMINANT",
174 "NDOF_BUTTON_PLUS", "NDOF_BUTTON_MINUS", "NDOF_BUTTON_ESC", "NDOF_BUTTON_ALT", "NDOF_BUTTON_SHIFT",
175 "NDOF_BUTTON_CTRL", "NDOF_BUTTON_1", "NDOF_BUTTON_2", "NDOF_BUTTON_3", "NDOF_BUTTON_4",
177 "NDOF_BUTTON_6", "NDOF_BUTTON_7", "NDOF_BUTTON_8", "NDOF_BUTTON_9", "NDOF_BUTTON_10",
179 "NDOF_BUTTON_B", "NDOF_BUTTON_C"
188 mychecker
= MyChecker() # Global class handler
191 # ------------------------------------------------------
192 # Button: Class for search button
193 # ------------------------------------------------------
194 class RunActionCheck(Operator
):
195 bl_idname
= "iskeyfree.action_check"
197 bl_description
= "Verify if the selected shortcut is free"
199 # noinspection PyUnusedLocal
200 def execute(self
, context
):
201 scene
= context
.scene
.is_keyfree
202 txt
= scene
.data
.upper()
204 mychecker
.check(txt
, scene
.use_crtl
, scene
.use_alt
, scene
.use_shift
,
210 # ------------------------------------------------------
212 # ------------------------------------------------------
213 class UIControlPanel(Panel
):
214 bl_idname
= "DEVISKEYFREE_PT_ui"
215 bl_space_type
= "TEXT_EDITOR"
216 bl_region_type
= "UI"
217 bl_label
= "Is Key Free"
219 # noinspection PyUnusedLocal
220 def draw(self
, context
):
222 scene
= context
.scene
.is_keyfree
224 row
= layout
.row(align
=True)
225 row
.prop(scene
, "data")
226 row
.operator("iskeyfree.action_check", icon
="VIEWZOOM")
228 row
= layout
.row(align
=True)
229 row
.prop(scene
, "use_crtl", toggle
=True)
230 row
.prop(scene
, "use_alt", toggle
=True)
231 row
.prop(scene
, "use_shift", toggle
=True)
232 row
.prop(scene
, "use_oskey", toggle
=True)
235 row
.prop(scene
, "numpad")
237 layout
.operator("iskeyfree.run_export_keys", icon
="FILE_TEXT")
240 mylist
= mychecker
.getlist()
245 cmd
= mychecker
.getlast()
248 row
.label("Current uses of " + str(cmd
), icon
="PARTICLE_DATA")
250 if oldcontext
!= e
[0]:
252 box
.label(e
[0], icon
="UNPINNED")
255 row
= box
.row(align
=True)
258 cmd
= mychecker
.getlast()
261 if mychecker
.isvalidkey(mychecker
.getlastkey()) is False:
262 box
.label(str(mychecker
.getlastkey()) + " looks not valid key", icon
="ERROR")
264 box
.label(str(cmd
) + " is free", icon
="FILE_TICK")
267 # ------------------------------------------------------
268 # Update key (special values) event handler
269 # ------------------------------------------------------
270 # noinspection PyUnusedLocal
271 def update_data(self
, context
):
272 scene
= context
.scene
.is_keyfree
273 if scene
.numpad
!= "NONE":
274 scene
.data
= scene
.numpad
277 class IskeyFreeProperties(PropertyGroup
):
278 data
= StringProperty(
279 name
="Key", maxlen
=32,
280 description
="Shortcut to verify"
282 use_crtl
= BoolProperty(
284 description
="Ctrl key used in shortcut",
287 use_alt
= BoolProperty(
289 description
="Alt key used in shortcut",
292 use_shift
= BoolProperty(
294 description
="Shift key used in shortcut",
297 use_oskey
= BoolProperty(
299 description
="Operating system key used in shortcut",
302 numpad
= EnumProperty(
304 ('NONE', "Select key", ""),
305 ("LEFTMOUSE", "LEFTMOUSE", ""),
306 ("MIDDLEMOUSE", "MIDDLEMOUSE", ""),
307 ("RIGHTMOUSE", "RIGHTMOUSE", ""),
308 ("BUTTON4MOUSE", "BUTTON4MOUSE", ""),
309 ("BUTTON5MOUSE", "BUTTON5MOUSE", ""),
310 ("BUTTON6MOUSE", "BUTTON6MOUSE", ""),
311 ("BUTTON7MOUSE", "BUTTON7MOUSE", ""),
312 ("MOUSEMOVE", "MOUSEMOVE", ""),
313 ("INBETWEEN_MOUSEMOVE", "INBETWEEN_MOUSEMOVE", ""),
314 ("TRACKPADPAN", "TRACKPADPAN", ""),
315 ("TRACKPADZOOM", "TRACKPADZOOM", ""),
316 ("MOUSEROTATE", "MOUSEROTATE", ""),
317 ("WHEELUPMOUSE", "WHEELUPMOUSE", ""),
318 ("WHEELDOWNMOUSE", "WHEELDOWNMOUSE", ""),
319 ("WHEELINMOUSE", "WHEELINMOUSE", ""),
320 ("WHEELOUTMOUSE", "WHEELOUTMOUSE", ""),
321 ("EVT_TWEAK_L", "EVT_TWEAK_L", ""),
322 ("EVT_TWEAK_M", "EVT_TWEAK_M", ""),
323 ("EVT_TWEAK_R", "EVT_TWEAK_R", ""),
350 ("ZERO", "ZERO", ""),
353 ("THREE", "THREE", ""),
354 ("FOUR", "FOUR", ""),
355 ("FIVE", "FIVE", ""),
357 ("SEVEN", "SEVEN", ""),
358 ("EIGHT", "EIGHT", ""),
359 ("NINE", "NINE", ""),
360 ("LEFT_CTRL", "LEFT_CTRL", ""),
361 ("LEFT_ALT", "LEFT_ALT", ""),
362 ("LEFT_SHIFT", "LEFT_SHIFT", ""),
363 ("RIGHT_ALT", "RIGHT_ALT", ""),
364 ("RIGHT_CTRL", "RIGHT_CTRL", ""),
365 ("RIGHT_SHIFT", "RIGHT_SHIFT", ""),
366 ("OSKEY", "OSKEY", ""),
367 ("GRLESS", "GRLESS", ""),
371 ("SPACE", "SPACE", ""),
372 ("LINE_FEED", "LINE_FEED", ""),
373 ("BACK_SPACE", "BACK_SPACE", ""),
375 ("SEMI_COLON", "SEMI_COLON", ""),
376 ("PERIOD", "PERIOD", ""),
377 ("COMMA", "COMMA", ""),
378 ("QUOTE", "QUOTE", ""),
379 ("ACCENT_GRAVE", "ACCENT_GRAVE", ""),
380 ("MINUS", "MINUS", ""),
381 ("SLASH", "SLASH", ""),
382 ("BACK_SLASH", "BACK_SLASH", ""),
383 ("EQUAL", "EQUAL", ""),
384 ("LEFT_BRACKET", "LEFT_BRACKET", ""),
385 ("RIGHT_BRACKET", "RIGHT_BRACKET", ""),
386 ("LEFT_ARROW", "LEFT_ARROW", ""),
387 ("DOWN_ARROW", "DOWN_ARROW", ""),
388 ("RIGHT_ARROW", "RIGHT_ARROW", ""),
389 ("UP_ARROW", "UP_ARROW", ""),
390 ("NUMPAD_1", "NUMPAD_1", ""),
391 ("NUMPAD_2", "NUMPAD_2", ""),
392 ("NUMPAD_3", "NUMPAD_3", ""),
393 ("NUMPAD_4", "NUMPAD_4", ""),
394 ("NUMPAD_5", "NUMPAD_5", ""),
395 ("NUMPAD_6", "NUMPAD_6", ""),
396 ("NUMPAD_7", "NUMPAD_7", ""),
397 ("NUMPAD_8", "NUMPAD_8", ""),
398 ("NUMPAD_9", "NUMPAD_9", ""),
399 ("NUMPAD_0", "NUMPAD_0", ""),
400 ("NUMPAD_PERIOD", "NUMPAD_PERIOD", ""),
401 ("NUMPAD_SLASH", "NUMPAD_SLASH", ""),
402 ("NUMPAD_ASTERIX", "NUMPAD_ASTERIX", ""),
403 ("NUMPAD_MINUS", "NUMPAD_MINUS", ""),
404 ("NUMPAD_ENTER", "NUMPAD_ENTER", ""),
405 ("NUMPAD_PLUS", "NUMPAD_PLUS", ""),
425 ("PAUSE", "PAUSE", ""),
426 ("INSERT", "INSERT", ""),
427 ("HOME", "HOME", ""),
428 ("PAGE_UP", "PAGE_UP", ""),
429 ("PAGE_DOWN", "PAGE_DOWN", ""),
431 ("MEDIA_PLAY", "MEDIA_PLAY", ""),
432 ("MEDIA_STOP", "MEDIA_STOP", ""),
433 ("MEDIA_FIRST", "MEDIA_FIRST", ""),
434 ("MEDIA_LAST", "MEDIA_LAST", ""),
435 ("TEXTINPUT", "TEXTINPUT", ""),
436 ("WINDOW_DEACTIVATE", "WINDOW_DEACTIVATE", ""),
437 ("TIMER", "TIMER", ""),
438 ("TIMER0", "TIMER0", ""),
439 ("TIMER1", "TIMER1", ""),
440 ("TIMER2", "TIMER2", ""),
441 ("TIMER_JOBS", "TIMER_JOBS", ""),
442 ("TIMER_AUTOSAVE", "TIMER_AUTOSAVE", ""),
443 ("TIMER_REPORT", "TIMER_REPORT", ""),
444 ("TIMERREGION", "TIMERREGION", ""),
445 ("NDOF_MOTION", "NDOF_MOTION", ""),
446 ("NDOF_BUTTON_MENU", "NDOF_BUTTON_MENU", ""),
447 ("NDOF_BUTTON_FIT", "NDOF_BUTTON_FIT", ""),
448 ("NDOF_BUTTON_TOP", "NDOF_BUTTON_TOP", ""),
449 ("NDOF_BUTTON_BOTTOM", "NDOF_BUTTON_BOTTOM", ""),
450 ("NDOF_BUTTON_LEFT", "NDOF_BUTTON_LEFT", ""),
451 ("NDOF_BUTTON_RIGHT", "NDOF_BUTTON_RIGHT", ""),
452 ("NDOF_BUTTON_FRONT", "NDOF_BUTTON_FRONT", ""),
453 ("NDOF_BUTTON_BACK", "NDOF_BUTTON_BACK", ""),
454 ("NDOF_BUTTON_ISO1", "NDOF_BUTTON_ISO1", ""),
455 ("NDOF_BUTTON_ISO2", "NDOF_BUTTON_ISO2", ""),
456 ("NDOF_BUTTON_ROLL_CW", "NDOF_BUTTON_ROLL_CW", ""),
457 ("NDOF_BUTTON_ROLL_CCW", "NDOF_BUTTON_ROLL_CCW", ""),
458 ("NDOF_BUTTON_SPIN_CW", "NDOF_BUTTON_SPIN_CW", ""),
459 ("NDOF_BUTTON_SPIN_CCW", "NDOF_BUTTON_SPIN_CCW", ""),
460 ("NDOF_BUTTON_TILT_CW", "NDOF_BUTTON_TILT_CW", ""),
461 ("NDOF_BUTTON_TILT_CCW", "NDOF_BUTTON_TILT_CCW", ""),
462 ("NDOF_BUTTON_ROTATE", "NDOF_BUTTON_ROTATE", ""),
463 ("NDOF_BUTTON_PANZOOM", "NDOF_BUTTON_PANZOOM", ""),
464 ("NDOF_BUTTON_DOMINANT", "NDOF_BUTTON_DOMINANT", ""),
465 ("NDOF_BUTTON_PLUS", "NDOF_BUTTON_PLUS", ""),
466 ("NDOF_BUTTON_MINUS", "NDOF_BUTTON_MINUS", ""),
467 ("NDOF_BUTTON_ESC", "NDOF_BUTTON_ESC", ""),
468 ("NDOF_BUTTON_ALT", "NDOF_BUTTON_ALT", ""),
469 ("NDOF_BUTTON_SHIFT", "NDOF_BUTTON_SHIFT", ""),
470 ("NDOF_BUTTON_CTRL", "NDOF_BUTTON_CTRL", ""),
471 ("NDOF_BUTTON_1", "NDOF_BUTTON_1", ""),
472 ("NDOF_BUTTON_2", "NDOF_BUTTON_2", ""),
473 ("NDOF_BUTTON_3", "NDOF_BUTTON_3", ""),
474 ("NDOF_BUTTON_4", "NDOF_BUTTON_4", ""),
475 ("NDOF_BUTTON_5", "NDOF_BUTTON_5", ""),
476 ("NDOF_BUTTON_6", "NDOF_BUTTON_6", ""),
477 ("NDOF_BUTTON_7", "NDOF_BUTTON_7", ""),
478 ("NDOF_BUTTON_8", "NDOF_BUTTON_8", ""),
479 ("NDOF_BUTTON_9", "NDOF_BUTTON_9", ""),
480 ("NDOF_BUTTON_10", "NDOF_BUTTON_10", ""),
481 ("NDOF_BUTTON_A", "NDOF_BUTTON_A", ""),
482 ("NDOF_BUTTON_B", "NDOF_BUTTON_B", ""),
483 ("NDOF_BUTTON_C", "NDOF_BUTTON_C", "")
486 description
="Enter key code in find text",
491 class IsKeyFreeRunExportKeys(Operator
):
492 bl_idname
= "iskeyfree.run_export_keys"
493 bl_label
= "List all Shortcuts"
494 bl_description
= ("List all existing shortcuts in a text block\n"
495 "The newly generated list will be made active in the Text Editor\n"
496 "To access the previous ones, select them from the Header dropdown")
498 def all_shortcuts_name(self
, context
):
499 new_name
, def_name
, ext
= "", "All_Shortcuts", ".txt"
502 # first slap a simple linear count + 1 for numeric suffix, if it fails
503 # harvest for the rightmost numbers and append the max value
505 data_txt
= bpy
.data
.texts
506 list_txt
= [txt
.name
for txt
in data_txt
if txt
.name
.startswith("All_Shortcuts")]
507 new_name
= "{}_{}{}".format(def_name
, len(list_txt
) + 1, ext
)
509 if new_name
in list_txt
:
510 from re
import findall
511 test_num
= [findall("\d+", words
) for words
in list_txt
]
512 suffix
+= max([int(l
[-1]) for l
in test_num
])
513 new_name
= "{}_{}{}".format(def_name
, suffix
, ext
)
518 def execute(self
, context
):
519 wm
= bpy
.context
.window_manager
520 from collections
import defaultdict
521 mykeys
= defaultdict(list)
522 file_name
= self
.all_shortcuts_name(context
) or "All_Shortcut.txt"
523 start_note
= "# Note: Some of the shortcuts entries don't have a name. Mostly Modal stuff\n"
524 col_width
, col_shortcuts
= 2, 2
526 for ctx_type
, keyboardmap
in wm
.keyconfigs
.user
.keymaps
.items():
527 for myitem
in keyboardmap
.keymap_items
:
528 padding
= len(myitem
.name
)
529 col_width
= padding
+ 2 if padding
> col_width
else col_width
531 short_type
= myitem
.type if myitem
.type else "UNKNOWN"
532 is_ctrl
= " Ctrl" if myitem
.ctrl
is True else ""
533 is_alt
= " Alt" if myitem
.alt
is True else ""
534 is_shift
= " Shift" if myitem
.shift
is True else ""
535 is_oskey
= " OsKey" if myitem
.oskey
is True else ""
536 short_cuts
= "{}{}{}{}{}".format(short_type
, is_ctrl
, is_alt
, is_shift
, is_oskey
)
539 myitem
.name
if myitem
.name
else "No Name",
542 mykeys
[ctx_type
].append(t
)
543 padding_s
= len(short_cuts
) + 2
544 col_shortcuts
= padding_s
if padding_s
> col_shortcuts
else col_shortcuts
546 max_line
= col_shortcuts
+ col_width
+ 4
547 textblock
= bpy
.data
.texts
.new(file_name
)
548 total
= sum([len(mykeys
[ctxs
]) for ctxs
in mykeys
])
549 textblock
.write('# %d Total Shortcuts\n\n' % total
)
550 textblock
.write(start_note
)
553 textblock
.write("\n[%s]\nEntries: %s\n\n" % (ctx
, len(mykeys
[ctx
])))
554 line_k
= sorted(mykeys
[ctx
])
556 add_ticks
= "-" * (max_line
- (len(keys
[0]) + len(keys
[1])))
557 entries
= "{ticks} {entry}".format(ticks
=add_ticks
, entry
=keys
[1])
558 textblock
.write("{name} {entry}\n".format(name
=keys
[0], entry
=entries
))
560 textblock
.write("\n\n")
562 # try to set the created text block to active
563 if context
.area
.type in {"TEXT_EDITOR"}:
564 bpy
.context
.space_data
.text
= bpy
.data
.texts
[file_name
]
566 self
.report({'INFO'}, "See %s textblock" % file_name
)
571 # -----------------------------------------------------
573 # ------------------------------------------------------
578 IsKeyFreeRunExportKeys
,
584 bpy
.utils
.register_class(cls
)
585 bpy
.types
.Scene
.is_keyfree
= PointerProperty(type=IskeyFreeProperties
)
590 bpy
.utils
.unregister_class(cls
)
591 del bpy
.types
.Scene
.is_keyfree