Compare commits

..

5 Commits

Author SHA1 Message Date
cb73030e6a Bumped version to 1.5.99999 2017-01-24 17:22:04 +01:00
fbf02c3625 Captialising labels/captions 2017-01-24 15:15:08 +01:00
95699aca36 Open webbrowser after submitting a Flamenco job
This can be disabled in the add-on preferences.
2017-01-24 15:14:43 +01:00
60018cd78c Using explicit status EnumProperty to show Flamenco status
By Andy's request, I've removed the window_manager.progress_xxx stuff (so
no longer hijacking the mouse cursor) and instead show the Flamenco status
in the Flamenco Render panel.
2017-01-24 14:55:39 +01:00
5f73837d3c Don't write to artist's blend file, but to temporary file.
This file is saved to '{blendfile}.flamenco.blend', which is used to BAM-
pack and create the Flamenco job. After that, the file is removed from
the local filesystem again.
2017-01-24 14:39:49 +01:00
4 changed files with 101 additions and 33 deletions

View File

@@ -21,7 +21,7 @@
bl_info = { bl_info = {
'name': 'Blender Cloud', 'name': 'Blender Cloud',
"author": "Sybren A. Stüvel, Francesco Siddi, Inês Almeida, Antony Riakiotakis", "author": "Sybren A. Stüvel, Francesco Siddi, Inês Almeida, Antony Riakiotakis",
'version': (1, 5, 9999), 'version': (1, 5, 99999),
'blender': (2, 77, 0), 'blender': (2, 77, 0),
'location': 'Addon Preferences panel, and Ctrl+Shift+Alt+A anywhere for texture browser', 'location': 'Addon Preferences panel, and Ctrl+Shift+Alt+A anywhere for texture browser',
'description': 'Texture library browser and Blender Sync. Requires the Blender ID addon ' 'description': 'Texture library browser and Blender Sync. Requires the Blender ID addon '

View File

@@ -155,12 +155,12 @@ class BlenderCloudPreferences(AddonPreferences):
) )
local_texture_dir = StringProperty( local_texture_dir = StringProperty(
name='Default Blender Cloud texture storage directory', name='Default Blender Cloud Texture Storage Directory',
subtype='DIR_PATH', subtype='DIR_PATH',
default='//textures') default='//textures')
open_browser_after_share = BoolProperty( open_browser_after_share = BoolProperty(
name='Open browser after sharing file', name='Open Browser after Sharing File',
description='When enabled, Blender will open a webbrowser', description='When enabled, Blender will open a webbrowser',
default=True default=True
) )
@@ -169,7 +169,7 @@ class BlenderCloudPreferences(AddonPreferences):
# can switch projects and the local path switches with it. # can switch projects and the local path switches with it.
attract_project = PointerProperty(type=BlenderCloudProjectGroup) attract_project = PointerProperty(type=BlenderCloudProjectGroup)
attract_project_local_path = StringProperty( attract_project_local_path = StringProperty(
name='Local project path', name='Local Project Path',
description='Local path of your Attract project, used to search for blend files; ' description='Local path of your Attract project, used to search for blend files; '
'usually best to set to an absolute path', 'usually best to set to an absolute path',
subtype='DIR_PATH', subtype='DIR_PATH',
@@ -180,19 +180,19 @@ class BlenderCloudPreferences(AddonPreferences):
# NOTE: The assumption is that the workers can also find the files in the same path. # NOTE: The assumption is that the workers can also find the files in the same path.
# This assumption is true for the Blender Institute. # This assumption is true for the Blender Institute.
flamenco_job_file_path = StringProperty( flamenco_job_file_path = StringProperty(
name='Job file path', name='Job File Path',
description='Path where to store job files, should be accesible for Workers too', description='Path where to store job files, should be accesible for Workers too',
subtype='DIR_PATH', subtype='DIR_PATH',
default='/render/_flamenco/storage') default='/render/_flamenco/storage')
# TODO: before making Flamenco public, change the defaults to something less Institute-specific. # TODO: before making Flamenco public, change the defaults to something less Institute-specific.
flamenco_job_output_path = StringProperty( flamenco_job_output_path = StringProperty(
name='Job output path', name='Job Output Path',
description='Path where to store output files, should be accessible for Workers', description='Path where to store output files, should be accessible for Workers',
subtype='DIR_PATH', subtype='DIR_PATH',
default='/render/_flamenco/output') default='/render/_flamenco/output')
flamenco_job_output_strip_components = IntProperty( flamenco_job_output_strip_components = IntProperty(
name='Job output path strip components', name='Job Output Path Strip Components',
description='The final output path comprises of the job output path, and the blend file ' description='The final output path comprises of the job output path, and the blend file '
'path relative to the project with this many path components stripped off ' 'path relative to the project with this many path components stripped off '
'the front', 'the front',
@@ -200,6 +200,11 @@ class BlenderCloudPreferences(AddonPreferences):
default=0, default=0,
soft_max=4, soft_max=4,
) )
flamenco_open_browser_after_submit = BoolProperty(
name='Open Browser after Submitting Job',
description='When enabled, Blender will open a webbrowser',
default=True
)
def draw(self, context): def draw(self, context):
import textwrap import textwrap
@@ -379,7 +384,7 @@ class BlenderCloudPreferences(AddonPreferences):
props.path = self.flamenco_job_output_path props.path = self.flamenco_job_output_path
job_output_box.prop(self, 'flamenco_job_output_strip_components', job_output_box.prop(self, 'flamenco_job_output_strip_components',
text='Strip components') text='Strip Components')
from .flamenco import render_output_path from .flamenco import render_output_path
@@ -389,6 +394,8 @@ class BlenderCloudPreferences(AddonPreferences):
props = path_box.operator('flamenco.explore_file_path', text='', icon='DISK_DRIVE') props = path_box.operator('flamenco.explore_file_path', text='', icon='DISK_DRIVE')
props.path = str(output_path.parent) props.path = str(output_path.parent)
flamenco_box.prop(self, 'flamenco_open_browser_after_submit')
# TODO: make a reusable way to select projects, and use that for Attract and Flamenco. # TODO: make a reusable way to select projects, and use that for Attract and Flamenco.
note_box = flamenco_box.column(align=True) note_box = flamenco_box.column(align=True)
note_box.label('NOTE: For now, Flamenco uses the same project as Attract.') note_box.label('NOTE: For now, Flamenco uses the same project as Attract.')

View File

@@ -128,22 +128,27 @@ class FLAMENCO_OT_render(async_loop.AsyncModalOperatorMixin,
if not await self.authenticate(context): if not await self.authenticate(context):
return return
context.window_manager.progress_begin(0, 4)
context.window_manager.progress_update(1)
from pillarsdk import exceptions as sdk_exceptions from pillarsdk import exceptions as sdk_exceptions
from ..blender import preferences from ..blender import preferences
filepath = Path(context.blend_data.filepath)
scene = context.scene scene = context.scene
# The file extension should be determined by the render settings, not necessarily # The file extension should be determined by the render settings, not necessarily
# by the setttings in the output panel. # by the setttings in the output panel.
scene.render.use_file_extension = True scene.render.use_file_extension = True
bpy.ops.wm.save_mainfile()
# Save to a different file, specifically for Flamenco. We shouldn't overwrite
# the artist's file. We can compress, since this file won't be managed by SVN
# and doesn't need diffability.
context.window_manager.flamenco_status = 'PACKING'
filepath = Path(context.blend_data.filepath).with_suffix('.flamenco.blend')
self.log.info('Saving copy to temporary file %s', filepath)
bpy.ops.wm.save_as_mainfile(filepath=str(filepath),
compress=True,
copy=True)
# Determine where the render output will be stored. # Determine where the render output will be stored.
render_output = render_output_path(context) render_output = render_output_path(context, filepath)
if render_output is None: if render_output is None:
self.report({'ERROR'}, 'Current file is outside of project path.') self.report({'ERROR'}, 'Current file is outside of project path.')
self.quit() self.quit()
@@ -155,11 +160,10 @@ class FLAMENCO_OT_render(async_loop.AsyncModalOperatorMixin,
if not outfile: if not outfile:
return return
context.window_manager.progress_update(3)
# Create the job at Flamenco Server. # Create the job at Flamenco Server.
prefs = preferences() prefs = preferences()
context.window_manager.flamenco_status = 'COMMUNICATING'
settings = {'blender_cmd': '{blender}', settings = {'blender_cmd': '{blender}',
'chunk_size': scene.flamenco_render_chunk_size, 'chunk_size': scene.flamenco_render_chunk_size,
'filepath': str(outfile), 'filepath': str(outfile),
@@ -186,6 +190,25 @@ class FLAMENCO_OT_render(async_loop.AsyncModalOperatorMixin,
job_info['missing_files'] = [str(mf) for mf in missing_sources] job_info['missing_files'] = [str(mf) for mf in missing_sources]
json.dump(job_info, outfile, sort_keys=True, indent=4) json.dump(job_info, outfile, sort_keys=True, indent=4)
# We can now remove the local copy we made with bpy.ops.wm.save_as_mainfile().
# Strictly speaking we can already remove it after the BAM-pack, but it may come in
# handy in case of failures.
try:
self.log.info('Removing temporary file %s', filepath)
filepath.unlink()
except Exception as ex:
self.report({'ERROR'}, 'Unable to remove file: %s' % ex)
self.quit()
return
if prefs.flamenco_open_browser_after_submit:
import webbrowser
from urllib.parse import urljoin
from ..blender import PILLAR_WEB_SERVER_URL
url = urljoin(PILLAR_WEB_SERVER_URL, '/flamenco/jobs/%s/redir' % job_info['_id'])
webbrowser.open_new_tab(url)
# Do a final report. # Do a final report.
if missing_sources: if missing_sources:
names = (ms.name for ms in missing_sources) names = (ms.name for ms in missing_sources)
@@ -193,11 +216,12 @@ class FLAMENCO_OT_render(async_loop.AsyncModalOperatorMixin,
'; '.join(names)) '; '.join(names))
else: else:
self.report({'INFO'}, 'Flamenco job created.') self.report({'INFO'}, 'Flamenco job created.')
self.quit() self.quit()
def quit(self): def quit(self):
super().quit() super().quit()
bpy.context.window_manager.progress_end() bpy.context.window_manager.flamenco_status = 'IDLE'
async def bam_pack(self, filepath: Path) -> (typing.Optional[Path], typing.List[Path]): async def bam_pack(self, filepath: Path) -> (typing.Optional[Path], typing.List[Path]):
"""BAM-packs the blendfile to the destination directory. """BAM-packs the blendfile to the destination directory.
@@ -269,6 +293,8 @@ class FLAMENCO_OT_copy_files(Operator,
from . import bam_interface from . import bam_interface
from ..blender import preferences from ..blender import preferences
context.window_manager.flamenco_status = 'PACKING'
missing_sources = await bam_interface.bam_copy( missing_sources = await bam_interface.bam_copy(
Path(context.blend_data.filepath), Path(context.blend_data.filepath),
Path(preferences().flamenco_job_file_path), Path(preferences().flamenco_job_file_path),
@@ -280,6 +306,10 @@ class FLAMENCO_OT_copy_files(Operator,
self.quit() self.quit()
def quit(self):
super().quit()
bpy.context.window_manager.flamenco_status = 'IDLE'
class FLAMENCO_OT_explore_file_path(Operator): class FLAMENCO_OT_explore_file_path(Operator):
"""Opens the Flamenco job storage path in a file explorer.""" """Opens the Flamenco job storage path in a file explorer."""
@@ -353,7 +383,7 @@ def is_image_type(render_output_type: str) -> bool:
@functools.lru_cache(1) @functools.lru_cache(1)
def _render_output_path( def _render_output_path(
local_project_path: str, local_project_path: str,
blend_filepath: str, blend_filepath: Path,
flamenco_job_output_strip_components: int, flamenco_job_output_strip_components: int,
flamenco_job_output_path: str, flamenco_job_output_path: str,
render_image_format: str, render_image_format: str,
@@ -366,16 +396,15 @@ def _render_output_path(
""" """
project_path = Path(bpy.path.abspath(local_project_path)).resolve() project_path = Path(bpy.path.abspath(local_project_path)).resolve()
blendfile = Path(blend_filepath)
try: try:
proj_rel = blendfile.parent.relative_to(project_path) proj_rel = blend_filepath.parent.relative_to(project_path)
except ValueError: except ValueError:
return None return None
rel_parts = proj_rel.parts[flamenco_job_output_strip_components:] rel_parts = proj_rel.parts[flamenco_job_output_strip_components:]
output_top = Path(flamenco_job_output_path) output_top = Path(flamenco_job_output_path)
dir_components = output_top.joinpath(*rel_parts) / blendfile.stem dir_components = output_top.joinpath(*rel_parts) / blend_filepath.stem
# Blender will have to append the file extensions by itself. # Blender will have to append the file extensions by itself.
if is_image_type(render_image_format): if is_image_type(render_image_format):
@@ -383,9 +412,12 @@ def _render_output_path(
return dir_components / flamenco_render_frame_range return dir_components / flamenco_render_frame_range
def render_output_path(context) -> typing.Optional[PurePath]: def render_output_path(context, filepath: Path=None) -> typing.Optional[PurePath]:
"""Returns the render output path to be sent to Flamenco. """Returns the render output path to be sent to Flamenco.
:param context: the Blender context (used to find Flamenco preferences etc.)
:param filepath: the Path of the blend file to render, or None for the current file.
Returns None when the current blend file is outside the project path. Returns None when the current blend file is outside the project path.
""" """
@@ -394,9 +426,12 @@ def render_output_path(context) -> typing.Optional[PurePath]:
scene = context.scene scene = context.scene
prefs = preferences() prefs = preferences()
if filepath is None:
filepath = Path(context.blend_data.filepath)
return _render_output_path( return _render_output_path(
prefs.attract_project_local_path, prefs.attract_project_local_path,
context.blend_data.filepath, filepath,
prefs.flamenco_job_output_strip_components, prefs.flamenco_job_output_strip_components,
prefs.flamenco_job_output_path, prefs.flamenco_job_output_path,
scene.render.image_settings.file_format, scene.render.image_settings.file_format,
@@ -421,18 +456,18 @@ class FLAMENCO_PT_render(bpy.types.Panel):
layout.prop(context.scene, 'flamenco_render_job_priority') layout.prop(context.scene, 'flamenco_render_job_priority')
layout.prop(context.scene, 'flamenco_render_chunk_size') layout.prop(context.scene, 'flamenco_render_chunk_size')
labeled_row = layout.split(0.2, align=True) labeled_row = layout.split(0.25, align=True)
labeled_row.label('Job type:') labeled_row.label('Job Type:')
labeled_row.prop(context.scene, 'flamenco_render_job_type', text='') labeled_row.prop(context.scene, 'flamenco_render_job_type', text='')
labeled_row = layout.split(0.2, align=True) labeled_row = layout.split(0.25, align=True)
labeled_row.label('Frame range:') labeled_row.label('Frame Range:')
prop_btn_row = labeled_row.row(align=True) prop_btn_row = labeled_row.row(align=True)
prop_btn_row.prop(context.scene, 'flamenco_render_frame_range', text='') prop_btn_row.prop(context.scene, 'flamenco_render_frame_range', text='')
prop_btn_row.operator('flamenco.scene_to_frame_range', text='', icon='ARROW_LEFTRIGHT') prop_btn_row.operator('flamenco.scene_to_frame_range', text='', icon='ARROW_LEFTRIGHT')
readonly_stuff = layout.column(align=True) readonly_stuff = layout.column(align=True)
labeled_row = readonly_stuff.split(0.2, align=True) labeled_row = readonly_stuff.split(0.25, align=True)
labeled_row.label('Storage:') labeled_row.label('Storage:')
prop_btn_row = labeled_row.row(align=True) prop_btn_row = labeled_row.row(align=True)
prop_btn_row.label(prefs.flamenco_job_file_path) prop_btn_row.label(prefs.flamenco_job_file_path)
@@ -440,10 +475,11 @@ class FLAMENCO_PT_render(bpy.types.Panel):
text='', icon='DISK_DRIVE') text='', icon='DISK_DRIVE')
props.path = prefs.flamenco_job_file_path props.path = prefs.flamenco_job_file_path
labeled_row = readonly_stuff.split(0.2, align=True) labeled_row = readonly_stuff.split(0.25, align=True)
labeled_row.label('Output:') labeled_row.label('Output:')
prop_btn_row = labeled_row.row(align=True) prop_btn_row = labeled_row.row(align=True)
render_output = render_output_path(context) render_output = render_output_path(context)
if render_output is None: if render_output is None:
prop_btn_row.label('Unable to render with Flamenco, outside of project directory.') prop_btn_row.label('Unable to render with Flamenco, outside of project directory.')
else: else:
@@ -452,9 +488,17 @@ class FLAMENCO_PT_render(bpy.types.Panel):
text='', icon='DISK_DRIVE') text='', icon='DISK_DRIVE')
props.path = str(render_output.parent) props.path = str(render_output.parent)
layout.operator(FLAMENCO_OT_render.bl_idname, flamenco_status = context.window_manager.flamenco_status
text='Render on Flamenco', if flamenco_status == 'IDLE':
icon='RENDER_ANIMATION') layout.operator(FLAMENCO_OT_render.bl_idname,
text='Render on Flamenco',
icon='RENDER_ANIMATION')
elif flamenco_status == 'PACKING':
layout.label('Flamenco is packing your file + dependencies')
elif flamenco_status == 'COMMUNICATING':
layout.label('Communicating with Flamenco Server')
else:
layout.label('Unknown Flamenco status %s' % flamenco_status)
if not context.scene.render.use_overwrite: if not context.scene.render.use_overwrite:
warnbox = layout.box().column(align=True) warnbox = layout.box().column(align=True)
@@ -463,6 +507,8 @@ class FLAMENCO_PT_render(bpy.types.Panel):
def register(): def register():
from ..utils import redraw
bpy.utils.register_class(FlamencoManagerGroup) bpy.utils.register_class(FlamencoManagerGroup)
bpy.utils.register_class(FLAMENCO_OT_fmanagers) bpy.utils.register_class(FLAMENCO_OT_fmanagers)
bpy.utils.register_class(FLAMENCO_OT_render) bpy.utils.register_class(FLAMENCO_OT_render)
@@ -496,6 +542,17 @@ def register():
description='Higher numbers mean higher priority' description='Higher numbers mean higher priority'
) )
bpy.types.WindowManager.flamenco_status = EnumProperty(
items=[
('IDLE', 'IDLE', 'Not doing anything.'),
('PACKING', 'PACKING', 'BAM-packing all dependencies.'),
('COMMUNICATING', 'COMMUNICATING', 'Communicating with Flamenco Server.'),
],
name='flamenco_status',
default='IDLE',
description='Current status of the Flamenco add-on',
update=redraw)
def unregister(): def unregister():
bpy.utils.unregister_module(__name__) bpy.utils.unregister_module(__name__)
@@ -516,3 +573,7 @@ def unregister():
del bpy.types.Scene.flamenco_render_job_priority del bpy.types.Scene.flamenco_render_job_priority
except AttributeError: except AttributeError:
pass pass
try:
del bpy.types.WindowManager.flamenco_status
except AttributeError:
pass

View File

@@ -227,7 +227,7 @@ setup(
'wheels': BuildWheels}, 'wheels': BuildWheels},
name='blender_cloud', name='blender_cloud',
description='The Blender Cloud addon allows browsing the Blender Cloud from Blender.', description='The Blender Cloud addon allows browsing the Blender Cloud from Blender.',
version='1.5.9999', version='1.5.99999',
author='Sybren A. Stüvel', author='Sybren A. Stüvel',
author_email='sybren@stuvel.eu', author_email='sybren@stuvel.eu',
packages=find_packages('.'), packages=find_packages('.'),