Store project-specific settings in the preferences.

This stores project-specific settings, such as filesystem paths, for each
project, and restores those settings when the project is selected again.
Does not touch settings that haven't been set for the newly selected
project.
This commit is contained in:
Sybren A. Stüvel 2018-01-02 16:42:37 +01:00
parent 0a7e7195a2
commit ba14c33b6d
4 changed files with 126 additions and 38 deletions

View File

@ -88,9 +88,10 @@ def register():
settings_sync = reload_mod('settings_sync') settings_sync = reload_mod('settings_sync')
image_sharing = reload_mod('image_sharing') image_sharing = reload_mod('image_sharing')
blender = reload_mod('blender') blender = reload_mod('blender')
project_specific = reload_mod('project_specific')
else: else:
from . import (blender, texture_browser, async_loop, settings_sync, blendfile, home_project, from . import (blender, texture_browser, async_loop, settings_sync, blendfile, home_project,
image_sharing, attract, flamenco) image_sharing, attract, flamenco, project_specific)
async_loop.setup_asyncio_executor() async_loop.setup_asyncio_executor()
async_loop.register() async_loop.register()
@ -102,7 +103,7 @@ def register():
image_sharing.register() image_sharing.register()
blender.register() blender.register()
blender.handle_project_update() project_specific.handle_project_update()
def _monkey_patch_requests(): def _monkey_patch_requests():

View File

@ -30,7 +30,7 @@ from bpy.types import AddonPreferences, Operator, WindowManager, Scene, Property
from bpy.props import StringProperty, EnumProperty, PointerProperty, BoolProperty, IntProperty from bpy.props import StringProperty, EnumProperty, PointerProperty, BoolProperty, IntProperty
import rna_prop_ui import rna_prop_ui
from . import pillar, async_loop, flamenco from . import pillar, async_loop, flamenco, project_specific
from .utils import pyside_cache, redraw from .utils import pyside_cache, redraw
PILLAR_WEB_SERVER_URL = os.environ.get('BCLOUD_SERVER', 'https://cloud.blender.org/') PILLAR_WEB_SERVER_URL = os.environ.get('BCLOUD_SERVER', 'https://cloud.blender.org/')
@ -38,7 +38,6 @@ PILLAR_SERVER_URL = '%sapi/' % PILLAR_WEB_SERVER_URL
ADDON_NAME = 'blender_cloud' ADDON_NAME = 'blender_cloud'
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
icons = None icons = None
@ -140,30 +139,6 @@ def project_extensions(project_id) -> set:
return set(proj.get('enabled_for', ())) return set(proj.get('enabled_for', ()))
def handle_project_update(_=None, _2=None):
"""Handles changing projects, which may cause extensions to be disabled/enabled.
Ignores arguments so that it can be used as property update callback.
"""
project_id = preferences().project.project
log.info('Updating internal state to reflect extensions enabled on current project %s.',
project_id)
project_extensions.cache_clear()
from blender_cloud import attract, flamenco
attract.deactivate()
flamenco.deactivate()
enabled_for = project_extensions(project_id)
log.info('Project extensions: %s', enabled_for)
if 'attract' in enabled_for:
attract.activate()
if 'flamenco' in enabled_for:
flamenco.activate()
class BlenderCloudProjectGroup(PropertyGroup): class BlenderCloudProjectGroup(PropertyGroup):
status = EnumProperty( status = EnumProperty(
items=[ items=[
@ -178,7 +153,7 @@ class BlenderCloudProjectGroup(PropertyGroup):
items=bcloud_available_projects, items=bcloud_available_projects,
name='Cloud project', name='Cloud project',
description='Which Blender Cloud project to work with', description='Which Blender Cloud project to work with',
update=handle_project_update update=project_specific.handle_project_update
) )
# List of projects is stored in 'available_projects' ID property, # List of projects is stored in 'available_projects' ID property,
@ -190,13 +165,13 @@ class BlenderCloudProjectGroup(PropertyGroup):
@available_projects.setter @available_projects.setter
def available_projects(self, new_projects): def available_projects(self, new_projects):
self['available_projects'] = new_projects self['available_projects'] = new_projects
handle_project_update() project_specific.handle_project_update()
class BlenderCloudPreferences(AddonPreferences): class BlenderCloudPreferences(AddonPreferences):
bl_idname = ADDON_NAME bl_idname = ADDON_NAME
# The following two properties are read-only to limit the scope of the # The following property is read-only to limit the scope of the
# addon and allow for proper testing within this scope. # addon and allow for proper testing within this scope.
pillar_server = StringProperty( pillar_server = StringProperty(
name='Blender Cloud Server', name='Blender Cloud Server',
@ -225,24 +200,32 @@ class BlenderCloudPreferences(AddonPreferences):
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',
default='//../') default='//../',
update=project_specific.store,
)
flamenco_manager = PointerProperty(type=flamenco.FlamencoManagerGroup) flamenco_manager = PointerProperty(type=flamenco.FlamencoManagerGroup)
flamenco_exclude_filter = StringProperty( flamenco_exclude_filter = StringProperty(
name='File Exclude Filter', name='File Exclude Filter',
description='Filter like "*.abc;*.mkv" to prevent certain files to be packed ' description='Filter like "*.abc;*.mkv" to prevent certain files to be packed '
'into the output directory', 'into the output directory',
default='') default='',
update=project_specific.store,
)
flamenco_job_file_path = StringProperty( flamenco_job_file_path = StringProperty(
name='Job Storage Path', name='Job Storage 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=tempfile.gettempdir()) default=tempfile.gettempdir(),
update=project_specific.store,
)
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=tempfile.gettempdir()) default=tempfile.gettempdir(),
update=project_specific.store,
)
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 '
@ -251,11 +234,12 @@ class BlenderCloudPreferences(AddonPreferences):
min=0, min=0,
default=0, default=0,
soft_max=4, soft_max=4,
update=project_specific.store,
) )
flamenco_open_browser_after_submit = BoolProperty( flamenco_open_browser_after_submit = BoolProperty(
name='Open Browser after Submitting Job', name='Open Browser after Submitting Job',
description='When enabled, Blender will open a webbrowser', description='When enabled, Blender will open a webbrowser',
default=True default=True,
) )
def draw(self, context): def draw(self, context):

