tornavis/tools/utils/blender_keyconfig_export_pe...

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()