201 lines
5.7 KiB
Python
Executable File
201 lines
5.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# SPDX-FileCopyrightText: 2023 Blender Authors
|
|
#
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
|
|
r"""
|
|
This script exports permutations of key-maps with different settings modified.
|
|
|
|
Useful for checking changes intended for one configuration don't impact others accidentally.
|
|
|
|
./blender.bin -b --factory-startup \
|
|
--python tools/utils/blender_keyconfig_export_permutations.py -- \
|
|
--preset=Blender \
|
|
--output-dir=./output \
|
|
--keymap-prefs=select_mouse:rmb_action
|
|
|
|
/blender.bin -b --factory-startup \
|
|
--python tools/utils/blender_keyconfig_export_permutations.py -- \
|
|
--preset=Blender_27x \
|
|
--output-dir=output \
|
|
--keymap-prefs="select_mouse"
|
|
|
|
The preferences setting: ``select_mouse:rmb_action`` expands into:
|
|
|
|
config = [
|
|
("select_mouse", ('LEFT', 'RIGHT')),
|
|
("rmb_action", ('TWEAK', 'FALLBACK_TOOL')),
|
|
]
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
|
|
|
|
def argparse_create():
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description=__doc__,
|
|
formatter_class=argparse.RawTextHelpFormatter,
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--preset",
|
|
dest="preset",
|
|
default="Blender",
|
|
metavar='PRESET', type=str,
|
|
help="The name of the preset to export",
|
|
required=False,
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--output-dir",
|
|
dest="output_dir",
|
|
default=".",
|
|
metavar='OUTPUT_DIR', type=str,
|
|
help="The directory to output to.",
|
|
required=False,
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--keymap-prefs",
|
|
dest="keymap_prefs",
|
|
default="select_mouse:rmb_action",
|
|
metavar='KEYMAP_PREFS', type=str,
|
|
help=(
|
|
"Colon separated list of attributes to generate key-map configuration permutations. "
|
|
"WARNING: as all combinations are tested, their number increases exponentially!"
|
|
),
|
|
required=False,
|
|
)
|
|
|
|
return parser
|
|
|
|
|
|
def permutations_from_attrs_impl(config, permutation, index):
|
|
index_next = index + 1
|
|
attr, values = config[index]
|
|
for val in values:
|
|
permutation[index] = (attr, val)
|
|
if index_next == len(config):
|
|
yield tuple(permutation)
|
|
else:
|
|
# Keep walking down the list of permutations.
|
|
yield from permutations_from_attrs_impl(config, permutation, index_next)
|
|
# Not necessary, just ensure stale values aren't used.
|
|
permutation[index] = None
|
|
|
|
|
|
def permutations_from_attrs(config):
|
|
"""
|
|
Take a list of attributes and possible values:
|
|
|
|
config = [
|
|
("select_mouse", ('LEFT', 'RIGHT')),
|
|
("rmb_action", ('TWEAK', 'FALLBACK_TOOL')),
|
|
]
|
|
|
|
Yielding all permutations:
|
|
|
|
[("select_mouse", 'LEFT'), ("rmb_action", 'TWEAK')],
|
|
[("select_mouse", 'LEFT'), ("rmb_action", 'FALLBACK_TOOL')],
|
|
... etc ...
|
|
"""
|
|
if not config:
|
|
return ()
|
|
permutation = [None] * len(config)
|
|
result = list(permutations_from_attrs_impl(config, permutation, 0))
|
|
assert permutation == ([None] * len(config))
|
|
return result
|
|
|
|
|
|
def permutation_as_filename(preset, values):
|
|
"""
|
|
Takes a configuration, eg:
|
|
|
|
[("select_mouse", 'LEFT'), ("rmb_action", 'TWEAK')]
|
|
|
|
And returns a filename compatible path:
|
|
"""
|
|
from urllib.parse import quote
|
|
if not values:
|
|
return quote(preset)
|
|
|
|
return quote(
|
|
preset + "_" + ".".join([
|
|
"-".join((str(key), str(val)))
|
|
for key, val in values
|
|
]),
|
|
# Needed so forward slashes aren't included in the resulting name.
|
|
safe="",
|
|
)
|
|
|
|
|
|
def main():
|
|
args = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []
|
|
try:
|
|
import bpy
|
|
except ImportError:
|
|
# Run outside of Blender, just show "--help".
|
|
bpy = None
|
|
args.insert(0, "--help")
|
|
|
|
args = argparse_create().parse_args(args)
|
|
if bpy is None:
|
|
return
|
|
|
|
from bpy import context
|
|
|
|
preset = args.preset
|
|
output_dir = args.output_dir
|
|
|
|
os.makedirs(output_dir, exist_ok=True)
|
|
|
|
# Needed for background mode.
|
|
preset_filepath = bpy.utils.preset_find(preset, preset_path="keyconfig")
|
|
bpy.ops.preferences.keyconfig_activate(filepath=preset_filepath)
|
|
|
|
# Key-map preferences..
|
|
km_prefs = context.window_manager.keyconfigs.active.preferences
|
|
config = []
|
|
# Use RNA introspection:
|
|
if args.keymap_prefs:
|
|
for attr in args.keymap_prefs.split(":"):
|
|
if not hasattr(km_prefs, attr):
|
|
print(f"KeyMap preferences does not have attribute: {attr:s}")
|
|
sys.exit(1)
|
|
|
|
prop_def = km_prefs.rna_type.properties.get(attr)
|
|
match prop_def.type:
|
|
case 'ENUM':
|
|
value = tuple(val.identifier for val in prop_def.enum_items)
|
|
case 'BOOLEAN':
|
|
value = (True, False)
|
|
case _ as prop_def_type:
|
|
raise Exception(f"Unhandled attribute type {prop_def_type:s}")
|
|
config.append((attr, value))
|
|
config = tuple(config)
|
|
|
|
for attr_permutation in (permutations_from_attrs(config) or ((),)):
|
|
|
|
# Reload and set.
|
|
if attr_permutation is not None:
|
|
km_prefs = context.window_manager.keyconfigs.active.preferences
|
|
for attr, value in attr_permutation:
|
|
setattr(km_prefs, attr, value)
|
|
# Re-activate after setting preferences, tsk, ideally this shouldn't be necessary.
|
|
bpy.ops.preferences.keyconfig_activate(filepath=preset_filepath)
|
|
|
|
filepath = os.path.join(output_dir, permutation_as_filename(preset, attr_permutation) + ".py")
|
|
|
|
print("Writing:", filepath)
|
|
bpy.ops.preferences.keyconfig_export(filepath=filepath, all=True)
|
|
|
|
sys.exit()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|