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 "ACTIONMOUSE", "SELECTMOUSE", "MOUSEMOVE", "INBETWEEN_MOUSEMOVE", "TRACKPADPAN", "TRACKPADZOOM",
150 "MOUSEROTATE", "WHEELUPMOUSE", "WHEELDOWNMOUSE", "WHEELINMOUSE", "WHEELOUTMOUSE", "EVT_TWEAK_L",
151 "EVT_TWEAK_M", "EVT_TWEAK_R", "EVT_TWEAK_A", "EVT_TWEAK_S", "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 ("ACTIONMOUSE", "ACTIONMOUSE", ""),
313 ("SELECTMOUSE", "SELECTMOUSE", ""),
314 ("MOUSEMOVE", "MOUSEMOVE", ""),
315 ("INBETWEEN_MOUSEMOVE", "INBETWEEN_MOUSEMOVE", ""),
316 ("TRACKPADPAN", "TRACKPADPAN", ""),
317 ("TRACKPADZOOM", "TRACKPADZOOM", ""),
318 ("MOUSEROTATE", "MOUSEROTATE", ""),
319 ("WHEELUPMOUSE", "WHEELUPMOUSE", ""),
320 ("WHEELDOWNMOUSE", "WHEELDOWNMOUSE", ""),
321 ("WHEELINMOUSE", "WHEELINMOUSE", ""),
322 ("WHEELOUTMOUSE", "WHEELOUTMOUSE", ""),
323 ("EVT_TWEAK_L", "EVT_TWEAK_L", ""),
324 ("EVT_TWEAK_M", "EVT_TWEAK_M", ""),
325 ("EVT_TWEAK_R", "EVT_TWEAK_R", ""),
326 ("EVT_TWEAK_A", "EVT_TWEAK_A", ""),
327 ("EVT_TWEAK_S", "EVT_TWEAK_S", ""),
354 ("ZERO", "ZERO", ""),
357 ("THREE", "THREE", ""),
358 ("FOUR", "FOUR", ""),
359 ("FIVE", "FIVE", ""),
361 ("SEVEN", "SEVEN", ""),
362 ("EIGHT", "EIGHT", ""),
363 ("NINE", "NINE", ""),
364 ("LEFT_CTRL", "LEFT_CTRL", ""),
365 ("LEFT_ALT", "LEFT_ALT", ""),
366 ("LEFT_SHIFT", "LEFT_SHIFT", ""),
367 ("RIGHT_ALT", "RIGHT_ALT", ""),
368 ("RIGHT_CTRL", "RIGHT_CTRL", ""),
369 ("RIGHT_SHIFT", "RIGHT_SHIFT", ""),
370 ("OSKEY", "OSKEY", ""),
371 ("GRLESS", "GRLESS", ""),
375 ("SPACE", "SPACE", ""),
376 ("LINE_FEED", "LINE_FEED", ""),
377 ("BACK_SPACE", "BACK_SPACE", ""),
379 ("SEMI_COLON", "SEMI_COLON", ""),
380 ("PERIOD", "PERIOD", ""),
381 ("COMMA", "COMMA", ""),
382 ("QUOTE", "QUOTE", ""),
383 ("ACCENT_GRAVE", "ACCENT_GRAVE", ""),
384 ("MINUS", "MINUS", ""),
385 ("SLASH", "SLASH", ""),
386 ("BACK_SLASH", "BACK_SLASH", ""),
387 ("EQUAL", "EQUAL", ""),
388 ("LEFT_BRACKET", "LEFT_BRACKET", ""),
389 ("RIGHT_BRACKET", "RIGHT_BRACKET", ""),
390 ("LEFT_ARROW", "LEFT_ARROW", ""),
391 ("DOWN_ARROW", "DOWN_ARROW", ""),
392 ("RIGHT_ARROW", "RIGHT_ARROW", ""),
393 ("UP_ARROW", "UP_ARROW", ""),
394 ("NUMPAD_1", "NUMPAD_1", ""),
395 ("NUMPAD_2", "NUMPAD_2", ""),
396 ("NUMPAD_3", "NUMPAD_3", ""),
397 ("NUMPAD_4", "NUMPAD_4", ""),
398 ("NUMPAD_5", "NUMPAD_5", ""),
399 ("NUMPAD_6", "NUMPAD_6", ""),
400 ("NUMPAD_7", "NUMPAD_7", ""),
401 ("NUMPAD_8", "NUMPAD_8", ""),
402 ("NUMPAD_9", "NUMPAD_9", ""),
403 ("NUMPAD_0", "NUMPAD_0", ""),
404 ("NUMPAD_PERIOD", "NUMPAD_PERIOD", ""),
405 ("NUMPAD_SLASH", "NUMPAD_SLASH", ""),
406 ("NUMPAD_ASTERIX", "NUMPAD_ASTERIX", ""),
407 ("NUMPAD_MINUS", "NUMPAD_MINUS", ""),
408 ("NUMPAD_ENTER", "NUMPAD_ENTER", ""),
409 ("NUMPAD_PLUS", "NUMPAD_PLUS", ""),
429 ("PAUSE", "PAUSE", ""),
430 ("INSERT", "INSERT", ""),
431 ("HOME", "HOME", ""),
432 ("PAGE_UP", "PAGE_UP", ""),
433 ("PAGE_DOWN", "PAGE_DOWN", ""),
435 ("MEDIA_PLAY", "MEDIA_PLAY", ""),
436 ("MEDIA_STOP", "MEDIA_STOP", ""),
437 ("MEDIA_FIRST", "MEDIA_FIRST", ""),
438 ("MEDIA_LAST", "MEDIA_LAST", ""),
439 ("TEXTINPUT", "TEXTINPUT", ""),
440 ("WINDOW_DEACTIVATE", "WINDOW_DEACTIVATE", ""),
441 ("TIMER", "TIMER", ""),
442 ("TIMER0", "TIMER0", ""),
443 ("TIMER1", "TIMER1", ""),
444 ("TIMER2", "TIMER2", ""),
445 ("TIMER_JOBS", "TIMER_JOBS", ""),
446 ("TIMER_AUTOSAVE", "TIMER_AUTOSAVE", ""),
447 ("TIMER_REPORT", "TIMER_REPORT", ""),
448 ("TIMERREGION", "TIMERREGION", ""),
449 ("NDOF_MOTION", "NDOF_MOTION", ""),
450 ("NDOF_BUTTON_MENU", "NDOF_BUTTON_MENU", ""),
451 ("NDOF_BUTTON_FIT", "NDOF_BUTTON_FIT", ""),
452 ("NDOF_BUTTON_TOP", "NDOF_BUTTON_TOP", ""),
453 ("NDOF_BUTTON_BOTTOM", "NDOF_BUTTON_BOTTOM", ""),
454 ("NDOF_BUTTON_LEFT", "NDOF_BUTTON_LEFT", ""),
455 ("NDOF_BUTTON_RIGHT", "NDOF_BUTTON_RIGHT", ""),
456 ("NDOF_BUTTON_FRONT", "NDOF_BUTTON_FRONT", ""),
457 ("NDOF_BUTTON_BACK", "NDOF_BUTTON_BACK", ""),
458 ("NDOF_BUTTON_ISO1", "NDOF_BUTTON_ISO1", ""),
459 ("NDOF_BUTTON_ISO2", "NDOF_BUTTON_ISO2", ""),
460 ("NDOF_BUTTON_ROLL_CW", "NDOF_BUTTON_ROLL_CW", ""),
461 ("NDOF_BUTTON_ROLL_CCW", "NDOF_BUTTON_ROLL_CCW", ""),
462 ("NDOF_BUTTON_SPIN_CW", "NDOF_BUTTON_SPIN_CW", ""),
463 ("NDOF_BUTTON_SPIN_CCW", "NDOF_BUTTON_SPIN_CCW", ""),
464 ("NDOF_BUTTON_TILT_CW", "NDOF_BUTTON_TILT_CW", ""),
465 ("NDOF_BUTTON_TILT_CCW", "NDOF_BUTTON_TILT_CCW", ""),
466 ("NDOF_BUTTON_ROTATE", "NDOF_BUTTON_ROTATE", ""),
467 ("NDOF_BUTTON_PANZOOM", "NDOF_BUTTON_PANZOOM", ""),
468 ("NDOF_BUTTON_DOMINANT", "NDOF_BUTTON_DOMINANT", ""),
469 ("NDOF_BUTTON_PLUS", "NDOF_BUTTON_PLUS", ""),
470 ("NDOF_BUTTON_MINUS", "NDOF_BUTTON_MINUS", ""),
471 ("NDOF_BUTTON_ESC", "NDOF_BUTTON_ESC", ""),
472 ("NDOF_BUTTON_ALT", "NDOF_BUTTON_ALT", ""),
473 ("NDOF_BUTTON_SHIFT", "NDOF_BUTTON_SHIFT", ""),
474 ("NDOF_BUTTON_CTRL", "NDOF_BUTTON_CTRL", ""),
475 ("NDOF_BUTTON_1", "NDOF_BUTTON_1", ""),
476 ("NDOF_BUTTON_2", "NDOF_BUTTON_2", ""),
477 ("NDOF_BUTTON_3", "NDOF_BUTTON_3", ""),
478 ("NDOF_BUTTON_4", "NDOF_BUTTON_4", ""),
479 ("NDOF_BUTTON_5", "NDOF_BUTTON_5", ""),
480 ("NDOF_BUTTON_6", "NDOF_BUTTON_6", ""),
481 ("NDOF_BUTTON_7", "NDOF_BUTTON_7", ""),
482 ("NDOF_BUTTON_8", "NDOF_BUTTON_8", ""),
483 ("NDOF_BUTTON_9", "NDOF_BUTTON_9", ""),
484 ("NDOF_BUTTON_10", "NDOF_BUTTON_10", ""),
485 ("NDOF_BUTTON_A", "NDOF_BUTTON_A", ""),
486 ("NDOF_BUTTON_B", "NDOF_BUTTON_B", ""),
487 ("NDOF_BUTTON_C", "NDOF_BUTTON_C", "")
490 description
="Enter key code in find text",
495 class IsKeyFreeRunExportKeys(Operator
):
496 bl_idname
= "iskeyfree.run_export_keys"
497 bl_label
= "List all Shortcuts"
498 bl_description
= ("List all existing shortcuts in a text block\n"
499 "The newly generated list will be made active in the Text Editor\n"
500 "To access the previous ones, select them from the Header dropdown")
502 def all_shortcuts_name(self
, context
):
503 new_name
, def_name
, ext
= "", "All_Shortcuts", ".txt"
506 # first slap a simple linear count + 1 for numeric suffix, if it fails
507 # harvest for the rightmost numbers and append the max value
509 data_txt
= bpy
.data
.texts
510 list_txt
= [txt
.name
for txt
in data_txt
if txt
.name
.startswith("All_Shortcuts")]
511 new_name
= "{}_{}{}".format(def_name
, len(list_txt
) + 1, ext
)
513 if new_name
in list_txt
:
514 from re
import findall
515 test_num
= [findall("\d+", words
) for words
in list_txt
]
516 suffix
+= max([int(l
[-1]) for l
in test_num
])
517 new_name
= "{}_{}{}".format(def_name
, suffix
, ext
)
522 def execute(self
, context
):
523 wm
= bpy
.context
.window_manager
524 from collections
import defaultdict
525 mykeys
= defaultdict(list)
526 file_name
= self
.all_shortcuts_name(context
) or "All_Shortcut.txt"
527 start_note
= "# Note: Some of the shortcuts entries don't have a name. Mostly Modal stuff\n"
528 col_width
, col_shortcuts
= 2, 2
530 for ctx_type
, keyboardmap
in wm
.keyconfigs
.user
.keymaps
.items():
531 for myitem
in keyboardmap
.keymap_items
:
532 padding
= len(myitem
.name
)
533 col_width
= padding
+ 2 if padding
> col_width
else col_width
535 short_type
= myitem
.type if myitem
.type else "UNKNOWN"
536 is_ctrl
= " Ctrl" if myitem
.ctrl
is True else ""
537 is_alt
= " Alt" if myitem
.alt
is True else ""
538 is_shift
= " Shift" if myitem
.shift
is True else ""
539 is_oskey
= " OsKey" if myitem
.oskey
is True else ""
540 short_cuts
= "{}{}{}{}{}".format(short_type
, is_ctrl
, is_alt
, is_shift
, is_oskey
)
543 myitem
.name
if myitem
.name
else "No Name",
546 mykeys
[ctx_type
].append(t
)
547 padding_s
= len(short_cuts
) + 2
548 col_shortcuts
= padding_s
if padding_s
> col_shortcuts
else col_shortcuts
550 max_line
= col_shortcuts
+ col_width
+ 4
551 textblock
= bpy
.data
.texts
.new(file_name
)
552 total
= sum([len(mykeys
[ctxs
]) for ctxs
in mykeys
])
553 textblock
.write('# %d Total Shortcuts\n\n' % total
)
554 textblock
.write(start_note
)
557 textblock
.write("\n[%s]\nEntries: %s\n\n" % (ctx
, len(mykeys
[ctx
])))
558 line_k
= sorted(mykeys
[ctx
])
560 add_ticks
= "-" * (max_line
- (len(keys
[0]) + len(keys
[1])))
561 entries
= "{ticks} {entry}".format(ticks
=add_ticks
, entry
=keys
[1])
562 textblock
.write("{name} {entry}\n".format(name
=keys
[0], entry
=entries
))
564 textblock
.write("\n\n")
566 # try to set the created text block to active
567 if context
.area
.type in {"TEXT_EDITOR"}:
568 bpy
.context
.space_data
.text
= bpy
.data
.texts
[file_name
]
570 self
.report({'INFO'}, "See %s textblock" % file_name
)
575 # -----------------------------------------------------
577 # ------------------------------------------------------
582 IsKeyFreeRunExportKeys
,
588 bpy
.utils
.register_class(cls
)
589 bpy
.types
.Scene
.is_keyfree
= PointerProperty(type=IskeyFreeProperties
)
594 bpy
.utils
.unregister_class(cls
)
595 del bpy
.types
.Scene
.is_keyfree