Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2012-01-04 08:22:16 +0400
committerCampbell Barton <ideasman42@gmail.com>2012-01-04 08:22:16 +0400
commit279933b83f0eeb77a1a32c8ddd6d58b4e36c8342 (patch)
tree76b7afe0903ffa1fe34a408222d112e8446f8f58
parent07cfcae0f19f7d10551e535f55ce1632985c4b33 (diff)
tabs -> spaces, no functional changes
-rw-r--r--modules/extensions_framework/__init__.py654
-rw-r--r--modules/extensions_framework/ui.py604
-rw-r--r--modules/extensions_framework/util.py398
-rw-r--r--modules/extensions_framework/validate.py286
4 files changed, 971 insertions, 971 deletions
diff --git a/modules/extensions_framework/__init__.py b/modules/extensions_framework/__init__.py
index df815eb3..af909b45 100644
--- a/modules/extensions_framework/__init__.py
+++ b/modules/extensions_framework/__init__.py
@@ -33,339 +33,339 @@ bpy.utils.register_class(EF_OT_msg)
del EF_OT_msg
def log(str, popup=False, module_name='EF'):
- """Print a message to the console, prefixed with the module_name
- and the current time. If the popup flag is True, the message will
- be raised in the UI as a warning using the operator bpy.ops.ef.msg.
-
- """
- print("[%s %s] %s" %
- (module_name, time.strftime('%Y-%b-%d %H:%M:%S'), str))
- if popup:
- bpy.ops.ef.msg(
- msg_type='WARNING',
- msg_text=str
- )
+ """Print a message to the console, prefixed with the module_name
+ and the current time. If the popup flag is True, the message will
+ be raised in the UI as a warning using the operator bpy.ops.ef.msg.
+
+ """
+ print("[%s %s] %s" %
+ (module_name, time.strftime('%Y-%b-%d %H:%M:%S'), str))
+ if popup:
+ bpy.ops.ef.msg(
+ msg_type='WARNING',
+ msg_text=str
+ )
added_property_cache = {}
def init_properties(obj, props, cache=True):
- """Initialise custom properties in the given object or type.
- The props list is described in the declarative_property_group
- class definition. If the cache flag is False, this function
- will attempt to redefine properties even if they have already been
- added.
-
- """
-
- if not obj in added_property_cache.keys():
- added_property_cache[obj] = []
-
- for prop in props:
- try:
- if cache and prop['attr'] in added_property_cache[obj]:
- continue
-
- if prop['type'] == 'bool':
- t = bpy.props.BoolProperty
- a = {k: v for k,v in prop.items() if k in ["name",
- "description","default","options","subtype","update"]}
- elif prop['type'] == 'bool_vector':
- t = bpy.props.BoolVectorProperty
- a = {k: v for k,v in prop.items() if k in ["name",
- "description","default","options","subtype","size",
- "update"]}
- elif prop['type'] == 'collection':
- t = bpy.props.CollectionProperty
- a = {k: v for k,v in prop.items() if k in ["ptype","name",
- "description","default","options"]}
- a['type'] = a['ptype']
- del a['ptype']
- elif prop['type'] == 'enum':
- t = bpy.props.EnumProperty
- a = {k: v for k,v in prop.items() if k in ["items","name",
- "description","default","options","update"]}
- elif prop['type'] == 'float':
- t = bpy.props.FloatProperty
- a = {k: v for k,v in prop.items() if k in ["name",
- "description","default","min","max","soft_min","soft_max",
- "step","precision","options","subtype","unit","update"]}
- elif prop['type'] == 'float_vector':
- t = bpy.props.FloatVectorProperty
- a = {k: v for k,v in prop.items() if k in ["name",
- "description","default","min","max","soft_min","soft_max",
- "step","precision","options","subtype","size","update"]}
- elif prop['type'] == 'int':
- t = bpy.props.IntProperty
- a = {k: v for k,v in prop.items() if k in ["name",
- "description","default","min","max","soft_min","soft_max",
- "step","options","subtype","update"]}
- elif prop['type'] == 'int_vector':
- t = bpy.props.IntVectorProperty
- a = {k: v for k,v in prop.items() if k in ["name",
- "description","default","min","max","soft_min","soft_max",
- "options","subtype","size","update"]}
- elif prop['type'] == 'pointer':
- t = bpy.props.PointerProperty
- a = {k: v for k,v in prop.items() if k in ["ptype", "name",
- "description","options","update"]}
- a['type'] = a['ptype']
- del a['ptype']
- elif prop['type'] == 'string':
- t = bpy.props.StringProperty
- a = {k: v for k,v in prop.items() if k in ["name",
- "description","default","maxlen","options","subtype",
- "update"]}
- else:
- continue
-
- setattr(obj, prop['attr'], t(**a))
-
- added_property_cache[obj].append(prop['attr'])
- except KeyError:
- # Silently skip invalid entries in props
- continue
+ """Initialise custom properties in the given object or type.
+ The props list is described in the declarative_property_group
+ class definition. If the cache flag is False, this function
+ will attempt to redefine properties even if they have already been
+ added.
+
+ """
+
+ if not obj in added_property_cache.keys():
+ added_property_cache[obj] = []
+
+ for prop in props:
+ try:
+ if cache and prop['attr'] in added_property_cache[obj]:
+ continue
+
+ if prop['type'] == 'bool':
+ t = bpy.props.BoolProperty
+ a = {k: v for k,v in prop.items() if k in ["name",
+ "description","default","options","subtype","update"]}
+ elif prop['type'] == 'bool_vector':
+ t = bpy.props.BoolVectorProperty
+ a = {k: v for k,v in prop.items() if k in ["name",
+ "description","default","options","subtype","size",
+ "update"]}
+ elif prop['type'] == 'collection':
+ t = bpy.props.CollectionProperty
+ a = {k: v for k,v in prop.items() if k in ["ptype","name",
+ "description","default","options"]}
+ a['type'] = a['ptype']
+ del a['ptype']
+ elif prop['type'] == 'enum':
+ t = bpy.props.EnumProperty
+ a = {k: v for k,v in prop.items() if k in ["items","name",
+ "description","default","options","update"]}
+ elif prop['type'] == 'float':
+ t = bpy.props.FloatProperty
+ a = {k: v for k,v in prop.items() if k in ["name",
+ "description","default","min","max","soft_min","soft_max",
+ "step","precision","options","subtype","unit","update"]}
+ elif prop['type'] == 'float_vector':
+ t = bpy.props.FloatVectorProperty
+ a = {k: v for k,v in prop.items() if k in ["name",
+ "description","default","min","max","soft_min","soft_max",
+ "step","precision","options","subtype","size","update"]}
+ elif prop['type'] == 'int':
+ t = bpy.props.IntProperty
+ a = {k: v for k,v in prop.items() if k in ["name",
+ "description","default","min","max","soft_min","soft_max",
+ "step","options","subtype","update"]}
+ elif prop['type'] == 'int_vector':
+ t = bpy.props.IntVectorProperty
+ a = {k: v for k,v in prop.items() if k in ["name",
+ "description","default","min","max","soft_min","soft_max",
+ "options","subtype","size","update"]}
+ elif prop['type'] == 'pointer':
+ t = bpy.props.PointerProperty
+ a = {k: v for k,v in prop.items() if k in ["ptype", "name",
+ "description","options","update"]}
+ a['type'] = a['ptype']
+ del a['ptype']
+ elif prop['type'] == 'string':
+ t = bpy.props.StringProperty
+ a = {k: v for k,v in prop.items() if k in ["name",
+ "description","default","maxlen","options","subtype",
+ "update"]}
+ else:
+ continue
+
+ setattr(obj, prop['attr'], t(**a))
+
+ added_property_cache[obj].append(prop['attr'])
+ except KeyError:
+ # Silently skip invalid entries in props
+ continue
class declarative_property_group(bpy.types.PropertyGroup):
- """A declarative_property_group describes a set of logically
- related properties, using a declarative style to list each
- property type, name, values, and other relevant information.
- The information provided for each property depends on the
- property's type.
-
- The properties list attribute in this class describes the
- properties present in this group.
-
- Some additional information about the properties in this group
- can be specified, so that a UI can be generated to display them.
- To that end, the controls list attribute and the visibility dict
- attribute are present here, to be read and interpreted by a
- property_group_renderer object.
- See extensions_framework.ui.property_group_renderer.
-
- """
-
- ef_initialised = False
-
- """This property tells extensions_framework which bpy.type(s)
- to attach this PropertyGroup to. If left as an empty list,
- it will not be attached to any type, but its properties will
- still be initialised. The type(s) given in the list should be
- a string, such as 'Scene'.
-
- """
- ef_attach_to = []
-
- @classmethod
- def initialise_properties(cls):
- """This is a function that should be called on
- sub-classes of declarative_property_group in order
- to ensure that they are initialised when the addon
- is loaded.
- the init_properties is called without caching here,
- as it is assumed that any addon calling this function
- will also call ef_remove_properties when it is
- unregistered.
-
- """
-
- if not cls.ef_initialised:
- for property_group_parent in cls.ef_attach_to:
- if property_group_parent is not None:
- prototype = getattr(bpy.types, property_group_parent)
- if not hasattr(prototype, cls.__name__):
- init_properties(prototype, [{
- 'type': 'pointer',
- 'attr': cls.__name__,
- 'ptype': cls,
- 'name': cls.__name__,
- 'description': cls.__name__
- }], cache=False)
-
- init_properties(cls, cls.properties, cache=False)
- cls.ef_initialised = True
-
- return cls
-
- @classmethod
- def register_initialise_properties(cls):
- """As ef_initialise_properties, but also registers the
- class with RNA. Note that this isn't a great idea
- because it's non-trivial to unregister the class, unless
- you keep track of it yourself.
- """
-
- bpy.utils.register_class(cls)
- cls.initialise_properties()
- return cls
-
- @classmethod
- def remove_properties(cls):
- """This is a function that should be called on
- sub-classes of declarative_property_group in order
- to ensure that they are un-initialised when the addon
- is unloaded.
-
- """
-
- if cls.ef_initialised:
- prototype = getattr(bpy.types, cls.__name__)
- for prop in cls.properties:
- if hasattr(prototype, prop['attr']):
- delattr(prototype, prop['attr'])
-
- for property_group_parent in cls.ef_attach_to:
- if property_group_parent is not None:
- prototype = getattr(bpy.types, property_group_parent)
- if hasattr(prototype, cls.__name__):
- delattr(prototype, cls.__name__)
-
- cls.ef_initialised = False
-
- return cls
-
-
- """This list controls the order of property layout when rendered
- by a property_group_renderer. This can be a nested list, where each
- list becomes a row in the panel layout. Nesting may be to any depth.
-
- """
- controls = []
-
- """The visibility dict controls the visibility of properties based on
- the value of other properties. See extensions_framework.validate
- for test syntax.
-
- """
- visibility = {}
-
- """The enabled dict controls the enabled state of properties based on
- the value of other properties. See extensions_framework.validate
- for test syntax.
-
- """
- enabled = {}
-
- """The alert dict controls the alert state of properties based on
- the value of other properties. See extensions_framework.validate
- for test syntax.
-
- """
- alert = {}
-
- """The properties list describes each property to be created. Each
- item should be a dict of args to pass to a
- bpy.props.<?>Property function, with the exception of 'type'
- which is used and stripped by extensions_framework in order to
- determine which Property creation function to call.
-
- Example item:
- {
- 'type': 'int', # bpy.props.IntProperty
- 'attr': 'threads', # bpy.types.<type>.threads
- 'name': 'Render Threads', # Rendered next to the UI
- 'description': 'Number of threads to use', # Tooltip text in the UI
- 'default': 1,
- 'min': 1,
- 'soft_min': 1,
- 'max': 64,
- 'soft_max': 64
- }
-
- """
- properties = []
-
- def draw_callback(self, context):
- """Sub-classes can override this to get a callback when
- rendering is completed by a property_group_renderer sub-class.
-
- """
-
- pass
-
- @classmethod
- def get_exportable_properties(cls):
- """Return a list of properties which have the 'save_in_preset' key
- set to True, and hence should be saved into preset files.
-
- """
-
- out = []
- for prop in cls.properties:
- if 'save_in_preset' in prop.keys() and prop['save_in_preset']:
- out.append(prop)
- return out
-
- def reset(self):
- """Reset all properties in this group to the default value,
- if specified"""
- for prop in self.properties:
- pk = prop.keys()
- if 'attr' in pk and 'default' in pk and hasattr(self, prop['attr']):
- setattr(self, prop['attr'], prop['default'])
+ """A declarative_property_group describes a set of logically
+ related properties, using a declarative style to list each
+ property type, name, values, and other relevant information.
+ The information provided for each property depends on the
+ property's type.
+
+ The properties list attribute in this class describes the
+ properties present in this group.
+
+ Some additional information about the properties in this group
+ can be specified, so that a UI can be generated to display them.
+ To that end, the controls list attribute and the visibility dict
+ attribute are present here, to be read and interpreted by a
+ property_group_renderer object.
+ See extensions_framework.ui.property_group_renderer.
+
+ """
+
+ ef_initialised = False
+
+ """This property tells extensions_framework which bpy.type(s)
+ to attach this PropertyGroup to. If left as an empty list,
+ it will not be attached to any type, but its properties will
+ still be initialised. The type(s) given in the list should be
+ a string, such as 'Scene'.
+
+ """
+ ef_attach_to = []
+
+ @classmethod
+ def initialise_properties(cls):
+ """This is a function that should be called on
+ sub-classes of declarative_property_group in order
+ to ensure that they are initialised when the addon
+ is loaded.
+ the init_properties is called without caching here,
+ as it is assumed that any addon calling this function
+ will also call ef_remove_properties when it is
+ unregistered.
+
+ """
+
+ if not cls.ef_initialised:
+ for property_group_parent in cls.ef_attach_to:
+ if property_group_parent is not None:
+ prototype = getattr(bpy.types, property_group_parent)
+ if not hasattr(prototype, cls.__name__):
+ init_properties(prototype, [{
+ 'type': 'pointer',
+ 'attr': cls.__name__,
+ 'ptype': cls,
+ 'name': cls.__name__,
+ 'description': cls.__name__
+ }], cache=False)
+
+ init_properties(cls, cls.properties, cache=False)
+ cls.ef_initialised = True
+
+ return cls
+
+ @classmethod
+ def register_initialise_properties(cls):
+ """As ef_initialise_properties, but also registers the
+ class with RNA. Note that this isn't a great idea
+ because it's non-trivial to unregister the class, unless
+ you keep track of it yourself.
+ """
+
+ bpy.utils.register_class(cls)
+ cls.initialise_properties()
+ return cls
+
+ @classmethod
+ def remove_properties(cls):
+ """This is a function that should be called on
+ sub-classes of declarative_property_group in order
+ to ensure that they are un-initialised when the addon
+ is unloaded.
+
+ """
+
+ if cls.ef_initialised:
+ prototype = getattr(bpy.types, cls.__name__)
+ for prop in cls.properties:
+ if hasattr(prototype, prop['attr']):
+ delattr(prototype, prop['attr'])
+
+ for property_group_parent in cls.ef_attach_to:
+ if property_group_parent is not None:
+ prototype = getattr(bpy.types, property_group_parent)
+ if hasattr(prototype, cls.__name__):
+ delattr(prototype, cls.__name__)
+
+ cls.ef_initialised = False
+
+ return cls
+
+
+ """This list controls the order of property layout when rendered
+ by a property_group_renderer. This can be a nested list, where each
+ list becomes a row in the panel layout. Nesting may be to any depth.
+
+ """
+ controls = []
+
+ """The visibility dict controls the visibility of properties based on
+ the value of other properties. See extensions_framework.validate
+ for test syntax.
+
+ """
+ visibility = {}
+
+ """The enabled dict controls the enabled state of properties based on
+ the value of other properties. See extensions_framework.validate
+ for test syntax.
+
+ """
+ enabled = {}
+
+ """The alert dict controls the alert state of properties based on
+ the value of other properties. See extensions_framework.validate
+ for test syntax.
+
+ """
+ alert = {}
+
+ """The properties list describes each property to be created. Each
+ item should be a dict of args to pass to a
+ bpy.props.<?>Property function, with the exception of 'type'
+ which is used and stripped by extensions_framework in order to
+ determine which Property creation function to call.
+
+ Example item:
+ {
+ 'type': 'int', # bpy.props.IntProperty
+ 'attr': 'threads', # bpy.types.<type>.threads
+ 'name': 'Render Threads', # Rendered next to the UI
+ 'description': 'Number of threads to use', # Tooltip text in the UI
+ 'default': 1,
+ 'min': 1,
+ 'soft_min': 1,
+ 'max': 64,
+ 'soft_max': 64
+ }
+
+ """
+ properties = []
+
+ def draw_callback(self, context):
+ """Sub-classes can override this to get a callback when
+ rendering is completed by a property_group_renderer sub-class.
+
+ """
+
+ pass
+
+ @classmethod
+ def get_exportable_properties(cls):
+ """Return a list of properties which have the 'save_in_preset' key
+ set to True, and hence should be saved into preset files.
+
+ """
+
+ out = []
+ for prop in cls.properties:
+ if 'save_in_preset' in prop.keys() and prop['save_in_preset']:
+ out.append(prop)
+ return out
+
+ def reset(self):
+ """Reset all properties in this group to the default value,
+ if specified"""
+ for prop in self.properties:
+ pk = prop.keys()
+ if 'attr' in pk and 'default' in pk and hasattr(self, prop['attr']):
+ setattr(self, prop['attr'], prop['default'])
class Addon(object):
- """A list of classes registered by this addon"""
- static_addon_count = 0
-
- addon_serial = 0
- addon_classes = None
- bl_info = None
-
- BL_VERSION = None
- BL_IDNAME = None
-
- def __init__(self, bl_info=None):
- self.addon_classes = []
- self.bl_info = bl_info
-
- # Keep a count in case we have to give this addon an anonymous name
- self.addon_serial = Addon.static_addon_count
- Addon.static_addon_count += 1
-
- if self.bl_info:
- self.BL_VERSION = '.'.join(['%s'%v for v in self.bl_info['version']]).lower()
- self.BL_IDNAME = self.bl_info['name'].lower() + '-' + self.BL_VERSION
- else:
- # construct anonymous name
- self.BL_VERSION = '0'
- self.BL_IDNAME = 'Addon-%03d'%self.addon_serial
-
- def addon_register_class(self, cls):
- """This method is designed to be used as a decorator on RNA-registerable
- classes defined by the addon. By using this decorator, this class will
- keep track of classes registered by this addon so that they can be
- unregistered later in the correct order.
-
- """
- self.addon_classes.append(cls)
- return cls
-
- def register(self):
- """This is the register function that should be exposed in the addon's
- __init__.
-
- """
- for cls in self.addon_classes:
- bpy.utils.register_class(cls)
- if hasattr(cls, 'ef_attach_to'): cls.initialise_properties()
-
- def unregister(self):
- """This is the unregister function that should be exposed in the addon's
- __init__.
-
- """
- for cls in self.addon_classes[::-1]: # unregister in reverse order
- if hasattr(cls, 'ef_attach_to'): cls.remove_properties()
- bpy.utils.unregister_class(cls)
-
- def init_functions(self):
- """Returns references to the three functions that this addon needs
- for successful class registration management. In the addon's __init__
- you would use like this:
-
- addon_register_class, register, unregister = Addon().init_functions()
-
- """
-
- return self.register, self.unregister
+ """A list of classes registered by this addon"""
+ static_addon_count = 0
+
+ addon_serial = 0
+ addon_classes = None
+ bl_info = None
+
+ BL_VERSION = None
+ BL_IDNAME = None
+
+ def __init__(self, bl_info=None):
+ self.addon_classes = []
+ self.bl_info = bl_info
+
+ # Keep a count in case we have to give this addon an anonymous name
+ self.addon_serial = Addon.static_addon_count
+ Addon.static_addon_count += 1
+
+ if self.bl_info:
+ self.BL_VERSION = '.'.join(['%s'%v for v in self.bl_info['version']]).lower()
+ self.BL_IDNAME = self.bl_info['name'].lower() + '-' + self.BL_VERSION
+ else:
+ # construct anonymous name
+ self.BL_VERSION = '0'
+ self.BL_IDNAME = 'Addon-%03d'%self.addon_serial
+
+ def addon_register_class(self, cls):
+ """This method is designed to be used as a decorator on RNA-registerable
+ classes defined by the addon. By using this decorator, this class will
+ keep track of classes registered by this addon so that they can be
+ unregistered later in the correct order.
+
+ """
+ self.addon_classes.append(cls)
+ return cls
+
+ def register(self):
+ """This is the register function that should be exposed in the addon's
+ __init__.
+
+ """
+ for cls in self.addon_classes:
+ bpy.utils.register_class(cls)
+ if hasattr(cls, 'ef_attach_to'): cls.initialise_properties()
+
+ def unregister(self):
+ """This is the unregister function that should be exposed in the addon's
+ __init__.
+
+ """
+ for cls in self.addon_classes[::-1]: # unregister in reverse order
+ if hasattr(cls, 'ef_attach_to'): cls.remove_properties()
+ bpy.utils.unregister_class(cls)
+
+ def init_functions(self):
+ """Returns references to the three functions that this addon needs
+ for successful class registration management. In the addon's __init__
+ you would use like this:
+
+ addon_register_class, register, unregister = Addon().init_functions()
+
+ """
+
+ return self.register, self.unregister
diff --git a/modules/extensions_framework/ui.py b/modules/extensions_framework/ui.py
index 4f24d873..94430911 100644
--- a/modules/extensions_framework/ui.py
+++ b/modules/extensions_framework/ui.py
@@ -29,309 +29,309 @@ import bpy
from extensions_framework.validate import Logician
class EF_OT_msg(bpy.types.Operator):
- """An operator to show simple messages in the UI"""
- bl_idname = 'ef.msg'
- bl_label = 'Show UI Message'
- msg_type = bpy.props.StringProperty(default='INFO')
- msg_text = bpy.props.StringProperty(default='')
- def execute(self, context):
- self.report({self.properties.msg_type}, self.properties.msg_text)
- return {'FINISHED'}
+ """An operator to show simple messages in the UI"""
+ bl_idname = 'ef.msg'
+ bl_label = 'Show UI Message'
+ msg_type = bpy.props.StringProperty(default='INFO')
+ msg_text = bpy.props.StringProperty(default='')
+ def execute(self, context):
+ self.report({self.properties.msg_type}, self.properties.msg_text)
+ return {'FINISHED'}
def _get_item_from_context(context, path):
- """Utility to get an object when the path to it is known:
- _get_item_from_context(context, ['a','b','c']) returns
- context.a.b.c
- No error checking is performed other than checking that context
- is not None. Exceptions caused by invalid path should be caught in
- the calling code.
-
- """
-
- if context is not None:
- for p in path:
- context = getattr(context, p)
- return context
+ """Utility to get an object when the path to it is known:
+ _get_item_from_context(context, ['a','b','c']) returns
+ context.a.b.c
+ No error checking is performed other than checking that context
+ is not None. Exceptions caused by invalid path should be caught in
+ the calling code.
+
+ """
+
+ if context is not None:
+ for p in path:
+ context = getattr(context, p)
+ return context
class property_group_renderer(bpy.types.Panel):
- """Mix-in class for sub-classes of bpy.types.Panel. This class
- will provide the draw() method which implements drawing one or
- more property groups derived from
- extensions_framework.declarative_propery_group.
- The display_property_groups list attribute describes which
- declarative_property_groups should be drawn in the Panel, and
- how to extract those groups from the context passed to draw().
-
- """
-
- """The display_property_groups list attribute specifies which
- custom declarative_property_groups this panel should draw, and
- where to find that property group in the active context.
- Example item:
- ( ('scene',), 'myaddon_property_group')
- In this case, this renderer will look for properties in
- context.scene.myaddon_property_group to draw in the Panel.
-
- """
- display_property_groups = []
-
- def draw(self, context):
- """Sub-classes should override this if they need to display
- other (object-related) property groups. super().draw(context)
- can be a useful call in those cases.
-
- """
- for property_group_path, property_group_name in \
- self.display_property_groups:
- ctx = _get_item_from_context(context, property_group_path)
- property_group = getattr(ctx, property_group_name)
- for p in property_group.controls:
- self.draw_column(p, self.layout, ctx, context,
- property_group=property_group)
- property_group.draw_callback(context)
-
- def check_visibility(self, lookup_property, property_group):
- """Determine if the lookup_property should be drawn in the Panel"""
- vt = Logician(property_group)
- if lookup_property in property_group.visibility.keys():
- if hasattr(property_group, lookup_property):
- member = getattr(property_group, lookup_property)
- else:
- member = None
- return vt.test_logic(member,
- property_group.visibility[lookup_property])
- else:
- return True
-
- def check_enabled(self, lookup_property, property_group):
- """Determine if the lookup_property should be enabled in the Panel"""
- et = Logician(property_group)
- if lookup_property in property_group.enabled.keys():
- if hasattr(property_group, lookup_property):
- member = getattr(property_group, lookup_property)
- else:
- member = None
- return et.test_logic(member,
- property_group.enabled[lookup_property])
- else:
- return True
-
- def check_alert(self, lookup_property, property_group):
- """Determine if the lookup_property should be in an alert state in the Panel"""
- et = Logician(property_group)
- if lookup_property in property_group.alert.keys():
- if hasattr(property_group, lookup_property):
- member = getattr(property_group, lookup_property)
- else:
- member = None
- return et.test_logic(member,
- property_group.alert[lookup_property])
- else:
- return False
-
- def is_real_property(self, lookup_property, property_group):
- for prop in property_group.properties:
- if prop['attr'] == lookup_property:
- return prop['type'] not in ['text', 'prop_search']
-
- return False
-
- def draw_column(self, control_list_item, layout, context,
- supercontext=None, property_group=None):
- """Draw a column's worth of UI controls in this Panel"""
- if type(control_list_item) is list:
- draw_row = False
-
- found_percent = None
- for sp in control_list_item:
- if type(sp) is float:
- found_percent = sp
- elif type(sp) is list:
- for ssp in [s for s in sp if self.is_real_property(s, property_group)]:
- draw_row = draw_row or self.check_visibility(ssp,
- property_group)
- else:
- draw_row = draw_row or self.check_visibility(sp,
- property_group)
-
- next_items = [s for s in control_list_item if type(s) in [str, list]]
- if draw_row and len(next_items) > 0:
- if found_percent is not None:
- splt = layout.split(percentage=found_percent)
- else:
- splt = layout.row(True)
- for sp in next_items:
- col2 = splt.column()
- self.draw_column(sp, col2, context, supercontext,
- property_group)
- else:
- if self.check_visibility(control_list_item, property_group):
-
- for current_property in property_group.properties:
- if current_property['attr'] == control_list_item:
- current_property_keys = current_property.keys()
-
- sub_layout_created = False
- if not self.check_enabled(control_list_item, property_group):
- last_layout = layout
- sub_layout_created = True
-
- layout = layout.row()
- layout.enabled = False
-
- if self.check_alert(control_list_item, property_group):
- if not sub_layout_created:
- last_layout = layout
- sub_layout_created = True
- layout = layout.row()
- layout.alert = True
-
- if 'type' in current_property_keys:
- if current_property['type'] in ['int', 'float',
- 'float_vector', 'string']:
- layout.prop(
- property_group,
- control_list_item,
- text = current_property['name'],
- expand = current_property['expand'] \
- if 'expand' in current_property_keys \
- else False,
- slider = current_property['slider'] \
- if 'slider' in current_property_keys \
- else False,
- toggle = current_property['toggle'] \
- if 'toggle' in current_property_keys \
- else False,
- icon_only = current_property['icon_only'] \
- if 'icon_only' in current_property_keys \
- else False,
- event = current_property['event'] \
- if 'event' in current_property_keys \
- else False,
- full_event = current_property['full_event'] \
- if 'full_event' in current_property_keys \
- else False,
- emboss = current_property['emboss'] \
- if 'emboss' in current_property_keys \
- else True,
- )
- if current_property['type'] in ['enum']:
- if 'use_menu' in current_property_keys and \
- current_property['use_menu']:
- layout.prop_menu_enum(
- property_group,
- control_list_item,
- text = current_property['name']
- )
- else:
- layout.prop(
- property_group,
- control_list_item,
- text = current_property['name'],
- expand = current_property['expand'] \
- if 'expand' in current_property_keys \
- else False,
- slider = current_property['slider'] \
- if 'slider' in current_property_keys \
- else False,
- toggle = current_property['toggle'] \
- if 'toggle' in current_property_keys \
- else False,
- icon_only = current_property['icon_only'] \
- if 'icon_only' in current_property_keys \
- else False,
- event = current_property['event'] \
- if 'event' in current_property_keys \
- else False,
- full_event = current_property['full_event'] \
- if 'full_event' in current_property_keys \
- else False,
- emboss = current_property['emboss'] \
- if 'emboss' in current_property_keys \
- else True,
- )
- if current_property['type'] in ['bool']:
- layout.prop(
- property_group,
- control_list_item,
- text = current_property['name'],
- toggle = current_property['toggle'] \
- if 'toggle' in current_property_keys \
- else False,
- icon_only = current_property['icon_only'] \
- if 'icon_only' in current_property_keys \
- else False,
- event = current_property['event'] \
- if 'event' in current_property_keys \
- else False,
- full_event = current_property['full_event'] \
- if 'full_event' in current_property_keys \
- else False,
- emboss = current_property['emboss'] \
- if 'emboss' in current_property_keys \
- else True,
- )
- elif current_property['type'] in ['operator']:
- args = {}
- for optional_arg in ('text', 'icon'):
- if optional_arg in current_property_keys:
- args.update({
- optional_arg: current_property[optional_arg],
- })
- layout.operator( current_property['operator'], **args )
-
- elif current_property['type'] in ['menu']:
- args = {}
- for optional_arg in ('text', 'icon'):
- if optional_arg in current_property_keys:
- args.update({
- optional_arg: current_property[optional_arg],
- })
- layout.menu(current_property['menu'], **args)
-
- elif current_property['type'] in ['text']:
- layout.label(
- text = current_property['name']
- )
-
- elif current_property['type'] in ['template_list']:
- layout.template_list(
- current_property['src'](supercontext, context),
- current_property['src_attr'],
- current_property['trg'](supercontext, context),
- current_property['trg_attr'],
- rows = 4 \
- if not 'rows' in current_property_keys \
- else current_property['rows'],
- maxrows = 4 \
- if not 'rows' in current_property_keys \
- else current_property['rows'],
- type = 'DEFAULT' \
- if not 'list_type' in current_property_keys \
- else current_property['list_type']
- )
-
- elif current_property['type'] in ['prop_search']:
- layout.prop_search(
- current_property['trg'](supercontext,
- context),
- current_property['trg_attr'],
- current_property['src'](supercontext,
- context),
- current_property['src_attr'],
- text = current_property['name'],
- )
-
- elif current_property['type'] in ['ef_callback']:
- getattr(self, current_property['method'])(supercontext)
- else:
- layout.prop(property_group, control_list_item)
-
- if sub_layout_created:
- layout = last_layout
-
- # Fire a draw callback if specified
- if 'draw' in current_property_keys:
- current_property['draw'](supercontext, context)
-
- break
+ """Mix-in class for sub-classes of bpy.types.Panel. This class
+ will provide the draw() method which implements drawing one or
+ more property groups derived from
+ extensions_framework.declarative_propery_group.
+ The display_property_groups list attribute describes which
+ declarative_property_groups should be drawn in the Panel, and
+ how to extract those groups from the context passed to draw().
+
+ """
+
+ """The display_property_groups list attribute specifies which
+ custom declarative_property_groups this panel should draw, and
+ where to find that property group in the active context.
+ Example item:
+ ( ('scene',), 'myaddon_property_group')
+ In this case, this renderer will look for properties in
+ context.scene.myaddon_property_group to draw in the Panel.
+
+ """
+ display_property_groups = []
+
+ def draw(self, context):
+ """Sub-classes should override this if they need to display
+ other (object-related) property groups. super().draw(context)
+ can be a useful call in those cases.
+
+ """
+ for property_group_path, property_group_name in \
+ self.display_property_groups:
+ ctx = _get_item_from_context(context, property_group_path)
+ property_group = getattr(ctx, property_group_name)
+ for p in property_group.controls:
+ self.draw_column(p, self.layout, ctx, context,
+ property_group=property_group)
+ property_group.draw_callback(context)
+
+ def check_visibility(self, lookup_property, property_group):
+ """Determine if the lookup_property should be drawn in the Panel"""
+ vt = Logician(property_group)
+ if lookup_property in property_group.visibility.keys():
+ if hasattr(property_group, lookup_property):
+ member = getattr(property_group, lookup_property)
+ else:
+ member = None
+ return vt.test_logic(member,
+ property_group.visibility[lookup_property])
+ else:
+ return True
+
+ def check_enabled(self, lookup_property, property_group):
+ """Determine if the lookup_property should be enabled in the Panel"""
+ et = Logician(property_group)
+ if lookup_property in property_group.enabled.keys():
+ if hasattr(property_group, lookup_property):
+ member = getattr(property_group, lookup_property)
+ else:
+ member = None
+ return et.test_logic(member,
+ property_group.enabled[lookup_property])
+ else:
+ return True
+
+ def check_alert(self, lookup_property, property_group):
+ """Determine if the lookup_property should be in an alert state in the Panel"""
+ et = Logician(property_group)
+ if lookup_property in property_group.alert.keys():
+ if hasattr(property_group, lookup_property):
+ member = getattr(property_group, lookup_property)
+ else:
+ member = None
+ return et.test_logic(member,
+ property_group.alert[lookup_property])
+ else:
+ return False
+
+ def is_real_property(self, lookup_property, property_group):
+ for prop in property_group.properties:
+ if prop['attr'] == lookup_property:
+ return prop['type'] not in ['text', 'prop_search']
+
+ return False
+
+ def draw_column(self, control_list_item, layout, context,
+ supercontext=None, property_group=None):
+ """Draw a column's worth of UI controls in this Panel"""
+ if type(control_list_item) is list:
+ draw_row = False
+
+ found_percent = None
+ for sp in control_list_item:
+ if type(sp) is float:
+ found_percent = sp
+ elif type(sp) is list:
+ for ssp in [s for s in sp if self.is_real_property(s, property_group)]:
+ draw_row = draw_row or self.check_visibility(ssp,
+ property_group)
+ else:
+ draw_row = draw_row or self.check_visibility(sp,
+ property_group)
+
+ next_items = [s for s in control_list_item if type(s) in [str, list]]
+ if draw_row and len(next_items) > 0:
+ if found_percent is not None:
+ splt = layout.split(percentage=found_percent)
+ else:
+ splt = layout.row(True)
+ for sp in next_items:
+ col2 = splt.column()
+ self.draw_column(sp, col2, context, supercontext,
+ property_group)
+ else:
+ if self.check_visibility(control_list_item, property_group):
+
+ for current_property in property_group.properties:
+ if current_property['attr'] == control_list_item:
+ current_property_keys = current_property.keys()
+
+ sub_layout_created = False
+ if not self.check_enabled(control_list_item, property_group):
+ last_layout = layout
+ sub_layout_created = True
+
+ layout = layout.row()
+ layout.enabled = False
+
+ if self.check_alert(control_list_item, property_group):
+ if not sub_layout_created:
+ last_layout = layout
+ sub_layout_created = True
+ layout = layout.row()
+ layout.alert = True
+
+ if 'type' in current_property_keys:
+ if current_property['type'] in ['int', 'float',
+ 'float_vector', 'string']:
+ layout.prop(
+ property_group,
+ control_list_item,
+ text = current_property['name'],
+ expand = current_property['expand'] \
+ if 'expand' in current_property_keys \
+ else False,
+ slider = current_property['slider'] \
+ if 'slider' in current_property_keys \
+ else False,
+ toggle = current_property['toggle'] \
+ if 'toggle' in current_property_keys \
+ else False,
+ icon_only = current_property['icon_only'] \
+ if 'icon_only' in current_property_keys \
+ else False,
+ event = current_property['event'] \
+ if 'event' in current_property_keys \
+ else False,
+ full_event = current_property['full_event'] \
+ if 'full_event' in current_property_keys \
+ else False,
+ emboss = current_property['emboss'] \
+ if 'emboss' in current_property_keys \
+ else True,
+ )
+ if current_property['type'] in ['enum']:
+ if 'use_menu' in current_property_keys and \
+ current_property['use_menu']:
+ layout.prop_menu_enum(
+ property_group,
+ control_list_item,
+ text = current_property['name']
+ )
+ else:
+ layout.prop(
+ property_group,
+ control_list_item,
+ text = current_property['name'],
+ expand = current_property['expand'] \
+ if 'expand' in current_property_keys \
+ else False,
+ slider = current_property['slider'] \
+ if 'slider' in current_property_keys \
+ else False,
+ toggle = current_property['toggle'] \
+ if 'toggle' in current_property_keys \
+ else False,
+ icon_only = current_property['icon_only'] \
+ if 'icon_only' in current_property_keys \
+ else False,
+ event = current_property['event'] \
+ if 'event' in current_property_keys \
+ else False,
+ full_event = current_property['full_event'] \
+ if 'full_event' in current_property_keys \
+ else False,
+ emboss = current_property['emboss'] \
+ if 'emboss' in current_property_keys \
+ else True,
+ )
+ if current_property['type'] in ['bool']:
+ layout.prop(
+ property_group,
+ control_list_item,
+ text = current_property['name'],
+ toggle = current_property['toggle'] \
+ if 'toggle' in current_property_keys \
+ else False,
+ icon_only = current_property['icon_only'] \
+ if 'icon_only' in current_property_keys \
+ else False,
+ event = current_property['event'] \
+ if 'event' in current_property_keys \
+ else False,
+ full_event = current_property['full_event'] \
+ if 'full_event' in current_property_keys \
+ else False,
+ emboss = current_property['emboss'] \
+ if 'emboss' in current_property_keys \
+ else True,
+ )
+ elif current_property['type'] in ['operator']:
+ args = {}
+ for optional_arg in ('text', 'icon'):
+ if optional_arg in current_property_keys:
+ args.update({
+ optional_arg: current_property[optional_arg],
+ })
+ layout.operator( current_property['operator'], **args )
+
+ elif current_property['type'] in ['menu']:
+ args = {}
+ for optional_arg in ('text', 'icon'):
+ if optional_arg in current_property_keys:
+ args.update({
+ optional_arg: current_property[optional_arg],
+ })
+ layout.menu(current_property['menu'], **args)
+
+ elif current_property['type'] in ['text']:
+ layout.label(
+ text = current_property['name']
+ )
+
+ elif current_property['type'] in ['template_list']:
+ layout.template_list(
+ current_property['src'](supercontext, context),
+ current_property['src_attr'],
+ current_property['trg'](supercontext, context),
+ current_property['trg_attr'],
+ rows = 4 \
+ if not 'rows' in current_property_keys \
+ else current_property['rows'],
+ maxrows = 4 \
+ if not 'rows' in current_property_keys \
+ else current_property['rows'],
+ type = 'DEFAULT' \
+ if not 'list_type' in current_property_keys \
+ else current_property['list_type']
+ )
+
+ elif current_property['type'] in ['prop_search']:
+ layout.prop_search(
+ current_property['trg'](supercontext,
+ context),
+ current_property['trg_attr'],
+ current_property['src'](supercontext,
+ context),
+ current_property['src_attr'],
+ text = current_property['name'],
+ )
+
+ elif current_property['type'] in ['ef_callback']:
+ getattr(self, current_property['method'])(supercontext)
+ else:
+ layout.prop(property_group, control_list_item)
+
+ if sub_layout_created:
+ layout = last_layout
+
+ # Fire a draw callback if specified
+ if 'draw' in current_property_keys:
+ current_property['draw'](supercontext, context)
+
+ break
diff --git a/modules/extensions_framework/util.py b/modules/extensions_framework/util.py
index b48c7026..dd71e55a 100644
--- a/modules/extensions_framework/util.py
+++ b/modules/extensions_framework/util.py
@@ -49,222 +49,222 @@ this one.
export_path = '';
def path_relative_to_export(p):
- """Return a path that is relative to the export path"""
- global export_path
- p = filesystem_path(p)
- ep = os.path.dirname(export_path)
-
- if os.sys.platform[:3] == "win":
- # Prevent an error whereby python thinks C: and c: are different drives
- if p[1] == ':': p = p[0].lower() + p[1:]
- if ep[1] == ':': ep = ep[0].lower() + ep[1:]
-
- try:
- relp = os.path.relpath(p, ep)
- except ValueError: # path on different drive on windows
- relp = p
-
- return relp.replace('\\', '/')
+ """Return a path that is relative to the export path"""
+ global export_path
+ p = filesystem_path(p)
+ ep = os.path.dirname(export_path)
+
+ if os.sys.platform[:3] == "win":
+ # Prevent an error whereby python thinks C: and c: are different drives
+ if p[1] == ':': p = p[0].lower() + p[1:]
+ if ep[1] == ':': ep = ep[0].lower() + ep[1:]
+
+ try:
+ relp = os.path.relpath(p, ep)
+ except ValueError: # path on different drive on windows
+ relp = p
+
+ return relp.replace('\\', '/')
def filesystem_path(p):
- """Resolve a relative Blender path to a real filesystem path"""
- if p.startswith('//'):
- pout = bpy.path.abspath(p)
- else:
- pout = os.path.realpath(p)
-
- return pout.replace('\\', '/')
+ """Resolve a relative Blender path to a real filesystem path"""
+ if p.startswith('//'):
+ pout = bpy.path.abspath(p)
+ else:
+ pout = os.path.realpath(p)
+
+ return pout.replace('\\', '/')
# TODO: - somehow specify TYPES to get/set from config
def find_config_value(module, section, key, default):
- """Attempt to find the configuration value specified by string key
- in the specified section of module's configuration file. If it is
- not found, return default.
-
- """
- global config_paths
- fc = []
- for p in config_paths:
- if os.path.exists(p) and os.path.isdir(p) and os.access(p, os.W_OK):
- fc.append( '/'.join([p, '%s.cfg' % module]))
-
- if len(fc) < 1:
- print('Cannot find %s config file path' % module)
- return default
-
- cp = configparser.SafeConfigParser()
-
- cfg_files = cp.read(fc)
- if len(cfg_files) > 0:
- try:
- val = cp.get(section, key)
- if val == 'true':
- return True
- elif val == 'false':
- return False
- else:
- return val
- except:
- return default
- else:
- return default
+ """Attempt to find the configuration value specified by string key
+ in the specified section of module's configuration file. If it is
+ not found, return default.
+
+ """
+ global config_paths
+ fc = []
+ for p in config_paths:
+ if os.path.exists(p) and os.path.isdir(p) and os.access(p, os.W_OK):
+ fc.append( '/'.join([p, '%s.cfg' % module]))
+
+ if len(fc) < 1:
+ print('Cannot find %s config file path' % module)
+ return default
+
+ cp = configparser.SafeConfigParser()
+
+ cfg_files = cp.read(fc)
+ if len(cfg_files) > 0:
+ try:
+ val = cp.get(section, key)
+ if val == 'true':
+ return True
+ elif val == 'false':
+ return False
+ else:
+ return val
+ except:
+ return default
+ else:
+ return default
def write_config_value(module, section, key, value):
- """Attempt to write the configuration value specified by string key
- in the specified section of module's configuration file.
-
- """
- global config_paths
- fc = []
- for p in config_paths:
- if os.path.exists(p) and os.path.isdir(p) and os.access(p, os.W_OK):
- fc.append( '/'.join([p, '%s.cfg' % module]))
-
- if len(fc) < 1:
- raise Exception('Cannot find a writable path to store %s config file' %
- module)
-
- cp = configparser.SafeConfigParser()
-
- cfg_files = cp.read(fc)
-
- if not cp.has_section(section):
- cp.add_section(section)
-
- if value == True:
- cp.set(section, key, 'true')
- elif value == False:
- cp.set(section, key, 'false')
- else:
- cp.set(section, key, value)
-
- if len(cfg_files) < 1:
- cfg_files = fc
-
- fh=open(cfg_files[0],'w')
- cp.write(fh)
- fh.close()
-
- return True
+ """Attempt to write the configuration value specified by string key
+ in the specified section of module's configuration file.
+
+ """
+ global config_paths
+ fc = []
+ for p in config_paths:
+ if os.path.exists(p) and os.path.isdir(p) and os.access(p, os.W_OK):
+ fc.append( '/'.join([p, '%s.cfg' % module]))
+
+ if len(fc) < 1:
+ raise Exception('Cannot find a writable path to store %s config file' %
+ module)
+
+ cp = configparser.SafeConfigParser()
+
+ cfg_files = cp.read(fc)
+
+ if not cp.has_section(section):
+ cp.add_section(section)
+
+ if value == True:
+ cp.set(section, key, 'true')
+ elif value == False:
+ cp.set(section, key, 'false')
+ else:
+ cp.set(section, key, value)
+
+ if len(cfg_files) < 1:
+ cfg_files = fc
+
+ fh=open(cfg_files[0],'w')
+ cp.write(fh)
+ fh.close()
+
+ return True
def scene_filename():
- """Construct a safe scene filename, using 'untitled' instead of ''"""
- filename = os.path.splitext(os.path.basename(bpy.data.filepath))[0]
- if filename == '':
- filename = 'untitled'
- return bpy.path.clean_name(filename)
+ """Construct a safe scene filename, using 'untitled' instead of ''"""
+ filename = os.path.splitext(os.path.basename(bpy.data.filepath))[0]
+ if filename == '':
+ filename = 'untitled'
+ return bpy.path.clean_name(filename)
def temp_directory():
- """Return the system temp directory"""
- return tempfile.gettempdir()
+ """Return the system temp directory"""
+ return tempfile.gettempdir()
def temp_file(ext='tmp'):
- """Get a temporary filename with the given extension. This function
- will actually attempt to create the file."""
- tf, fn = tempfile.mkstemp(suffix='.%s'%ext)
- os.close(tf)
- return fn
+ """Get a temporary filename with the given extension. This function
+ will actually attempt to create the file."""
+ tf, fn = tempfile.mkstemp(suffix='.%s'%ext)
+ os.close(tf)
+ return fn
class TimerThread(threading.Thread):
- """Periodically call self.kick(). The period of time in seconds
- between calling is given by self.KICK_PERIOD, and the first call
- may be delayed by setting self.STARTUP_DELAY, also in seconds.
- self.kick() will continue to be called at regular intervals until
- self.stop() is called. Since this is a thread, calling self.join()
- may be wise after calling self.stop() if self.kick() is performing
- a task necessary for the continuation of the program.
- The object that creates this TimerThread may pass into it data
- needed during self.kick() as a dict LocalStorage in __init__().
-
- """
- STARTUP_DELAY = 0
- KICK_PERIOD = 8
-
- active = True
- timer = None
-
- LocalStorage = None
-
- def __init__(self, LocalStorage=dict()):
- threading.Thread.__init__(self)
- self.LocalStorage = LocalStorage
-
- def set_kick_period(self, period):
- """Adjust the KICK_PERIOD between __init__() and start()"""
- self.KICK_PERIOD = period + self.STARTUP_DELAY
-
- def stop(self):
- """Stop this timer. This method does not join()"""
- self.active = False
- if self.timer is not None:
- self.timer.cancel()
-
- def run(self):
- """Timed Thread loop"""
- while self.active:
- self.timer = threading.Timer(self.KICK_PERIOD, self.kick_caller)
- self.timer.start()
- if self.timer.isAlive(): self.timer.join()
-
- def kick_caller(self):
- """Intermediary between the kick-wait-loop and kick to allow
- adjustment of the first KICK_PERIOD by STARTUP_DELAY
-
- """
- if self.STARTUP_DELAY > 0:
- self.KICK_PERIOD -= self.STARTUP_DELAY
- self.STARTUP_DELAY = 0
-
- self.kick()
-
- def kick(self):
- """Sub-classes do their work here"""
- pass
+ """Periodically call self.kick(). The period of time in seconds
+ between calling is given by self.KICK_PERIOD, and the first call
+ may be delayed by setting self.STARTUP_DELAY, also in seconds.
+ self.kick() will continue to be called at regular intervals until
+ self.stop() is called. Since this is a thread, calling self.join()
+ may be wise after calling self.stop() if self.kick() is performing
+ a task necessary for the continuation of the program.
+ The object that creates this TimerThread may pass into it data
+ needed during self.kick() as a dict LocalStorage in __init__().
+
+ """
+ STARTUP_DELAY = 0
+ KICK_PERIOD = 8
+
+ active = True
+ timer = None
+
+ LocalStorage = None
+
+ def __init__(self, LocalStorage=dict()):
+ threading.Thread.__init__(self)
+ self.LocalStorage = LocalStorage
+
+ def set_kick_period(self, period):
+ """Adjust the KICK_PERIOD between __init__() and start()"""
+ self.KICK_PERIOD = period + self.STARTUP_DELAY
+
+ def stop(self):
+ """Stop this timer. This method does not join()"""
+ self.active = False
+ if self.timer is not None:
+ self.timer.cancel()
+
+ def run(self):
+ """Timed Thread loop"""
+ while self.active:
+ self.timer = threading.Timer(self.KICK_PERIOD, self.kick_caller)
+ self.timer.start()
+ if self.timer.isAlive(): self.timer.join()
+
+ def kick_caller(self):
+ """Intermediary between the kick-wait-loop and kick to allow
+ adjustment of the first KICK_PERIOD by STARTUP_DELAY
+
+ """
+ if self.STARTUP_DELAY > 0:
+ self.KICK_PERIOD -= self.STARTUP_DELAY
+ self.STARTUP_DELAY = 0
+
+ self.kick()
+
+ def kick(self):
+ """Sub-classes do their work here"""
+ pass
def format_elapsed_time(t):
- """Format a duration in seconds as an HH:MM:SS format time"""
-
- td = datetime.timedelta(seconds=t)
- min = td.days*1440 + td.seconds/60.0
- hrs = td.days*24 + td.seconds/3600.0
-
- return '%i:%02i:%02i' % (hrs, min%60, td.seconds%60)
+ """Format a duration in seconds as an HH:MM:SS format time"""
+
+ td = datetime.timedelta(seconds=t)
+ min = td.days*1440 + td.seconds/60.0
+ hrs = td.days*24 + td.seconds/3600.0
+
+ return '%i:%02i:%02i' % (hrs, min%60, td.seconds%60)
def getSequenceTexturePath(it, f):
- import bpy.path
- import os.path
- import string
- fd = it.image_user.frame_duration
- fs = it.image_user.frame_start
- fo = it.image_user.frame_offset
- cyclic = it.image_user.use_cyclic
- ext = os.path.splitext(it.image.filepath)[-1]
- fb = bpy.path.display_name_from_filepath(it.image.filepath)
- dn = os.path.dirname(it.image.filepath)
- rf = fb[::-1]
- nl = 0
- for i in range (len(fb)):
- if rf[i] in string.digits:
- nl += 1
- else:
- break
- head = fb[:len(fb)-nl]
- fnum = f
- if fs != 1:
- if f != fs:
- fnum -= (fs-1)
- elif f == fs:
- fnum = 1
- if fnum <= 0:
- if cyclic:
- fnum = fd - abs(fnum) % fd
- else:
- fnum = 1
- elif fnum > fd:
- if cyclic:
- fnum = fnum % fd
- else:
- fnum = fd
- fnum += fo
- return dn + "/" + head + str(fnum).rjust(nl, "0") + ext
+ import bpy.path
+ import os.path
+ import string
+ fd = it.image_user.frame_duration
+ fs = it.image_user.frame_start
+ fo = it.image_user.frame_offset
+ cyclic = it.image_user.use_cyclic
+ ext = os.path.splitext(it.image.filepath)[-1]
+ fb = bpy.path.display_name_from_filepath(it.image.filepath)
+ dn = os.path.dirname(it.image.filepath)
+ rf = fb[::-1]
+ nl = 0
+ for i in range (len(fb)):
+ if rf[i] in string.digits:
+ nl += 1
+ else:
+ break
+ head = fb[:len(fb)-nl]
+ fnum = f
+ if fs != 1:
+ if f != fs:
+ fnum -= (fs-1)
+ elif f == fs:
+ fnum = 1
+ if fnum <= 0:
+ if cyclic:
+ fnum = fd - abs(fnum) % fd
+ else:
+ fnum = 1
+ elif fnum > fd:
+ if cyclic:
+ fnum = fnum % fd
+ else:
+ fnum = fd
+ fnum += fo
+ return dn + "/" + head + str(fnum).rjust(nl, "0") + ext
diff --git a/modules/extensions_framework/validate.py b/modules/extensions_framework/validate.py
index d9cee8fd..5b20552b 100644
--- a/modules/extensions_framework/validate.py
+++ b/modules/extensions_framework/validate.py
@@ -34,13 +34,13 @@ is possible to arrive at a True or False result for various purposes:
A Subject can be any object whose members are readable with getattr() :
class Subject(object):
- a = 0
- b = 1
- c = 'foo'
- d = True
- e = False
- f = 8
- g = 'bar'
+ a = 0
+ b = 1
+ c = 'foo'
+ d = True
+ e = False
+ f = 8
+ g = 'bar'
Tests are described thus:
@@ -51,27 +51,27 @@ numerical comparison.
With regards to Subject, each of these evaluate to True:
TESTA = {
- 'a': 0,
- 'c': Logic_OR([ 'foo', 'bar' ]),
- 'd': Logic_AND([True, True]),
- 'f': Logic_AND([8, {'b': 1}]),
- 'e': {'b': Logic_Operator({'gte':1, 'lt':3}) },
- 'g': Logic_OR([ 'baz', Logic_AND([{'b': 1}, {'f': 8}]) ])
+ 'a': 0,
+ 'c': Logic_OR([ 'foo', 'bar' ]),
+ 'd': Logic_AND([True, True]),
+ 'f': Logic_AND([8, {'b': 1}]),
+ 'e': {'b': Logic_Operator({'gte':1, 'lt':3}) },
+ 'g': Logic_OR([ 'baz', Logic_AND([{'b': 1}, {'f': 8}]) ])
}
With regards to Subject, each of these evaluate to False:
TESTB = {
- 'a': 'foo',
- 'c': Logic_OR([ 'bar', 'baz' ]),
- 'd': Logic_AND([ True, 'foo' ]),
- 'f': Logic_AND([9, {'b': 1}]),
- 'e': {'b': Logic_Operator({'gte':-10, 'lt': 1}) },
- 'g': Logic_OR([ 'baz', Logic_AND([{'b':0}, {'f': 8}]) ])
+ 'a': 'foo',
+ 'c': Logic_OR([ 'bar', 'baz' ]),
+ 'd': Logic_AND([ True, 'foo' ]),
+ 'f': Logic_AND([9, {'b': 1}]),
+ 'e': {'b': Logic_Operator({'gte':-10, 'lt': 1}) },
+ 'g': Logic_OR([ 'baz', Logic_AND([{'b':0}, {'f': 8}]) ])
}
With regards to Subject, this test is invalid
TESTC = {
- 'n': 0
+ 'n': 0
}
Tests are executed thus:
@@ -82,132 +82,132 @@ L.execute(TESTA)
"""
class Logic_AND(list):
- pass
+ pass
class Logic_OR(list):
- pass
+ pass
class Logic_Operator(dict):
- pass
+ pass
class Logician(object):
- """Given a subject and a dict that describes tests to perform on
- its members, this class will evaluate True or False results for
- each member/test pair. See the examples below for test syntax.
-
- """
-
- subject = None
- def __init__(self, subject):
- self.subject = subject
-
- def get_member(self, member_name):
- """Get a member value from the subject object. Raise exception
- if subject is None or member not found.
-
- """
- if self.subject is None:
- raise Exception('Cannot run tests on a subject which is None')
-
- return getattr(self.subject, member_name)
-
- def test_logic(self, member, logic, operator='eq'):
- """Find the type of test to run on member, and perform that test"""
-
- if type(logic) is dict:
- return self.test_dict(member, logic)
- elif type(logic) is Logic_AND:
- return self.test_and(member, logic)
- elif type(logic) is Logic_OR:
- return self.test_or(member, logic)
- elif type(logic) is Logic_Operator:
- return self.test_operator(member, logic)
- else:
- # compare the value, I think using Logic_Operator() here
- # allows completeness in test_operator(), but I can't put
- # my finger on why for the minute
- return self.test_operator(member,
- Logic_Operator({operator: logic}))
-
- def test_operator(self, member, value):
- """Execute the operators contained within value and expect that
- ALL operators are True
-
- """
-
- # something in this method is incomplete, what if operand is
- # a dict, Logic_AND, Logic_OR or another Logic_Operator ?
- # Do those constructs even make any sense ?
-
- result = True
- for operator, operand in value.items():
- operator = operator.lower().strip()
- if operator in ['eq', '==']:
- result &= member==operand
- if operator in ['not', '!=']:
- result &= member!=operand
- if operator in ['lt', '<']:
- result &= member<operand
- if operator in ['lte', '<=']:
- result &= member<=operand
- if operator in ['gt', '>']:
- result &= member>operand
- if operator in ['gte', '>=']:
- result &= member>=operand
- if operator in ['and', '&']:
- result &= member&operand
- if operator in ['or', '|']:
- result &= member|operand
- if operator in ['len']:
- result &= len(member)==operand
- # I can think of some more, but they're probably not useful.
-
- return result
-
- def test_or(self, member, logic):
- """Member is a value, logic is a set of values, ANY of which
- can be True
-
- """
- result = False
- for test in logic:
- result |= self.test_logic(member, test)
-
- return result
-
- def test_and(self, member, logic):
- """Member is a value, logic is a list of values, ALL of which
- must be True
-
- """
- result = True
- for test in logic:
- result &= self.test_logic(member, test)
-
- return result
-
- def test_dict(self, member, logic):
- """Member is a value, logic is a dict of other members to
- compare to. All other member tests must be True
-
- """
- result = True
- for other_member, test in logic.items():
- result &= self.test_logic(self.get_member(other_member), test)
-
- return result
-
- def execute(self, test):
- """Subject is an object, test is a dict of {member: test} pairs
- to perform on subject's members. Wach key in test is a member
- of subject.
-
- """
-
- for member_name, logic in test.items():
- result = self.test_logic(self.get_member(member_name), logic)
- print('member %s is %s' % (member_name, result))
-
-# A couple of name aliases
+ """Given a subject and a dict that describes tests to perform on
+ its members, this class will evaluate True or False results for
+ each member/test pair. See the examples below for test syntax.
+
+ """
+
+ subject = None
+ def __init__(self, subject):
+ self.subject = subject
+
+ def get_member(self, member_name):
+ """Get a member value from the subject object. Raise exception
+ if subject is None or member not found.
+
+ """
+ if self.subject is None:
+ raise Exception('Cannot run tests on a subject which is None')
+
+ return getattr(self.subject, member_name)
+
+ def test_logic(self, member, logic, operator='eq'):
+ """Find the type of test to run on member, and perform that test"""
+
+ if type(logic) is dict:
+ return self.test_dict(member, logic)
+ elif type(logic) is Logic_AND:
+ return self.test_and(member, logic)
+ elif type(logic) is Logic_OR:
+ return self.test_or(member, logic)
+ elif type(logic) is Logic_Operator:
+ return self.test_operator(member, logic)
+ else:
+ # compare the value, I think using Logic_Operator() here
+ # allows completeness in test_operator(), but I can't put
+ # my finger on why for the minute
+ return self.test_operator(member,
+ Logic_Operator({operator: logic}))
+
+ def test_operator(self, member, value):
+ """Execute the operators contained within value and expect that
+ ALL operators are True
+
+ """
+
+ # something in this method is incomplete, what if operand is
+ # a dict, Logic_AND, Logic_OR or another Logic_Operator ?
+ # Do those constructs even make any sense ?
+
+ result = True
+ for operator, operand in value.items():
+ operator = operator.lower().strip()
+ if operator in ['eq', '==']:
+ result &= member==operand
+ if operator in ['not', '!=']:
+ result &= member!=operand
+ if operator in ['lt', '<']:
+ result &= member<operand
+ if operator in ['lte', '<=']:
+ result &= member<=operand
+ if operator in ['gt', '>']:
+ result &= member>operand
+ if operator in ['gte', '>=']:
+ result &= member>=operand
+ if operator in ['and', '&']:
+ result &= member&operand
+ if operator in ['or', '|']:
+ result &= member|operand
+ if operator in ['len']:
+ result &= len(member)==operand
+ # I can think of some more, but they're probably not useful.
+
+ return result
+
+ def test_or(self, member, logic):
+ """Member is a value, logic is a set of values, ANY of which
+ can be True
+
+ """
+ result = False
+ for test in logic:
+ result |= self.test_logic(member, test)
+
+ return result
+
+ def test_and(self, member, logic):
+ """Member is a value, logic is a list of values, ALL of which
+ must be True
+
+ """
+ result = True
+ for test in logic:
+ result &= self.test_logic(member, test)
+
+ return result
+
+ def test_dict(self, member, logic):
+ """Member is a value, logic is a dict of other members to
+ compare to. All other member tests must be True
+
+ """
+ result = True
+ for other_member, test in logic.items():
+ result &= self.test_logic(self.get_member(other_member), test)
+
+ return result
+
+ def execute(self, test):
+ """Subject is an object, test is a dict of {member: test} pairs
+ to perform on subject's members. Wach key in test is a member
+ of subject.
+
+ """
+
+ for member_name, logic in test.items():
+ result = self.test_logic(self.get_member(member_name), logic)
+ print('member %s is %s' % (member_name, result))
+
+# A couple of name aliases
class Validation(Logician):
- pass
+ pass
class Visibility(Logician):
- pass
+ pass