View File

@ -42,7 +42,7 @@ import bpy
from bpy.types import AddonPreferences, Operator, WindowManager, Scene, PropertyGroup from bpy.types import AddonPreferences, Operator, WindowManager, Scene, PropertyGroup
from bpy.props import StringProperty, EnumProperty, PointerProperty, BoolProperty, IntProperty from bpy.props import StringProperty, EnumProperty, PointerProperty, BoolProperty, IntProperty
from .. import async_loop, pillar from .. import async_loop, pillar, project_specific
from ..utils import pyside_cache, redraw from ..utils import pyside_cache, redraw
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -67,7 +67,9 @@ class FlamencoManagerGroup(PropertyGroup):
manager = EnumProperty( manager = EnumProperty(
items=available_managers, items=available_managers,
name='Flamenco Manager', name='Flamenco Manager',
description='Which Flamenco Manager to use for jobs') description='Which Flamenco Manager to use for jobs',
update=project_specific.store,
)
status = EnumProperty( status = EnumProperty(
items=[ items=[

View File

@ -0,0 +1,101 @@
"""Handle saving and loading project-specific settings."""
import logging
# Names of BlenderCloudPreferences properties that are both project-specific
# and simple enough to store directly in a dict.
PROJECT_SPECIFIC_SIMPLE_PROPS = (
'cloud_project_local_path',
'flamenco_exclude_filter',
'flamenco_job_file_path',
'flamenco_job_output_path',
'flamenco_job_output_strip_components'
)
log = logging.getLogger(__name__)
project_settings_loading = False
def handle_project_update(_=None, _2=None):
"""Handles changing projects, which may cause extensions to be disabled/enabled.
Ignores arguments so that it can be used as property update callback.
"""
from .blender import preferences, project_extensions
global project_settings_loading
project_settings_loading = True
try:
prefs = preferences()
project_id = prefs.project.project
log.info('Updating internal state to reflect extensions enabled on current project %s.',
project_id)
project_extensions.cache_clear()
from blender_cloud import attract, flamenco
attract.deactivate()
flamenco.deactivate()
enabled_for = project_extensions(project_id)
log.info('Project extensions: %s', enabled_for)
if 'attract' in enabled_for:
attract.activate()
if 'flamenco' in enabled_for:
flamenco.activate()
# Load project-specific settings from the last time we visited this project.
ps = prefs.get('project_settings', {}).get(project_id, {})
if not ps:
log.debug('no project-specific settings are available, not touching options')
return
if log.isEnabledFor(logging.DEBUG):
from pprint import pformat
log.debug('loading project-specific settings:\n%s', pformat(ps.to_dict()))
for name in PROJECT_SPECIFIC_SIMPLE_PROPS:
if name in ps and hasattr(prefs, name):
setattr(prefs, name, ps[name])
if ps.get('flamenco_manager'):
prefs.flamenco_manager.manager = ps['flamenco_manager']
log.debug('setting flamenco manager to %s', ps['flamenco_manager'])
finally:
project_settings_loading = False
def store(_=None, _2=None):
"""Remember project-specific settings as soon as one of them changes.
Ignores arguments so that it can be used as property update callback.
No-op when project_settings_loading=True, to prevent saving project-
specific settings while they are actually being loaded.
"""
from .blender import preferences
global project_settings_loading
if project_settings_loading:
return
prefs = preferences()
project_id = prefs.project.project
all_settings = prefs.get('project_settings', {})
ps = all_settings.get(project_id, {})
for name in PROJECT_SPECIFIC_SIMPLE_PROPS:
ps[name] = getattr(prefs, name)
ps['flamenco_manager'] = prefs.flamenco_manager.manager
if log.isEnabledFor(logging.DEBUG):
from pprint import pformat
if hasattr(ps, 'to_dict'):
ps_to_log = ps.to_dict()
else:
ps_to_log = ps
log.debug('saving project-specific settings:\n%s', pformat(ps_to_log))
all_settings[project_id] = ps
prefs['project_settings'] = all_settings