From 73e2fd77e2dcf735149a542a6350d0e55e93f5e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 17 Jun 2016 13:14:57 +0200 Subject: [PATCH] Added reloading of home file after pulling (not implemented pull yet) Pull is easy, we can already download files from Cloud. Had to jump through some hoops to make the reload work reliably, though. --- blender_cloud/async_loop.py | 16 ++++++++++++ blender_cloud/settings_sync.py | 46 +++++++++++++++++++++++++++++----- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/blender_cloud/async_loop.py b/blender_cloud/async_loop.py index 6feed3b..7db5f6e 100644 --- a/blender_cloud/async_loop.py +++ b/blender_cloud/async_loop.py @@ -87,6 +87,16 @@ def ensure_async_loop(): log.debug('Result of starting modal operator is %r', result) +def erase_async_loop(): + global _loop_kicking_operator_running + + log.debug('Erasing async loop') + + loop = asyncio.get_event_loop() + loop.stop() + _loop_kicking_operator_running = False + + class AsyncLoopModalOperator(bpy.types.Operator): bl_idname = 'asyncio.loop' bl_label = 'Runs the asyncio main loop' @@ -115,6 +125,12 @@ class AsyncLoopModalOperator(bpy.types.Operator): def modal(self, context, event): global _loop_kicking_operator_running + # If _loop_kicking_operator_running is set to False, someone called + # erase_async_loop(). This is a signal that we really should stop + # running. + if not _loop_kicking_operator_running: + return {'FINISHED'} + if event.type != 'TIMER': return {'PASS_THROUGH'} diff --git a/blender_cloud/settings_sync.py b/blender_cloud/settings_sync.py index 627ae36..0db5a02 100644 --- a/blender_cloud/settings_sync.py +++ b/blender_cloud/settings_sync.py @@ -123,19 +123,30 @@ class PILLAR_OT_sync(async_loop.AsyncModalOperatorMixin, bpy.types.Operator): self._state = 'QUIT' return - if self.action == 'PUSH': - await self.action_push() - else: - self.report({'ERROR'}, 'Sorry, PULL not implemented yet.') + action = { + 'PUSH': self.action_push, + 'PULL': self.action_pull, + }[self.action] + await action(context) except Exception as ex: self.log.exception('Unexpected exception caught.') self.report({'ERROR'}, 'Unexpected error: %s' % ex) - self._state = 'QUIT' + try: + self._state = 'QUIT' + except ReferenceError: + # This happens after the call to bpy.ops.wm.read_homefile() in action_pull(). + # That call erases the StructRNA of this operator. As a result, it no longer + # runs as a modal operator. The currently running Python code is allowed + # to finish, though. + pass - async def action_push(self): + async def action_push(self, context): """Sends files to the Pillar server.""" + self.log.info('Saved user preferences to disk before pushing to cloud.') + bpy.ops.wm.save_userpref() + config_dir = pathlib.Path(bpy.utils.user_resource('CONFIG')) for fname in SETTINGS_FILES_TO_UPLOAD: @@ -149,6 +160,29 @@ class PILLAR_OT_sync(async_loop.AsyncModalOperatorMixin, bpy.types.Operator): self.report({'INFO'}, 'Settings pushed to Blender Cloud.') + async def action_pull(self, context): + """Loads files from the Pillar server.""" + + # Refuse to start if the file hasn't been saved. + if context.blend_data.is_dirty: + self.report({'ERROR'}, 'Please save your Blend file before pulling' + ' settings from the Blender Cloud.') + return + + self.report({'WARNING'}, 'Settings pulled from Blender Cloud, reloading.') + + # This call is tricy, as Blender destroys this modal operator's StructRNA. + # However, the Python code keeps running, so we have to be very careful + # what we do afterwards. + log.warning('Reloading home files (i.e. userprefs and startup)') + bpy.ops.wm.read_homefile() + + # The above call stops any running modal operator, so we have to be + # very careful with our asynchronous loop. Since it didn't stop by + # its own accord (because the current async task is still running), + # we need to shut it down forcefully. + async_loop.erase_async_loop() + async def find_sync_group_id(self) -> pillarsdk.Node: """Finds the group node in which to store sync assets.