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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDoug Hammond <doughammond@hamsterfight.co.uk>2010-11-18 00:28:22 +0300
committerDoug Hammond <doughammond@hamsterfight.co.uk>2010-11-18 00:28:22 +0300
commit9183f20fb4828bbd562cb6a2b8f65c8750c3c27b (patch)
tree756297ad1532112b2a4f61a3278e47643b7117a7 /release
parentb99a11bc3ce7ccc3bae35ac253d8171d2f02a285 (diff)
extensions_framework: lots of docs and code formatting to be more pep8-like
Diffstat (limited to 'release')
-rw-r--r--release/scripts/modules/extensions_framework/__init__.py177
-rw-r--r--release/scripts/modules/extensions_framework/engine.py10
-rw-r--r--release/scripts/modules/extensions_framework/outputs/xml_output.py36
-rw-r--r--release/scripts/modules/extensions_framework/plugin.py44
-rw-r--r--release/scripts/modules/extensions_framework/ui.py170
-rw-r--r--release/scripts/modules/extensions_framework/util.py85
-rw-r--r--release/scripts/modules/extensions_framework/validate.py91
7 files changed, 386 insertions, 227 deletions
diff --git a/release/scripts/modules/extensions_framework/__init__.py b/release/scripts/modules/extensions_framework/__init__.py
index fdb7ddd706d..6c4ced376c5 100644
--- a/release/scripts/modules/extensions_framework/__init__.py
+++ b/release/scripts/modules/extensions_framework/__init__.py
@@ -24,131 +24,170 @@
#
# ***** END GPL LICENCE BLOCK *****
#
+import time
-import os, time
import bpy
-#----------------------------------------------------------------------------------------------------------------------
+from extensions_framework.ui import EF_OT_msg
+
+bpy.types.register(EF_OT_msg)
+del EF_OT_msg
+
def log(str, popup=False, module_name='EF'):
- print("[%s %s] %s" % (module_name, time.strftime('%Y-%b-%d %H:%M:%S'), 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
)
-#----------------------------------------------------------------------------------------------------------------------
-
-from .ui import EF_OT_msg
-
-bpy.types.register(EF_OT_msg)
-ef_path = os.path.realpath( os.path.dirname(__file__) )
-# log('Extensions_Framework detected and loaded from %s'%ef_path)
-
-del EF_OT_msg, os
-
-#----------------------------------------------------------------------------------------------------------------------
-
-class ef(object):
- '''
- Extensions Framework base class
- '''
-
- added_property_cache = {}
+added_property_cache = {}
def init_properties(obj, props, cache=True):
- if not obj in ef.added_property_cache.keys():
- ef.added_property_cache[obj] = []
+ """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:
- if cache and prop['attr'] in ef.added_property_cache[obj]:
- continue
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']}
+ a = {k: v for k,v in prop.items() if k in ['name',
+ 'description','default']}
elif prop['type'] == 'collection':
t = bpy.props.CollectionProperty
- a = {k: v for k,v in prop.items() if k in ["ptype", "name", "description"]}
+ a = {k: v for k,v in prop.items() if k in ["ptype", "name",
+ "description"]}
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"]}
+ a = {k: v for k,v in prop.items() if k in ["items", "name",
+ "description", "default"]}
elif prop['type'] == 'float':
t = bpy.props.FloatProperty
- a = {k: v for k,v in prop.items() if k in ["name", "description", "min", "max", "soft_min", "soft_max", "default", "precision"]}
+ a = {k: v for k,v in prop.items() if k in ["name",
+ "description", "min", "max", "soft_min", "soft_max",
+ "default", "precision"]}
elif prop['type'] == 'float_vector':
t = bpy.props.FloatVectorProperty
- a = {k: v for k,v in prop.items() if k in ["name", "description", "min", "max", "soft_min", "soft_max", "default", "precision", "size", "subtype"]}
+ a = {k: v for k,v in prop.items() if k in ["name",
+ "description", "min", "max", "soft_min", "soft_max",
+ "default", "precision", "size", "subtype"]}
elif prop['type'] == 'int':
t = bpy.props.IntProperty
- a = {k: v for k,v in prop.items() if k in ["name", "description", "min", "max", "soft_min", "soft_max", "default"]}
+ a = {k: v for k,v in prop.items() if k in ["name",
+ "description", "min", "max", "soft_min", "soft_max",
+ "default"]}
elif prop['type'] == 'pointer':
t = bpy.props.PointerProperty
- a = {k: v for k,v in prop.items() if k in ["ptype", "name", "description"]}
+ a = {k: v for k,v in prop.items() if k in ["ptype", "name",
+ "description"]}
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", "maxlen", "default", "subtype"]}
+ a = {k: v for k,v in prop.items() if k in ["name",
+ "description", "maxlen", "default", "subtype"]}
else:
- #ef.log('Property type not recognised: %s' % prop['type'])
continue
setattr(obj, prop['attr'], t(**a))
- ef.added_property_cache[obj].append(prop['attr'])
- #log('Created property %s.%s' % (obj, prop['attr']))
+ added_property_cache[obj].append(prop['attr'])
except KeyError:
+ # Silently skip invalid entries in props
continue
+
class declarative_property_group(bpy.types.IDPropertyGroup):
+ """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.
+
+ """
+
+ """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 = [
- # 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
- ]
-
- # Include some properties in display based on values of others
- visibility = {
- # See ef.validate for test syntax
+ """
+ controls = []
+
+ """The visibility dict controls the display of properties based on
+ the value of other properties. See extensions_framework.validate
+ for test syntax.
+
+ """
+ visibility = {}
+
+ """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
}
- # engine-specific properties to create in the scene.
- # Each item should be a dict of args to pass to a
- # bpy.types.Scene.<?>Property function, with the exception
- # of 'type' which is used and stripped by ef
- properties = [
- # example:
- #{
- # 'type': 'int',
- # 'attr': 'threads',
- # 'name': 'Render Threads',
- # 'description': 'Number of threads to use',
- # '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 rendered by a property_group_renderer class
- '''
+ """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']:
diff --git a/release/scripts/modules/extensions_framework/engine.py b/release/scripts/modules/extensions_framework/engine.py
index 448eb715bef..0a6fecb034a 100644
--- a/release/scripts/modules/extensions_framework/engine.py
+++ b/release/scripts/modules/extensions_framework/engine.py
@@ -24,12 +24,14 @@
#
# ***** END GPL LICENCE BLOCK *****
#
-from .plugin import plugin
+from extensions_framework.plugin import plugin
class engine_base(plugin):
- '''
- Render Engine plugin base class
- '''
+ """Render Engine plugin base class
+
+ TODO: Remove, this class hasn't grown to be useful
+
+ """
bl_label = 'Abstract Render Engine Base Class'
diff --git a/release/scripts/modules/extensions_framework/outputs/xml_output.py b/release/scripts/modules/extensions_framework/outputs/xml_output.py
index 4d1af9cc877..3b1102c1888 100644
--- a/release/scripts/modules/extensions_framework/outputs/xml_output.py
+++ b/release/scripts/modules/extensions_framework/outputs/xml_output.py
@@ -28,36 +28,49 @@ import xml.etree.cElementTree as ET
import xml.dom.minidom as MD
class xml_output(object):
+ """This class serves to describe an XML output, it uses
+ cElementTree and minidom to construct and format the XML
+ data.
+ """
+
+ """The format dict describes the XML structure that this class
+ should generate, and which properties should be used to fill
+ the XML data structure
+
+ """
format = {}
def __str__(self):
return ET.tostring(self.root)
def write_pretty(self, file):
+ """Write a formatted XML string to file"""
xml_dom = MD.parseString(ET.tostring(self.root, encoding='utf-8'))
xml_dom.writexml(file, addindent=' ', newl='\n', encoding='utf-8')
def pretty(self):
+ """Return a formatted XML string"""
xml_str = MD.parseString(ET.tostring(self.root))
return xml_str.toprettyxml()
- # This should be overridden in classes that produce XML conditionally
def get_format(self):
+ """This should be overridden in classes that produce XML
+ conditionally
+
+ """
return self.format
def compute(self, context):
+ """Compute the XML output from the input format"""
self.context = context
self.root = ET.Element(self.root_element)
self.parse_dict(self.get_format(), self.root)
- #ET.dump(root)
return self.root
-
- def make_subelement(self, elem, name):
- return ET.SubElement(elem, name)
-
+
+ """Formatting functions for various data types"""
format_types = {
'bool': lambda c,x: str(x).lower(),
'collection': lambda c,x: x,
@@ -67,8 +80,12 @@ class xml_output(object):
'pointer': lambda c,x: x,
'string': lambda c,x: x,
}
-
+
def parse_dict(self, d, elem):
+ """Parse the values in the format dict and collect the
+ formatted data into XML structure starting at self.root
+
+ """
for key in d.keys():
# tuple provides multiple child elements
if type(d[key]) is tuple:
@@ -93,4 +110,7 @@ class xml_output(object):
if 'compute' in p.keys():
x.text = str(p['compute'](self.context, self))
else:
- x.text = str(self.format_types[p['type']](self.context, getattr(self, d[key])))
+ x.text = str(
+ self.format_types[p['type']](self.context,
+ getattr(self, d[key]))
+ )
diff --git a/release/scripts/modules/extensions_framework/plugin.py b/release/scripts/modules/extensions_framework/plugin.py
index a6a5b719e30..76f41930fdd 100644
--- a/release/scripts/modules/extensions_framework/plugin.py
+++ b/release/scripts/modules/extensions_framework/plugin.py
@@ -24,21 +24,45 @@
#
# ***** END GPL LICENCE BLOCK *****
#
-from . import init_properties
-from . import log
-
import bpy
+from extensions_framework import init_properties
+from extensions_framework import log
+
class plugin(object):
+ """Base class for plugins which wish to make use of utilities
+ provided in extensions_framework. Using the property_groups
+ attribute and the install() and uninstall() methods, a large number
+ of custom scene properties can be easily defined, displayed and
+ managed.
+
+ TODO: Rename, 'extension' would be more appropriate than 'plugin'
+
+ """
- # List of IDPropertyGroup types to create in the scene
- property_groups = [
- # ('bpy.type prototype to attach to. eg. Scene', <declarative_property_group type>)
- ]
+ """The property_groups defines a list of declarative_property_group
+ types to create in specified types during the initialisation of the
+ plugin.
+ Item format:
+ ('bpy.type prototype to attach to', <declarative_property_group>)
+
+ Example item:
+ ('Scene', myaddon_property_group)
+ In this example, a new property group will be attached to
+ bpy.types.Scene and all of the properties described in that group
+ will be added to it.
+ See extensions_framework.declarative_property_group.
+
+ """
+ property_groups = []
@classmethod
def install(r_class):
- # create custom property groups
+ """Initialise this plugin. So far, all this does is to create
+ custom property groups specified in the property_groups
+ attribute.
+
+ """
for property_group_parent, property_group in r_class.property_groups:
call_init = False
if property_group_parent is not None:
@@ -52,19 +76,17 @@ class plugin(object):
'description': property_group.__name__
}])
call_init = True
- #print('Created IDPropertyGroup %s.%s' % (prototype, property_group.__name__))
else:
call_init = True
if call_init:
init_properties(property_group, property_group.properties)
- #print('Initialised IDPropertyGroup %s' % property_group.__name__)
log('Extension "%s" initialised' % r_class.bl_label)
@classmethod
def uninstall(r_class):
- # unregister property groups in reverse order
+ """Unregister property groups in reverse order"""
reverse_property_groups = [p for p in r_class.property_groups]
reverse_property_groups.reverse()
for property_group_parent, property_group in reverse_property_groups:
diff --git a/release/scripts/modules/extensions_framework/ui.py b/release/scripts/modules/extensions_framework/ui.py
index 5d29ed7245a..bf68596b823 100644
--- a/release/scripts/modules/extensions_framework/ui.py
+++ b/release/scripts/modules/extensions_framework/ui.py
@@ -26,9 +26,10 @@
#
import bpy
-from .validate import Visibility
+from extensions_framework.validate import Visibility
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')
@@ -38,50 +39,81 @@ class EF_OT_msg(bpy.types.Operator):
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
class property_group_renderer(object):
+ """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().
+
+ """
- # Choose which custom property groups this panel should draw, and
- # where to find that property group in the active context
- display_property_groups = [
- # ( ('scene',), 'declarative_property_group name')
- ]
+ """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
- '''
- for property_group_path, property_group_name in self.display_property_groups:
+ """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)
+ self.draw_column(p, self.layout, ctx, context,
+ property_group=property_group)
property_group.draw_callback(context)
@staticmethod
def property_reload():
- '''
- override this in sub classes to force data refresh upon scene reload
- '''
+ """Override this in sub classes to force data refresh upon scene reload
+
+ TODO: Remove, this is not used anywhere
+ """
pass
- def check_visibility(self, lookup_property, context, property_group):
+ def check_visibility(self, lookup_property, property_group):
+ """Determine if the lookup_property should be drawn in the Panel"""
vt = Visibility(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])
+ return vt.test_logic(member,
+ property_group.visibility[lookup_property])
else:
return True
- def draw_column(self, control_list_item, layout, context, supercontext=None, property_group=None):
+ 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:
do_split = False
@@ -91,85 +123,117 @@ class property_group_renderer(object):
found_percent = sp
elif type(sp) is list:
for ssp in control_list_item:
- do_split = do_split and self.check_visibility(ssp, context, property_group)
+ do_split = do_split and self.check_visibility(ssp,
+ property_group)
else:
- do_split = do_split or self.check_visibility(sp, context, property_group)
+ do_split = do_split or self.check_visibility(sp,
+ property_group)
if do_split:
- # print('split %s'%p)
if found_percent is not None:
- fp = { 'percentage': found_percent }
+ fp = {'percentage': found_percent}
splt = layout.split(**fp)
else:
splt = layout.row(True)
- for sp in [s for s in control_list_item if type(s) in [str, list] ]:
+ for sp in [s for s in control_list_item if type(s) in \
+ [str, list]]:
col2 = splt.column()
- self.draw_column(sp, col2, context, supercontext, property_group)
- #else:
- # print('dont split %s'%p)
+ self.draw_column(sp, col2, context, supercontext,
+ property_group)
else:
- if self.check_visibility(control_list_item, context, property_group):
+ 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()
if 'type' in current_property_keys:
- if current_property['type'] in ['int', 'float', 'float_vector', 'enum', 'string']:
+ if current_property['type'] in ['int', 'float',
+ 'float_vector', 'enum', '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,
+ 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,
+ 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']:
layout.operator(current_property['operator'],
- text = current_property['text'],
- icon = current_property['icon']
+ text = current_property['text'],
+ icon = current_property['icon']
)
elif current_property['type'] in ['text']:
layout.label(
- text = current_property['name']
+ text = current_property['name']
)
elif current_property['type'] in ['template_list']:
- # row.template_list(idblock, "texture_slots", idblock, "active_texture_index", rows=2)
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']
+ 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']:
- # split.prop_search(tex, "uv_layer", ob.data, "uv_textures", text="")
layout.prop_search(
- current_property['trg'](supercontext, context),
+ current_property['trg'](supercontext,
+ context),
current_property['trg_attr'],
- current_property['src'](supercontext, context),
+ current_property['src'](supercontext,
+ context),
current_property['src_attr'],
- text = current_property['name'],
+ text = current_property['name'],
)
else:
layout.prop(property_group, control_list_item)
diff --git a/release/scripts/modules/extensions_framework/util.py b/release/scripts/modules/extensions_framework/util.py
index 8bbeaf74ec3..403eeb8a759 100644
--- a/release/scripts/modules/extensions_framework/util.py
+++ b/release/scripts/modules/extensions_framework/util.py
@@ -25,23 +25,24 @@
#
# ***** END GPL LICENCE BLOCK *****
#
-import datetime, os, configparser, threading, tempfile
+import configparser
+import datetime
+import os
+import tempfile
+import threading
import bpy
config_paths = bpy.utils.script_paths()
-'''
-This path is set at the start of export, so that
-calls to path_relative_to_export() can make all
-exported paths relative to this one
-'''
+"""This path is set at the start of export, so that calls to
+path_relative_to_export() can make all exported paths relative to
+this one.
+"""
export_path = '';
def path_relative_to_export(p):
- '''
- Return a path that is relative to the export path
- '''
+ """Return a path that is relative to the export path"""
global export_path
p = filesystem_path(p)
try:
@@ -49,26 +50,25 @@ def path_relative_to_export(p):
except ValueError: # path on different drive on windows
relp = p
- #print('Resolving rel path %s -> %s' % (p, relp))
-
return relp.replace('\\', '/')
def filesystem_path(p):
- '''
- Resolve a relative Blender path to a real filesystem path
- '''
+ """Resolve a relative Blender path to a real filesystem path"""
if p.startswith('//'):
pout = bpy.path.abspath(p)
else:
pout = os.path.realpath(p)
- #print('Resolving FS path %s -> %s' % (p,pout))
-
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:
@@ -97,6 +97,10 @@ def find_config_value(module, section, key, default):
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:
@@ -104,7 +108,8 @@ def write_config_value(module, section, key, value):
fc.append( '/'.join([p, '%s.cfg' % module]))
if len(fc) < 1:
- raise Exception('Cannot find a writable path to store %s config file' % module)
+ raise Exception('Cannot find a writable path to store %s config file' %
+ module)
cp = configparser.SafeConfigParser()
@@ -130,32 +135,35 @@ def write_config_value(module, section, key, value):
return True
def scene_filename():
- '''
- Construct a safe 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)
def temp_directory():
- '''
- Return the system temp directory
- '''
+ """Return the system temp directory"""
return tempfile.gettempdir()
def temp_file(ext='tmp'):
- '''
- Get a temporary filename with the given extension
- '''
+ """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()
- '''
+ """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
@@ -169,24 +177,27 @@ class TimerThread(threading.Thread):
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
- '''
-
+ """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
@@ -194,15 +205,11 @@ class TimerThread(threading.Thread):
self.kick()
def kick(self):
- '''
- sub-classes do their work here
- '''
+ """Sub-classes do their work here"""
pass
def format_elapsed_time(t):
- '''
- Format a duration in seconds as an HH:MM:SS format time
- '''
+ """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
diff --git a/release/scripts/modules/extensions_framework/validate.py b/release/scripts/modules/extensions_framework/validate.py
index b2960fcf01f..d9cee8fd807 100644
--- a/release/scripts/modules/extensions_framework/validate.py
+++ b/release/scripts/modules/extensions_framework/validate.py
@@ -24,7 +24,7 @@
#
# ***** END GPL LICENCE BLOCK *****
#
-'''
+"""
Pure logic and validation class.
By using a Subject object, and a dict of described logic tests, it
@@ -45,10 +45,11 @@ class Subject(object):
Tests are described thus:
-Use the special list types Logic_AND and Logic_OR to describe combinations
-of values and other members. Use Logic_Operator for numerical comparison.
+Use the special list types Logic_AND and Logic_OR to describe
+combinations of values and other members. Use Logic_Operator for
+numerical comparison.
-# With regards to Subject, each of these evaluate to True:
+With regards to Subject, each of these evaluate to True:
TESTA = {
'a': 0,
'c': Logic_OR([ 'foo', 'bar' ]),
@@ -58,7 +59,7 @@ TESTA = {
'g': Logic_OR([ 'baz', Logic_AND([{'b': 1}, {'f': 8}]) ])
}
-# With regards to Subject, each of these evaluate to False:
+With regards to Subject, each of these evaluate to False:
TESTB = {
'a': 'foo',
'c': Logic_OR([ 'bar', 'baz' ]),
@@ -68,17 +69,17 @@ TESTB = {
'g': Logic_OR([ 'baz', Logic_AND([{'b':0}, {'f': 8}]) ])
}
-# With regards to Subject, this test is invalid
+With regards to Subject, this test is invalid
TESTC = {
'n': 0
}
-# Tests are executed thus:
+Tests are executed thus:
S = Subject()
L = Logician(S)
L.execute(TESTA)
-'''
+"""
class Logic_AND(list):
pass
@@ -88,31 +89,28 @@ class Logic_Operator(dict):
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.
- '''
+ """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 is subject is None or member not found.
- '''
+ """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
- '''
+ """Find the type of test to run on member, and perform that test"""
if type(logic) is dict:
return self.test_dict(member, logic)
@@ -123,17 +121,21 @@ class Logician(object):
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}))
+ # 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
- '''
+ """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 ?
+ """
+
+ # 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():
@@ -161,9 +163,10 @@ class Logician(object):
return result
def test_or(self, member, logic):
- '''
- member is a value, logic is a set of values, ANY of which can be True
- '''
+ """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)
@@ -171,9 +174,10 @@ class Logician(object):
return result
def test_and(self, member, logic):
- '''
- member is a value, logic is a list of values, ALL of which must be True
- '''
+ """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)
@@ -181,9 +185,10 @@ class Logician(object):
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
- '''
+ """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)
@@ -191,11 +196,11 @@ class Logician(object):
return result
def execute(self, test):
- '''
- subject is an object,
- test is a dict of {member: test} pairs to perform on subject's members.
- each key in test is a member of subject.
- '''
+ """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)