keyconfig updates/changes

- use preset subclass with funcs for updating the keyconfig
- keyconfig filenames are used for the UI names as with presets (so separation anymore)
- keyconfig's are stored in the preset dir (scripts/cfg dir removed)
- only the active keyconfig script is loaded
- some bugfixes for saving keymaps
- user interactions no longer saves keyconfigs too, I think this needs to be re-worked.

developer note...
multiple keyconfigs at once are not really needed now that they are stored & accessed directly in python.
for now I left it alone but we could consider to remove this capability in the future.
This commit is contained in:
Campbell Barton 2010-09-14 16:45:24 +00:00
parent ce98d6ae17
commit 8d0c01e6e2
6 changed files with 150 additions and 76 deletions

@ -182,7 +182,7 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
user_path = user_script_path()
for base_path in script_paths():
for path_subdir in ("", "ui", "op", "io", "cfg", "keyingsets", "modules"):
for path_subdir in ("", "ui", "op", "io", "keyingsets", "modules"):
path = _os.path.join(base_path, path_subdir)
if _os.path.isdir(path):
_sys_path_ensure(path)
@ -202,6 +202,13 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
# deal with addons seperately
addon_reset_all()
# run the active integration preset
filepath = preset_find(_bpy.context.user_preferences.inputs.active_keyconfig, "keyconfig")
if filepath:
keyconfig_set(filepath)
if reload_scripts:
import gc
print("gc.collect() -> %d" % gc.collect())
@ -474,6 +481,9 @@ def addon_reset_all():
addon_disable(mod_name)
def preset_find(name, preset_path, display_name=False):
if not name:
return None
for directory in preset_paths(preset_path):
if display_name:
@ -488,4 +498,38 @@ def preset_find(name, preset_path, display_name=False):
if filename:
filepath = _os.path.join(directory, filename)
if _os.path.exists(filepath):
return filepath\
return filepath
def keyconfig_set(filepath):
from os.path import basename, splitext
print("loading preset:", filepath)
keyconfigs = _bpy.context.window_manager.keyconfigs
kc_orig = keyconfigs.active
keyconfigs_old = keyconfigs[:]
try:
exec(compile(open(filepath).read(), filepath, 'exec'), {"__file__": filepath})
except:
import traceback
traceback.print_exc()
kc_new = [kc for kc in keyconfigs if kc not in keyconfigs_old][0]
kc_new.name = ""
# remove duplicates
name = splitext(basename(filepath))[0]
while True:
kc_dupe = keyconfigs.get(name)
if kc_dupe:
keyconfigs.remove(kc_dupe)
else:
break
kc_new.name = name
keyconfigs.active = kc_new

