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)",
25 "blender": (2, 80, 0),
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"
220 # noinspection PyUnusedLocal
221 def draw(self
, context
):
223 scene
= context
.scene
.is_keyfree
225 row
= layout
.row(align
=True)
226 row
.prop(scene
, "data")
227 row
.operator("iskeyfree.action_check", icon
="VIEWZOOM")
229 row
= layout
.row(align
=True)
230 row
.prop(scene
, "use_crtl", toggle
=True)
231 row
.prop(scene
, "use_alt", toggle
=True)
232 row
.prop(scene
, "use_shift", toggle
=True)
233 row
.prop(scene
, "use_oskey", toggle
=True)
236 row
.prop(scene
, "numpad")
238 layout
.operator("iskeyfree.run_export_keys", icon
="FILE_TEXT")
241 mylist
= mychecker
.getlist()
246 cmd
= mychecker
.getlast()
249 row
.label(text
="Current uses of " + str(cmd
), icon
="PARTICLE_DATA")
251 if oldcontext
!= e
[0]:
253 box
.label(text
=e
[0], icon
="UNPINNED")
256 row
= box
.row(align
=True)
259 cmd
= mychecker
.getlast()
262 if mychecker
.isvalidkey(mychecker
.getlastkey()) is False:
263 box
.label(text
=str(mychecker
.getlastkey()) + " looks not valid key", icon
="ERROR")
265 box
.label(text
=str(cmd
) + " is free", icon
="FILE_TICK")
268 # ------------------------------------------------------
269 # Update key (special values) event handler
270 # ------------------------------------------------------
271 # noinspection PyUnusedLocal
272 def update_data(self
, context
):
273 scene
= context
.scene
.is_keyfree
274 if scene
.numpad
!= "NONE":
275 scene
.data
= scene
.numpad
278 class IskeyFreeProperties(PropertyGroup
):
279 data
: StringProperty(
280 name
="Key", maxlen
=32,
281 description
="Shortcut to verify"
283 use_crtl
: BoolProperty(
285 description
="Ctrl key used in shortcut",
288 use_alt
: BoolProperty(
290 description
="Alt key used in shortcut",
293 use_shift
: BoolProperty(
295 description
="Shift key used in shortcut",
298 use_oskey
: BoolProperty(
300 description
="Operating system key used in shortcut",
303 numpad
: EnumProperty(
305 ('NONE', "Select key", ""),
306 ("LEFTMOUSE", "LEFTMOUSE", ""),
307 ("MIDDLEMOUSE", "MIDDLEMOUSE", ""),
308 ("RIGHTMOUSE", "RIGHTMOUSE", ""),
309 ("BUTTON4MOUSE", "BUTTON4MOUSE", ""),
310 ("BUTTON5MOUSE", "BUTTON5MOUSE", ""),
311 ("BUTTON6MOUSE", "BUTTON6MOUSE", ""),
312 ("BUTTON7MOUSE", "BUTTON7MOUSE", ""),
313 ("MOUSEMOVE", "MOUSEMOVE", ""),
314 ("INBETWEEN_MOUSEMOVE", "INBETWEEN_MOUSEMOVE", ""),
315 ("TRACKPADPAN", "TRACKPADPAN", ""),
316 ("TRACKPADZOOM", "TRACKPADZOOM", ""),
317 ("MOUSEROTATE", "MOUSEROTATE", ""),
318 ("WHEELUPMOUSE", "WHEELUPMOUSE", ""),
319 ("WHEELDOWNMOUSE", "WHEELDOWNMOUSE", ""),
320 ("WHEELINMOUSE", "WHEELINMOUSE", ""),
321 ("WHEELOUTMOUSE", "WHEELOUTMOUSE", ""),
322 ("EVT_TWEAK_L", "EVT_TWEAK_L", ""),
323 ("EVT_TWEAK_M", "EVT_TWEAK_M", ""),
324 ("EVT_TWEAK_R", "EVT_TWEAK_R", ""),
351 ("ZERO", "ZERO", ""),
354 ("THREE", "THREE", ""),
355 ("FOUR", "FOUR", ""),
356 ("FIVE", "FIVE", ""),
358 ("SEVEN", "SEVEN", ""),
359 ("EIGHT", "EIGHT", ""),
360 ("NINE", "NINE", ""),
361 ("LEFT_CTRL", "LEFT_CTRL", ""),
362 ("LEFT_ALT", "LEFT_ALT", ""),
363 ("LEFT_SHIFT", "LEFT_SHIFT", ""),
364 ("RIGHT_ALT", "RIGHT_ALT", ""),
365 ("RIGHT_CTRL", "RIGHT_CTRL", ""),
366 ("RIGHT_SHIFT", "RIGHT_SHIFT", ""),
367 ("OSKEY", "OSKEY", ""),
368 ("GRLESS", "GRLESS", ""),
372 ("SPACE", "SPACE", ""),
373 ("LINE_FEED", "LINE_FEED", ""),
374 ("BACK_SPACE", "BACK_SPACE", ""),
376 ("SEMI_COLON", "SEMI_COLON", ""),
377 ("PERIOD", "PERIOD", ""),
378 ("COMMA", "COMMA", ""),
379 ("QUOTE", "QUOTE", ""),
380 ("ACCENT_GRAVE", "ACCENT_GRAVE", ""),
381 ("MINUS", "MINUS", ""),
382 ("SLASH", "SLASH", ""),
383 ("BACK_SLASH", "BACK_SLASH", ""),
384 ("EQUAL", "EQUAL", ""),
385 ("LEFT_BRACKET", "LEFT_BRACKET", ""),
386 ("RIGHT_BRACKET", "RIGHT_BRACKET", ""),
387 ("LEFT_ARROW", "LEFT_ARROW", ""),
388 ("DOWN_ARROW", "DOWN_ARROW", ""),
389 ("RIGHT_ARROW", "RIGHT_ARROW", ""),
390 ("UP_ARROW", "UP_ARROW", ""),
391 ("NUMPAD_1", "NUMPAD_1", ""),
392 ("NUMPAD_2", "NUMPAD_2", ""),
393 ("NUMPAD_3", "NUMPAD_3", ""),
394 ("NUMPAD_4", "NUMPAD_4", ""),
395 ("NUMPAD_5", "NUMPAD_5", ""),
396 ("NUMPAD_6", "NUMPAD_6", ""),
397 ("NUMPAD_7", "NUMPAD_7", ""),
398 ("NUMPAD_8", "NUMPAD_8", ""),
399 ("NUMPAD_9", "NUMPAD_9", ""),
400 ("NUMPAD_0", "NUMPAD_0", ""),
401 ("NUMPAD_PERIOD", "NUMPAD_PERIOD", ""),
402 ("NUMPAD_SLASH", "NUMPAD_SLASH", ""),
403 ("NUMPAD_ASTERIX", "NUMPAD_ASTERIX", ""),
404 ("NUMPAD_MINUS", "NUMPAD_MINUS", ""),
405 ("NUMPAD_ENTER", "NUMPAD_ENTER", ""),
406 ("NUMPAD_PLUS", "NUMPAD_PLUS", ""),
426 ("PAUSE", "PAUSE", ""),
427 ("INSERT", "INSERT", ""),
428 ("HOME", "HOME", ""),
429 ("PAGE_UP", "PAGE_UP", ""),
430 ("PAGE_DOWN", "PAGE_DOWN", ""),
432 ("MEDIA_PLAY", "MEDIA_PLAY", ""),
433 ("MEDIA_STOP", "MEDIA_STOP", ""),
434 ("MEDIA_FIRST", "MEDIA_FIRST", ""),
435 ("MEDIA_LAST", "MEDIA_LAST", ""),
436 ("TEXTINPUT", "TEXTINPUT", ""),
437 ("WINDOW_DEACTIVATE", "WINDOW_DEACTIVATE", ""),
438 ("TIMER", "TIMER", ""),
439 ("TIMER0", "TIMER0", ""),
440 ("TIMER1", "TIMER1", ""),
441 ("TIMER2", "TIMER2", ""),
442 ("TIMER_JOBS", "TIMER_JOBS", ""),
443 ("TIMER_AUTOSAVE", "TIMER_AUTOSAVE", ""),
444 ("TIMER_REPORT", "TIMER_REPORT", ""),
445 ("TIMERREGION", "TIMERREGION", ""),
446 ("NDOF_MOTION", "NDOF_MOTION", ""),
447 ("NDOF_BUTTON_MENU", "NDOF_BUTTON_MENU", ""),
448 ("NDOF_BUTTON_FIT", "NDOF_BUTTON_FIT", ""),
449 ("NDOF_BUTTON_TOP", "NDOF_BUTTON_TOP", ""),
450 ("NDOF_BUTTON_BOTTOM", "NDOF_BUTTON_BOTTOM", ""),
451 ("NDOF_BUTTON_LEFT", "NDOF_BUTTON_LEFT", ""),
452 ("NDOF_BUTTON_RIGHT", "NDOF_BUTTON_RIGHT", ""),
453 ("NDOF_BUTTON_FRONT", "NDOF_BUTTON_FRONT", ""),
454 ("NDOF_BUTTON_BACK", "NDOF_BUTTON_BACK", ""),
455 ("NDOF_BUTTON_ISO1", "NDOF_BUTTON_ISO1", ""),
456 ("NDOF_BUTTON_ISO2", "NDOF_BUTTON_ISO2", ""),
457 ("NDOF_BUTTON_ROLL_CW", "NDOF_BUTTON_ROLL_CW", ""),
458 ("NDOF_BUTTON_ROLL_CCW", "NDOF_BUTTON_ROLL_CCW", ""),
459 ("NDOF_BUTTON_SPIN_CW", "NDOF_BUTTON_SPIN_CW", ""),
460 ("NDOF_BUTTON_SPIN_CCW", "NDOF_BUTTON_SPIN_CCW", ""),
461 ("NDOF_BUTTON_TILT_CW", "NDOF_BUTTON_TILT_CW", ""),
462 ("NDOF_BUTTON_TILT_CCW", "NDOF_BUTTON_TILT_CCW", ""),
463 ("NDOF_BUTTON_ROTATE", "NDOF_BUTTON_ROTATE", ""),
464 ("NDOF_BUTTON_PANZOOM", "NDOF_BUTTON_PANZOOM", ""),
465 ("NDOF_BUTTON_DOMINANT", "NDOF_BUTTON_DOMINANT", ""),
466 ("NDOF_BUTTON_PLUS", "NDOF_BUTTON_PLUS", ""),
467 ("NDOF_BUTTON_MINUS", "NDOF_BUTTON_MINUS", ""),
468 ("NDOF_BUTTON_ESC", "NDOF_BUTTON_ESC", ""),
469 ("NDOF_BUTTON_ALT", "NDOF_BUTTON_ALT", ""),
470 ("NDOF_BUTTON_SHIFT", "NDOF_BUTTON_SHIFT", ""),
471 ("NDOF_BUTTON_CTRL", "NDOF_BUTTON_CTRL", ""),
472 ("NDOF_BUTTON_1", "NDOF_BUTTON_1", ""),
473 ("NDOF_BUTTON_2", "NDOF_BUTTON_2", ""),
474 ("NDOF_BUTTON_3", "NDOF_BUTTON_3", ""),
475 ("NDOF_BUTTON_4", "NDOF_BUTTON_4", ""),
476 ("NDOF_BUTTON_5", "NDOF_BUTTON_5", ""),
477 ("NDOF_BUTTON_6", "NDOF_BUTTON_6", ""),
478 ("NDOF_BUTTON_7", "NDOF_BUTTON_7", ""),
479 ("NDOF_BUTTON_8", "NDOF_BUTTON_8", ""),
480 ("NDOF_BUTTON_9", "NDOF_BUTTON_9", ""),
481 ("NDOF_BUTTON_10", "NDOF_BUTTON_10", ""),
482 ("NDOF_BUTTON_A", "NDOF_BUTTON_A", ""),
483 ("NDOF_BUTTON_B", "NDOF_BUTTON_B", ""),
484 ("NDOF_BUTTON_C", "NDOF_BUTTON_C", "")
487 description
="Enter key code in find text",
492 class IsKeyFreeRunExportKeys(Operator
):
493 bl_idname
= "iskeyfree.run_export_keys"
494 bl_label
= "List all Shortcuts"
495 bl_description
= ("List all existing shortcuts in a text block\n"
496 "The newly generated list will be made active in the Text Editor\n"
497 "To access the previous ones, select them from the Header dropdown")
499 def all_shortcuts_name(self
, context
):
500 new_name
, def_name
, ext
= "", "All_Shortcuts", ".txt"
503 # first slap a simple linear count + 1 for numeric suffix, if it fails
504 # harvest for the rightmost numbers and append the max value
506 data_txt
= bpy
.data
.texts
507 list_txt
= [txt
.name
for txt
in data_txt
if txt
.name
.startswith("All_Shortcuts")]
508 new_name
= "{}_{}{}".format(def_name
, len(list_txt
) + 1, ext
)
510 if new_name
in list_txt
:
511 from re
import findall
512 test_num
= [findall("\d+", words
) for words
in list_txt
]
513 suffix
+= max([int(l
[-1]) for l
in test_num
])
514 new_name
= "{}_{}{}".format(def_name
, suffix
, ext
)
519 def execute(self
, context
):
520 wm
= bpy
.context
.window_manager
521 from collections
import defaultdict
522 mykeys
= defaultdict(list)
523 file_name
= self
.all_shortcuts_name(context
) or "All_Shortcut.txt"
524 start_note
= "# Note: Some of the shortcuts entries don't have a name. Mostly Modal stuff\n"
525 col_width
, col_shortcuts
= 2, 2
527 for ctx_type
, keyboardmap
in wm
.keyconfigs
.user
.keymaps
.items():
528 for myitem
in keyboardmap
.keymap_items
:
529 padding
= len(myitem
.name
)
530 col_width
= padding
+ 2 if padding
> col_width
else col_width
532 short_type
= myitem
.type if myitem
.type else "UNKNOWN"
533 is_ctrl
= " Ctrl" if myitem
.ctrl
is True else ""
534 is_alt
= " Alt" if myitem
.alt
is True else ""
535 is_shift
= " Shift" if myitem
.shift
is True else ""
536 is_oskey
= " OsKey" if myitem
.oskey
is True else ""
537 short_cuts
= "{}{}{}{}{}".format(short_type
, is_ctrl
, is_alt
, is_shift
, is_oskey
)
540 myitem
.name
if myitem
.name
else "No Name",
543 mykeys
[ctx_type
].append(t
)
544 padding_s
= len(short_cuts
) + 2
545 col_shortcuts
= padding_s
if padding_s
> col_shortcuts
else col_shortcuts
547 max_line
= col_shortcuts
+ col_width
+ 4
548 textblock
= bpy
.data
.texts
.new(file_name
)
549 total
= sum([len(mykeys
[ctxs
]) for ctxs
in mykeys
])
550 textblock
.write('# %d Total Shortcuts\n\n' % total
)
551 textblock
.write(start_note
)
554 textblock
.write("\n[%s]\nEntries: %s\n\n" % (ctx
, len(mykeys
[ctx
])))
555 line_k
= sorted(mykeys
[ctx
])
557 add_ticks
= "-" * (max_line
- (len(keys
[0]) + len(keys
[1])))
558 entries
= "{ticks} {entry}".format(ticks
=add_ticks
, entry
=keys
[1])
559 textblock
.write("{name} {entry}\n".format(name
=keys
[0], entry
=entries
))
561 textblock
.write("\n\n")
563 # try to set the created text block to active
564 if context
.area
.type in {"TEXT_EDITOR"}:
565 bpy
.context
.space_data
.text
= bpy
.data
.texts
[file_name
]
567 self
.report({'INFO'}, "See %s textblock" % file_name
)
572 # -----------------------------------------------------
574 # ------------------------------------------------------
579 IsKeyFreeRunExportKeys
,
585 bpy
.utils
.register_class(cls
)
586 bpy
.types
.Scene
.is_keyfree
= PointerProperty(type=IskeyFreeProperties
)
591 bpy
.utils
.unregister_class(cls
)
592 del bpy
.types
.Scene
.is_keyfree