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:
Diffstat (limited to 'system_demo_mode')
-rw-r--r--system_demo_mode/__init__.py206
-rw-r--r--system_demo_mode/config.py74
-rw-r--r--system_demo_mode/demo_mode.py510
3 files changed, 790 insertions, 0 deletions
diff --git a/system_demo_mode/__init__.py b/system_demo_mode/__init__.py
new file mode 100644
index 00000000..cc568c11
--- /dev/null
+++ b/system_demo_mode/__init__.py
@@ -0,0 +1,206 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+bl_info = {
+ "name": "Demo Mode",
+ "author": "Campbell Barton",
+ "blender": (2, 5, 7),
+ "api": 35622,
+ "location": "Demo Menu",
+ "description": "Demo mode lets you select multiple blend files and loop over them.",
+ "warning": "",
+ "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.5/Py/"\
+ "Scripts/System/Demo_Mode#Running_Demo_Mode",
+ "tracker_url": "",
+ "support": 'OFFICIAL',
+ "category": "System"}
+
+# To support reload properly, try to access a package var, if it's there, reload everything
+if "bpy" in locals():
+ import imp
+ if "config" in locals():
+ imp.reload(config)
+
+
+import bpy
+from bpy.props import StringProperty, BoolProperty, IntProperty, FloatProperty, EnumProperty
+from io_utils import ImportHelper
+
+
+class DemoModeSetup(bpy.types.Operator):
+ '''Creates a demo script and optionally executes'''
+ bl_idname = "wm.demo_mode_setup"
+ bl_label = "Demo Mode (Setup)"
+ bl_options = {'PRESET'}
+
+ # List of operator properties, the attributes will be assigned
+ # to the class instance from the operator settings before calling.
+
+ # these are used to create the file list.
+ filepath = StringProperty(name="File Path", description="Filepath used for importing the file", maxlen=1024, default="", subtype='FILE_PATH')
+ random_order = BoolProperty(name="Random Order", description="Select files randomly", default=False)
+ mode = EnumProperty(items=(
+ ('AUTO', "Auto", ""),
+ ('PLAY', "Play", ""),
+ ('RENDER', "Render", ""),
+ ),
+ name="Method")
+
+ run = BoolProperty(name="Run Immediately!", description="Run demo immediately", default=True)
+
+ # these are mapped directly to the config!
+ #
+ # anim
+ # ====
+ anim_cycles = IntProperty(name="Cycles", description="Number of times to play the animation", min=1, max=1000, default=2)
+ anim_time_min = FloatProperty(name="Time Min", description="Minimum number of seconds to show the animation for (for small loops)", min=0.0, max=1000.0, soft_min=1.0, soft_max=1000.0, default=4.0)
+ anim_time_max = FloatProperty(name="Time Max", description="Maximum number of seconds to show the animation for (incase the end frame is very high for no reason)", min=0.0, max=100000000.0, soft_min=1.0, soft_max=100000000.0, default=8.0)
+ anim_screen_switch = FloatProperty(name="Screen Switch", description="Time between switching screens (in seconds) or 0 to disable", min=0.0, max=100000000.0, soft_min=1.0, soft_max=60.0, default=0.0)
+ #
+ # render
+ # ======
+ display_render = FloatProperty(name="Render Delay", description="Time to display the rendered image before moving on (in seconds)", min=0.0, max=60.0, default=4.0)
+ anim_render = BoolProperty(name="Render Anim", description="Render entire animation (render mode only)", default=False)
+
+ def execute(self, context):
+ from . import config
+
+ keywords = self.as_keywords(ignore=("filepath", "random_order", "run"))
+
+ from . import config
+ cfg_str, dirpath = config.as_string(self.filepath, self.random_order, **keywords)
+ text = bpy.data.texts.get("demo.py")
+ if text:
+ text.name += ".back"
+
+ text = bpy.data.texts.new("demo.py")
+ text.from_string(cfg_str)
+
+ if self.run:
+ extern_demo_mode_run()
+
+ return {'FINISHED'}
+
+ def invoke(self, context, event):
+ context.window_manager.fileselect_add(self)
+ return {'RUNNING_MODAL'}
+
+ def check(self, context):
+ return True # lazy
+
+ def draw(self, context):
+ layout = self.layout
+
+ box = layout.box()
+ box.label("Search *.blend recursively")
+ box.label("Writes: demo.py config text.")
+
+ col = layout.column()
+ col.prop(self, "run")
+
+ col.label("Generate Settings:")
+ row = col.row()
+ row.prop(self, "mode", expand=True)
+ col.prop(self, "random_order")
+
+ mode = self.mode
+
+ col.separator()
+ colsub = col.column()
+ colsub.active = (mode in ('AUTO', 'PLAY'))
+ colsub.label("Animate Settings:")
+ colsub.prop(self, "anim_cycles")
+ colsub.prop(self, "anim_time_min")
+ colsub.prop(self, "anim_time_max")
+ colsub.prop(self, "anim_screen_switch")
+
+ col.separator()
+ colsub = col.column()
+ colsub.active = (mode in ('AUTO', 'RENDER'))
+ colsub.label("Render Settings:")
+ colsub.prop(self, "display_render")
+
+
+class DemoModeRun(bpy.types.Operator):
+ bl_idname = "wm.demo_mode_run"
+ bl_label = "Demo Mode (Start)"
+
+ def execute(self, context):
+ if extern_demo_mode_run():
+ return {'FINISHED'}
+ else:
+ self.report({'ERROR'}, "Cant load demo.py config, run: File -> Demo Mode (Setup)")
+ return {'CANCELLED'}
+
+
+# --- call demo_mode.py funcs
+def extern_demo_mode_run():
+ # this accesses demo_mode.py which is kept standalone
+ # and can be run direct.
+ from . import demo_mode
+ if demo_mode.load_config():
+ demo_mode.demo_mode_load_file() # kick starts the modal operator
+ return True
+ else:
+ return False
+
+
+def extern_demo_mode_register():
+ # this accesses demo_mode.py which is kept standalone
+ # and can be run direct.
+ from . import demo_mode
+ demo_mode.register()
+
+
+def extern_demo_mode_unregister():
+ # this accesses demo_mode.py which is kept standalone
+ # and can be run direct.
+ from . import demo_mode
+ demo_mode.unregister()
+
+# --- intergration
+
+
+def menu_func(self, context):
+ layout = self.layout
+ layout.operator(DemoModeSetup.bl_idname, icon='PREFERENCES')
+ layout.operator(DemoModeRun.bl_idname, icon='PLAY')
+ layout.separator()
+
+
+def register():
+ bpy.utils.register_class(DemoModeSetup)
+ bpy.utils.register_class(DemoModeRun)
+
+ bpy.types.INFO_MT_file.prepend(menu_func)
+
+ extern_demo_mode_register()
+
+
+def unregister():
+ bpy.utils.unregister_class(DemoModeSetup)
+ bpy.utils.unregister_class(DemoModeRun)
+
+ bpy.types.INFO_MT_file.remove(menu_func)
+
+ extern_demo_mode_unregister()
+
+if __name__ == "__main__":
+ register()
diff --git a/system_demo_mode/config.py b/system_demo_mode/config.py
new file mode 100644
index 00000000..d39daae1
--- /dev/null
+++ b/system_demo_mode/config.py
@@ -0,0 +1,74 @@
+import os
+
+
+def blend_list(path):
+ for dirpath, dirnames, filenames in os.walk(path):
+
+ # skip '.svn'
+ if dirpath.startswith("."):
+ continue
+
+ for filename in filenames:
+ if filename.lower().endswith(".blend"):
+ filepath = os.path.join(dirpath, filename)
+ yield filepath
+
+
+def generate(dirpath, random_order, **kwargs):
+
+ # incase file is selected!
+ if not os.path.exists(dirpath) or not os.path.isdir(dirpath):
+ dirpath = os.path.dirname(dirpath)
+
+ files = list(blend_list(dirpath))
+ if random_order:
+ import random
+ random.shuffle(files)
+ else:
+ files.sort()
+
+ config = []
+ for f in files:
+ defaults = kwargs.copy()
+ defaults["file"] = f
+ config.append(defaults)
+
+ return config, dirpath
+
+
+def as_string(dirpath, random_order, **kwargs):
+ """ Config loader is in demo_mode.py
+ """
+ cfg, dirpath = generate(dirpath, random_order, **kwargs)
+
+ # hint for reader, can be used if files are not found.
+ cfg_str = []
+ cfg_str += ["# generated file\n"]
+ cfg_str += ["\n"]
+ cfg_str += ["# edit the search path so other systems may find the files below\n"]
+ cfg_str += ["# based on name only if the absolute paths cant be found\n"]
+ cfg_str += ["# Use '//' for current blend file path.\n"]
+ cfg_str += ["\n"]
+ cfg_str += ["search_path = %r\n" % dirpath]
+ cfg_str += ["\n"]
+
+ # All these work but use nicest formatting!
+ if 0: # works but not nice to edit.
+ cfg_str += ["config = %r" % cfg]
+ elif 0:
+ import pprint
+ cfg_str += ["config = %s" % pprint.pformat(cfg, indent=0, width=120)]
+ elif 0:
+ cfg_str += [("config = %r" % cfg).replace("{", "\n {")]
+ else:
+ import pprint
+
+ def dict_as_kw(d):
+ return "dict(%s)" % ", ".join(("%s=%s" % (k, pprint.pformat(v))) for k, v in sorted(d.items()))
+ ident = " "
+ cfg_str += ["config = [\n"]
+ for cfg_item in cfg:
+ cfg_str += ["%s%s,\n" % (ident, dict_as_kw(cfg_item))]
+ cfg_str += ["%s]\n\n" % ident]
+
+ return "".join(cfg_str), dirpath
diff --git a/system_demo_mode/demo_mode.py b/system_demo_mode/demo_mode.py
new file mode 100644
index 00000000..a2e64839
--- /dev/null
+++ b/system_demo_mode/demo_mode.py
@@ -0,0 +1,510 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+'''
+Even though this is in a package this can run as a stand alone scripts.
+
+# --- example usage
+blender --python release/scripts/addons/system_demo_mode/demo_mode.py
+
+looks for demo.py textblock or file in the same path as the blend:
+# --- example
+config = [
+ dict(anim_cycles=1, anim_render=False, anim_screen_switch=0.0, anim_time_max=10.0, anim_time_min=4.0, mode='AUTO', display_render=4.0, file='/l/19534_simplest_mesh_2.blend'),
+ dict(anim_cycles=1, anim_render=False, anim_screen_switch=0.0, anim_time_max=10.0, anim_time_min=4.0, mode='AUTO', display_render=4.0, file='/l/252_pivotConstraint_01.blend'),
+ ]
+# ---
+/data/src/blender/lib/tests/rendering/
+'''
+
+import bpy
+import time
+import tempfile
+import os
+
+DEMO_CFG = "demo.py"
+
+# populate from script
+global_config_files = []
+
+
+global_config = dict(anim_cycles=1,
+ anim_render=False,
+ anim_screen_switch=0.0,
+ anim_time_max=60.0,
+ anim_time_min=4.0,
+ mode='AUTO',
+ display_render=4.0)
+
+# switch to the next file in 2 sec.
+global_config_fallback = dict(anim_cycles=1,
+ anim_render=False,
+ anim_screen_switch=0.0,
+ anim_time_max=60.0,
+ anim_time_min=4.0,
+ mode='AUTO',
+ display_render=4.0)
+
+
+global_state = {
+ "init_time": 0.0,
+ "last_switch": 0.0,
+ "reset_anim": False,
+ "anim_cycles": 0, # count how many times we played the anim
+ "last_frame": -1,
+ "render_out": "",
+ "render_time": "", # time render was finished.
+ "timer": None,
+ "basedir": "", # demo.py is stored here
+ "demo_index": 0,
+}
+
+
+def demo_mode_auto_select():
+
+ play_area = 0
+ render_area = 0
+
+ totimg = 0
+
+ for area in bpy.context.window.screen.areas:
+ size = area.width * area.height
+ if area.type in {'VIEW_3D', 'GRAPH_EDITOR', 'DOPESHEET_EDITOR', 'NLA_EDITOR', 'TIMELINE'}:
+ play_area += size
+ elif area.type in {'IMAGE_EDITOR', 'SEQUENCE_EDITOR', 'NODE_EDITOR'}:
+ render_area += size
+
+ if area.type == 'IMAGE_EDITOR':
+ totimg += 1
+
+ # since our test files have this as defacto standard
+ scene = bpy.context.scene
+ if totimg >= 2 and (scene.camera or scene.render.use_sequencer):
+ mode = 'RENDER'
+ else:
+ if play_area >= render_area:
+ mode = 'PLAY'
+ else:
+ mode = 'RENDER'
+
+ if 0:
+ return 'PLAY'
+
+ return mode
+
+
+def demo_mode_next_file(step=1):
+ print(global_state["demo_index"])
+ global_state["demo_index"] = (global_state["demo_index"] + step) % len(global_config_files)
+ print(global_state["demo_index"], "....")
+ print("func:demo_mode_next_file", global_state["demo_index"])
+ filepath = global_config_files[global_state["demo_index"]]["file"]
+ bpy.ops.wm.open_mainfile(filepath=filepath)
+
+
+def demo_mode_timer_add():
+ global_state["timer"] = bpy.context.window_manager.event_timer_add(0.8, bpy.context.window)
+
+
+def demo_mode_timer_remove():
+ if global_state["timer"]:
+ bpy.context.window_manager.event_timer_remove(global_state["timer"])
+ global_state["timer"] = None
+
+
+def demo_mode_load_file():
+ """ Take care, this can only do limited functions since its running
+ before the file is fully loaded.
+ Some operators will crash like playing an animation.
+ """
+ print("func:demo_mode_load_file")
+ DemoMode.first_run = True
+ bpy.ops.wm.demo_mode('EXEC_DEFAULT')
+
+
+def demo_mode_init():
+ print("func:demo_mode_init")
+ DemoKeepAlive.ensure()
+
+ if 1:
+ global_config.clear()
+ global_config.update(global_config_files[global_state["demo_index"]])
+
+ print(global_config)
+
+ demo_mode_timer_add()
+
+ if global_config["mode"] == 'AUTO':
+ global_config["mode"] = demo_mode_auto_select()
+
+ if global_config["mode"] == 'PLAY':
+ global_state["last_frame"] = -1
+ global_state["anim_cycles"] = 0
+ bpy.ops.screen.animation_play()
+
+ elif global_config["mode"] == 'RENDER':
+ print(" render")
+
+ # setup tempfile
+ global_state["render_out"] = tempfile.mkstemp()[1]
+ if os.path.exists(global_state["render_out"]):
+ print(" render!!!")
+ os.remove(global_state["render_out"])
+
+ # setup scene.
+ scene = bpy.context.scene
+ scene.render.filepath = global_state["render_out"]
+ scene.render.file_format = 'AVI_JPEG' if global_config["anim_render"] else 'PNG'
+ scene.render.use_file_extension = False
+ scene.render.use_placeholder = False
+ try:
+ if global_config["anim_render"]:
+ bpy.ops.render.render('INVOKE_DEFAULT', animation=True)
+ else:
+ bpy.ops.render.render('INVOKE_DEFAULT', write_still=True)
+ except RuntimeError: # no camera for eg:
+ import traceback
+ traceback.print_exc()
+
+ open(global_state["render_out"], 'w').close() # touch so we move on.
+
+ else:
+ raise Exception("Unsupported mode %r" % global_config["mode"])
+
+ global_state["init_time"] = global_state["last_switch"] = time.time()
+ global_state["render_time"] = -1.0
+
+
+def demo_mode_update():
+ time_current = time.time()
+ time_delta = time_current - global_state["last_switch"]
+ time_total = time_current - global_state["init_time"]
+
+ # --------------------------------------------------------------------------
+ # ANIMATE MODE
+ if global_config["mode"] == 'PLAY':
+ frame = bpy.context.scene.frame_current
+ # check for exit
+ if time_total > global_config["anim_time_max"]:
+ demo_mode_next_file()
+ return
+ # above cycles and minimum display time
+ if (time_total > global_config["anim_time_min"]) and \
+ (global_state["anim_cycles"] > global_config["anim_cycles"]):
+
+ # looped enough now.
+ demo_mode_next_file()
+ return
+
+ # run update funcs
+ if global_state["reset_anim"]:
+ global_state["reset_anim"] = False
+ bpy.ops.screen.animation_cancel(restore_frame=False)
+ bpy.ops.screen.animation_play()
+
+ # warning, switching the screen can switch the scene
+ # and mess with our last-frame/cycles counting.
+ if global_config["anim_screen_switch"]:
+ # print(time_delta, 1)
+ if time_delta > global_config["anim_screen_switch"]:
+
+ screen = bpy.context.window.screen
+ index = bpy.data.screens.keys().index(screen.name)
+ screen_new = bpy.data.screens[(index if index > 0 else len(bpy.data.screens)) - 1]
+ bpy.context.window.screen = screen_new
+
+ global_state["last_switch"] = time_current
+
+ # if we also switch scenes then reset last frame
+ # otherwise it could mess up cycle calc.
+ if screen.scene != screen_new.scene:
+ global_state["last_frame"] = -1
+
+ #if global_config["mode"] == 'PLAY':
+ if 1:
+ global_state["reset_anim"] = True
+
+ # did we loop?
+ if global_state["last_frame"] > frame:
+ print("Cycle!")
+ global_state["anim_cycles"] += 1
+
+ global_state["last_frame"] = frame
+
+ # --------------------------------------------------------------------------
+ # RENDER MODE
+ elif global_config["mode"] == 'RENDER':
+ if os.path.exists(global_state["render_out"]):
+ # wait until the time has passed
+ # XXX, todo, if rendering an anim we need some way to check its done.
+ if global_state["render_time"] == -1.0:
+ global_state["render_time"] = time.time()
+ else:
+ if time.time() - global_state["render_time"] > global_config["display_render"]:
+ os.remove(global_state["render_out"])
+ demo_mode_next_file()
+ return
+ else:
+ raise Exception("Unsupported mode %r" % global_config["mode"])
+
+# -----------------------------------------------------------------------------
+# modal operator
+
+
+class DemoKeepAlive:
+ secret_attr = "_keepalive"
+
+ @staticmethod
+ def ensure():
+ if DemoKeepAlive.secret_attr not in bpy.app.driver_namespace:
+ bpy.app.driver_namespace[DemoKeepAlive.secret_attr] = DemoKeepAlive()
+
+ @staticmethod
+ def remove():
+ if DemoKeepAlive.secret_attr in bpy.app.driver_namespace:
+ del bpy.app.driver_namespace[DemoKeepAlive.secret_attr]
+
+ def __del__(self):
+ """ Hack, when the file is loaded the drivers namespace is cleared.
+ """
+ if DemoMode.enabled:
+ demo_mode_load_file()
+
+
+class DemoMode(bpy.types.Operator):
+ bl_idname = "wm.demo_mode"
+ bl_label = "Demo"
+
+ enabled = False
+ first_run = True
+
+ def cleanup(self, disable=False):
+ demo_mode_timer_remove()
+ __class__.first_run = True
+
+ if disable:
+ __class__.enabled = False
+ DemoKeepAlive.remove()
+
+ def modal(self, context, event):
+ # print("DemoMode.modal", global_state["anim_cycles"])
+ if not __class__.enabled:
+ self.cleanup(disable=True)
+ return {'CANCELLED'}
+
+ if event.type == 'ESC':
+ self.cleanup(disable=True)
+ # disable here and not in cleanup because this is a user level disable.
+ # which should stay disabled until explicitly enabled again.
+ return {'CANCELLED'}
+
+ # print(event.type)
+ if __class__.first_run:
+ __class__.first_run = False
+
+ demo_mode_init()
+ else:
+ demo_mode_update()
+
+ return {'PASS_THROUGH'}
+
+ def execute(self, context):
+ print("func:DemoMode.execute:", len(global_config_files), "files")
+
+ # load config if not loaded
+ if not global_config_files:
+ load_config()
+ if not global_config_files:
+ self.report({'INFO'}, "No configuration found with text or file: %s. Run File -> Demo Mode Setup" % DEMO_CFG)
+ return {'CANCELLED'}
+
+ # toggle
+ if __class__.enabled and __class__.first_run == False:
+ # this actually cancells the previous running instance
+ # should never happen now, DemoModeControl is for this.
+ return {'CANCELLED'}
+ else:
+ __class__.enabled = True
+ context.window_manager.modal_handler_add(self)
+
+ return {'RUNNING_MODAL'}
+
+ def cancel(self, context):
+ print("func:DemoMode.cancel")
+ # disable here means no running on file-load.
+ self.cleanup()
+ return {'CANCELLED'}
+
+ # call from DemoModeControl
+ @classmethod
+ def disable(cls):
+ if cls.enabled and cls.first_run == False:
+ # this actually cancells the previous running instance
+ # should never happen now, DemoModeControl is for this.
+ cls.enabled = False
+
+
+class DemoModeControl(bpy.types.Operator):
+ bl_idname = "wm.demo_mode_control"
+ bl_label = "Control"
+
+ mode = bpy.props.EnumProperty(items=(
+ ('PREV', "Prev", ""),
+ ('PAUSE', "Pause", ""),
+ ('NEXT', "Next", ""),
+ ),
+ name="Mode")
+
+ def execute(self, context):
+ mode = self.mode
+ if mode == 'PREV':
+ demo_mode_next_file(-1)
+ elif mode == 'NEXT':
+ demo_mode_next_file(1)
+ else: # pause
+ DemoMode.disable()
+ return {'FINISHED'}
+
+
+def menu_func(self, context):
+ # print("func:menu_func - DemoMode.enabled:", DemoMode.enabled, "bpy.app.driver_namespace:", DemoKeepAlive.secret_attr not in bpy.app.driver_namespace, 'global_state["timer"]:', global_state["timer"])
+ layout = self.layout
+ layout.operator_context = 'EXEC_DEFAULT'
+ box = layout.row() # BOX messes layout
+ row = box.row(align=True)
+ row.label("Demo Mode:")
+ if not DemoMode.enabled:
+ row.operator("wm.demo_mode", icon='PLAY', text="")
+ else:
+ row.operator("wm.demo_mode_control", icon='REW', text="").mode = 'PREV'
+ row.operator("wm.demo_mode_control", icon='PAUSE', text="").mode = 'PAUSE'
+ row.operator("wm.demo_mode_control", icon='FF', text="").mode = 'NEXT'
+
+
+def register():
+ bpy.utils.register_class(DemoMode)
+ bpy.utils.register_class(DemoModeControl)
+ bpy.types.INFO_HT_header.append(menu_func)
+
+
+def unregister():
+ bpy.utils.unregister_class(DemoMode)
+ bpy.utils.unregister_class(DemoModeControl)
+ bpy.types.INFO_HT_header.remove(menu_func)
+
+
+# -----------------------------------------------------------------------------
+# parse args
+
+def load_config(cfg_name=DEMO_CFG):
+ namespace = {}
+ global_config_files[:] = []
+ basedir = os.path.dirname(bpy.data.filepath)
+
+ text = bpy.data.texts.get(cfg_name)
+ if text is None:
+ demo_path = os.path.join(basedir, cfg_name)
+ if os.path.exists(demo_path):
+ print("Using config file: %r" % demo_path)
+ demo_file = open(demo_path, "r")
+ demo_data = demo_file.read()
+ demo_file.close()
+ else:
+ demo_data = ""
+ else:
+ print("Using config textblock: %r" % cfg_name)
+ demo_data = text.as_string()
+ demo_path = os.path.join(bpy.data.filepath, cfg_name) # fake
+
+ if not demo_data:
+ print("Could not find %r textblock or %r file." % (DEMO_CFG, demo_path))
+ return False
+
+ namespace["__file__"] = demo_path
+
+ exec(demo_data, namespace, namespace)
+
+ demo_config = namespace["config"]
+ demo_search_path = namespace.get("search_path")
+
+ if demo_search_path is None:
+ print("reading: %r, no search_path found, missing files wont be searched." % demo_path)
+ if demo_search_path.startswith("//"):
+ demo_search_path = os.path.relpath(demo_search_path)
+ if not os.path.exists(demo_search_path):
+ print("reading: %r, search_path %r does not exist." % (demo_path, demo_search_path))
+ demo_search_path = None
+
+ blend_lookup = {}
+ # initialize once, case insensitive dict
+
+ def lookup_file(filepath):
+ filename = os.path.basename(filepath).lower()
+
+ if not blend_lookup:
+ # ensure only ever run once.
+ blend_lookup[None] = None
+
+ def blend_dict_items(path):
+ for dirpath, dirnames, filenames in os.walk(path):
+ # skip '.svn'
+ if dirpath.startswith("."):
+ continue
+ for filename in filenames:
+ if filename.lower().endswith(".blend"):
+ filepath = os.path.join(dirpath, filename)
+ yield (filename.lower(), filepath)
+
+ blend_lookup.update(dict(blend_dict_items(demo_search_path)))
+
+ # fallback to orginal file
+ return blend_lookup.get(filename, filepath)
+ # done with search lookup
+
+ for filecfg in demo_config:
+ filepath_test = filecfg["file"]
+ if not os.path.exists(filepath_test):
+ filepath_test = os.path.join(basedir, filecfg["file"])
+ if not os.path.exists(filepath_test):
+ filepath_test = lookup_file(filepath_test) # attempt to get from searchpath
+ if not os.path.exists(filepath_test):
+ print("Cant find %r or %r, skipping!")
+ continue
+
+ filecfg["file"] = os.path.normpath(filepath_test)
+
+ # sanitize
+ filecfg["file"] = os.path.abspath(filecfg["file"])
+ filecfg["file"] = os.path.normpath(filecfg["file"])
+ print(" Adding: %r" % filecfg["file"])
+ global_config_files.append(filecfg)
+
+ print("found %d files" % len(global_config_files))
+
+ global_state["basedir"] = basedir
+
+ return bool(global_config_files)
+
+
+# support direct execution
+if __name__ == "__main__":
+ register()
+
+ demo_mode_load_file() # kick starts the modal operator