@ -33,13 +33,18 @@ class AddPresetBase():
name = bpy.props.StringProperty(name="Name", description="Name of the preset, used to make the path name", maxlen=64, default="")
remove_active = bpy.props.BoolProperty(default=False, options={'HIDDEN'})
def _as_filename(self, name): # could reuse for other presets
for char in " !@#$%^&*(){}:\";'[]<>,./?":
@staticmethod
def as_filename(name): # could reuse for other presets
for char in " !@#$%^&*(){}:\";'[]<>,.\\/?":
name = name.replace(char, '_')
return name.lower().strip()
def execute(self, context):
import os
if hasattr(self, "pre_cb"):
self.pre_cb(context)
preset_menu_class = getattr(bpy.types, self.preset_menu)
if not self.remove_active:
@ -47,24 +52,23 @@ class AddPresetBase():
if not self.name:
return {'FINISHED'}
filename = self._as_filename(self.name) + ".py"
filename = self.as_filename(self.name)
target_path = bpy.utils.preset_paths(self.preset_subdir)[0] # we need some way to tell the user and system preset path
filepath = os.path.join(target_path, filename)
if getattr(self, "save_keyconfig", False):
bpy.ops.wm.keyconfig_export(filepath=filepath, kc_name=self.name)
file_preset = open(filepath, 'a')
file_preset.write("wm.keyconfigs.active = kc\n\n")
filepath = os.path.join(target_path, filename) + ".py"
if hasattr(self, "add"):
self.add(context, filepath)
else:
file_preset = open(filepath, 'w')
file_preset.write("import bpy\n")
for rna_path in self.preset_values:
value = eval(rna_path)
file_preset.write("%s = %s\n" % (rna_path, repr(value)))
for rna_path in self.preset_values:
value = eval(rna_path)
file_preset.write("%s = %s\n" % (rna_path, repr(value)))
file_preset.close()
file_preset.close()
preset_menu_class.bl_label = bpy.path.display_name(self.name)
@ -73,20 +77,27 @@ class AddPresetBase():
# fairly sloppy but convenient.
filepath = bpy.utils.preset_find(preset_active, self.preset_subdir)
if not filepath:
filepath = bpy.utils.preset_find(preset_active, self.preset_subdir, display_name=True)
if not filepath:
return {'CANCELLED'}
try:
os.remove(filepath)
except:
import traceback
traceback.print_exc()
if hasattr(self, "remove"):
self.remove(context, filepath)
else:
try:
os.remove(filepath)
except:
import traceback
traceback.print_exc()
# XXX, stupid!
preset_menu_class.bl_label = bpy.path.display_name(self.preset_menu.replace("_MT_", " ").lower())
preset_menu_class.bl_label = "Presets"
if hasattr(self, "post_cb"):
self.post_cb(context)
return {'FINISHED'}
@ -218,7 +229,6 @@ class AddPresetInteraction(AddPresetBase, bpy.types.Operator):
bl_idname = "wm.interaction_preset_add"
bl_label = "Add Interaction Preset"
preset_menu = "USERPREF_MT_interaction_presets"
save_keyconfig = True
preset_values = [
"bpy.context.user_preferences.edit.use_drag_immediately",
@ -236,6 +246,29 @@ class AddPresetInteraction(AddPresetBase, bpy.types.Operator):
preset_subdir = "interaction"
class AddPresetKeyconfig(AddPresetBase, bpy.types.Operator):
'''Add a Keyconfig Preset'''
bl_idname = "wm.keyconfig_preset_add"
bl_label = "Add Keyconfig Preset"
preset_menu = "PREFS_MT_keyconfigs"
preset_subdir = "keyconfig"
def add(self, context, filepath):
bpy.ops.wm.keyconfig_export(filepath=filepath)
bpy.utils.keyconfig_set(filepath)
def pre_cb(self, context):
keyconfigs = bpy.context.window_manager.keyconfigs
if self.remove_active:
preset_menu_class = getattr(bpy.types, self.preset_menu)
preset_menu_class.bl_label = keyconfigs.active.name
def post_cb(self, context):
keyconfigs = bpy.context.window_manager.keyconfigs
if self.remove_active:
keyconfigs.remove(keyconfigs.active)
def register():
pass

@ -768,6 +768,17 @@ class WM_OT_properties_remove(bpy.types.Operator):
return {'FINISHED'}
class WM_OT_keyconfig_activate(bpy.types.Operator):
bl_idname = "wm.keyconfig_activate"
bl_label = "Activate Keyconfig"
filepath = StringProperty(name="File Path", maxlen=1024)
def execute(self, context):
bpy.utils.keyconfig_set(self.filepath)
return {'FINISHED'}
def register():
pass

@ -725,6 +725,7 @@ class USERPREF_PT_input(InputKeyMapPanel):
sub = col.column()
sub.label(text="Presets:")
subrow = sub.row(align=True)
subrow.menu("USERPREF_MT_interaction_presets", text=bpy.types.USERPREF_MT_interaction_presets.bl_label)
subrow.operator("wm.interaction_preset_add", text="", icon='ZOOMIN')
subrow.operator("wm.interaction_preset_add", text="", icon='ZOOMOUT').remove_active = True

@ -124,6 +124,19 @@ def _merge_keymaps(kc1, kc2):
return merged_keymaps
class PREFS_MT_keyconfigs(bpy.types.Menu):
bl_label = "KeyPresets"
preset_subdir = "keyconfig"
preset_operator = "wm.keyconfig_activate"
def draw(self, context):
props = self.layout.operator("wm.context_set_value", text="Blender (default)")
props.data_path = "window_manager.keyconfigs.active"
props.value = "context.window_manager.keyconfigs.default"
# now draw the presets
bpy.types.Menu.draw_preset(self, context)
class InputKeyMapPanel(bpy.types.Panel):
bl_space_type = 'USER_PREFERENCES'
bl_label = "Input"
@ -365,10 +378,18 @@ class InputKeyMapPanel(bpy.types.Panel):
subsplit = sub.split()
subcol = subsplit.column()
row = subcol.row()
row.prop_search(wm.keyconfigs, "active", wm, "keyconfigs", text="Key Config:")
layout.context_pointer_set("keyconfig", wm.keyconfigs.active)
row.operator("wm.keyconfig_remove", text="", icon='X')
row = subcol.row(align=True)
#row.prop_search(wm.keyconfigs, "active", wm, "keyconfigs", text="Key Config:")
text = bpy.path.display_name(context.window_manager.keyconfigs.active.name)
if not text:
text = "Blender (default)"
row.menu("PREFS_MT_keyconfigs", text=text)
row.operator("wm.keyconfig_preset_add", text="", icon="ZOOMIN")
row.operator("wm.keyconfig_preset_add", text="", icon="ZOOMOUT").remove_active = True
# layout.context_pointer_set("keyconfig", wm.keyconfigs.active)
# row.operator("wm.keyconfig_remove", text="", icon='X')
row.prop(context.space_data, "filter_text", icon="VIEWZOOM")
@ -389,8 +410,9 @@ def export_properties(prefix, properties, lines=None):
if lines is None:
lines = []
for pname, value in properties.items():
for pname in properties.keys():
if not properties.is_property_hidden(pname):
value = getattr(properties, pname)
if isinstance(value, bpy.types.OperatorProperties):
export_properties(prefix + "." + pname, value, lines)
elif properties.is_property_set(pname):
@ -501,7 +523,7 @@ def _string_value(value):
if isinstance(value, str) or isinstance(value, bool) or isinstance(value, float) or isinstance(value, int):
result = repr(value)
elif getattr(value, '__len__', False):
repr(list(value))
return repr(list(value))
else:
print("Export key configuration: can't write ", value)
@ -521,6 +543,7 @@ class WM_OT_keyconfig_import(bpy.types.Operator):
keep_original = BoolProperty(name="Keep original", description="Keep original file after copying to configuration folder", default=True)
def execute(self, context):
from os.path import basename
import shutil
if not self.filepath:
raise Exception("Filepath not set")
@ -529,24 +552,16 @@ class WM_OT_keyconfig_import(bpy.types.Operator):
if not f:
raise Exception("Could not open file")
config_name = None
for line in f:
if line.startswith("kc = wm.keyconfigs.new("):
config_name = line[24:-3]
break
config_name = basename(self.filepath)
if config_name is None:
raise Exception("config name not found")
path = os.path.join(__file__, "..", "..", "cfg") # remove ui/space_userpref.py
path = os.path.normpath(path)
path = bpy.utils.preset_paths("keyconfig")[0] # we need some way to tell the user and system preset path
print(path)
# create config folder if needed
if not os.path.exists(path):
os.mkdir(path)
path = os.path.join(path, config_name + ".py")
path = os.path.join(path, config_name)
if self.keep_original:
shutil.copy(self.filepath, path)
@ -554,18 +569,7 @@ class WM_OT_keyconfig_import(bpy.types.Operator):
shutil.move(self.filepath, path)
# sneaky way to check we're actually running the code.
wm = context.window_manager
while config_name in wm.keyconfigs:
wm.keyconfigs.remove(wm.keyconfigs[config_name])
wm = context.window_manager
totmap = len(wm.keyconfigs)
mod = __import__(config_name)
if totmap == len(wm.keyconfigs):
reload(mod)
wm = bpy.context.window_manager
wm.keyconfigs.active = wm.keyconfigs[config_name]
bpy.utils.keyconfig_set(path)
return {'FINISHED'}
@ -586,7 +590,6 @@ class WM_OT_keyconfig_export(bpy.types.Operator):
filter_folder = BoolProperty(name="Filter folders", description="", default=True, options={'HIDDEN'})
filter_text = BoolProperty(name="Filter text", description="", default=True, options={'HIDDEN'})
filter_python = BoolProperty(name="Filter python", description="", default=True, options={'HIDDEN'})
kc_name = StringProperty(name="KeyConfig Name", description="Name to save the key config as")
def execute(self, context):
if not self.filepath:
@ -599,18 +602,10 @@ class WM_OT_keyconfig_export(bpy.types.Operator):
wm = context.window_manager
kc = wm.keyconfigs.active
if self.kc_name != '':
name = self.kc_name
elif kc.name == 'Blender':
name = os.path.splitext(os.path.basename(self.filepath))[0]
else:
name = kc.name
f.write("# Configuration %s\n" % name)
f.write("import bpy\n\n")
f.write("import bpy\n")
f.write("import os\n\n")
f.write("wm = bpy.context.window_manager\n")
f.write("kc = wm.keyconfigs.new('%s')\n\n" % name)
f.write("kc = wm.keyconfigs.new(os.path.splitext(os.path.basename(__file__))[0])\n\n") # keymap must be created by caller
# Generate a list of keymaps to export:
#
@ -775,21 +770,7 @@ class WM_OT_keyconfig_remove(bpy.types.Operator):
def execute(self, context):
import sys
wm = context.window_manager
keyconfig = wm.keyconfigs.active
module = sys.modules.get(keyconfig.name)
if module:
path = module.__file__
if os.path.exists(path):
os.remove(path)
path = module.__file__ + "c" # for .pyc
if os.path.exists(path):
os.remove(path)
wm.keyconfigs.remove(keyconfig)
return {'FINISHED'}

@ -2655,6 +2655,10 @@ static void rna_def_userdef_input(BlenderRNA *brna)
RNA_def_property_collection_sdna(prop, NULL, "keymaps", NULL);
RNA_def_property_struct_type(prop, "KeyMap");
RNA_def_property_ui_text(prop, "Edited Keymaps", "");
prop= RNA_def_property(srna, "active_keyconfig", PROP_STRING, PROP_DIRPATH);
RNA_def_property_string_sdna(prop, NULL, "keyconfigstr");
RNA_def_property_ui_text(prop, "Key Config", "The name of the active key configuration");
}
static void rna_def_userdef_filepaths(BlenderRNA *brna)