Compare commits

..

16 Commits

Author SHA1 Message Date
68d2fc8e42 Fixed scene update post handler disappearing. 2016-11-08 15:11:27 +01:00
2de4a8e87c Removed beta warning 2016-11-08 14:09:33 +01:00
39b2bacdcc Bumped version to 1.5.0 2016-11-08 14:08:15 +01:00
a1416f99dd Added script to bump versions in all the right places.
Must be called with major.minor.micro revision number (so 3 components).

Signed-off-by: Sybren A. Stüvel <sybren@stuvel.eu>
2016-11-08 14:08:03 +01:00
0fa7d60028 Keep Attract & Image Share prefs enabled when sync error is shown. 2016-11-08 10:14:26 +01:00
c1b6480f9a Texture browser: Also draw a dark background for INITIALIZING state 2016-11-08 10:14:26 +01:00
56353d4177 More captialisation of button labels. 2016-11-07 13:39:33 +01:00
469a9318af Also allow meta strips to be considered "shots" in Attract. 2016-11-07 13:39:14 +01:00
e265081131 Change wiki URL to HTTPS 2016-11-07 11:23:30 +01:00
115eea82c6 Prevent errors when there is no sequencer in the current scene 2016-11-04 17:47:27 +01:00
900068a6f5 Slight improvements to Attract shot delete operator 2016-11-04 17:47:13 +01:00
c8229500d1 Bugfix 2016-11-04 16:42:26 +01:00
65ff9da428 Attract: Draw conflicts more subtly 2016-11-04 16:12:13 +01:00
fcba8a2e0f Attract: Compute strip conflicts in scene update handler 2016-11-04 16:11:59 +01:00
7ef5e522f8 Attract thumbs: only use middle frame when current frame not on shot
This now also applies when rendering multiple shot thumbnails.
2016-11-04 15:45:23 +01:00
ae570e5907 Allow undeletion of shots by relinking to the edit. 2016-11-04 13:44:12 +01:00
7 changed files with 91 additions and 36 deletions

View File

@@ -21,15 +21,14 @@
bl_info = {
'name': 'Blender Cloud',
"author": "Sybren A. Stüvel, Francesco Siddi, Inês Almeida, Antony Riakiotakis",
'version': (1, 4, 999),
'version': (1, 5, 0),
'blender': (2, 77, 0),
'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 '
'and Blender 2.77a or newer.',
'wiki_url': 'http://wiki.blender.org/index.php/Extensions:2.6/Py/'
'wiki_url': 'https://wiki.blender.org/index.php/Extensions:2.6/Py/'
'Scripts/System/BlenderCloud',
'category': 'System',
'warning': 'This is a beta version; the first to support Attract.'
}
import logging

View File

@@ -127,18 +127,31 @@ def shot_id_use(strips):
return ids_in_use
def compute_strip_conflicts(context):
def compute_strip_conflicts(scene):
"""Sets the strip property atc_object_id_conflict for each strip."""
ids_in_use = shot_id_use(context.scene.sequence_editor.sequences_all)
if not scene or not scene.sequence_editor or not scene.sequence_editor.sequences_all:
return
tag_redraw = False
ids_in_use = shot_id_use(scene.sequence_editor.sequences_all)
for strips in ids_in_use.values():
is_conflict = len(strips) > 1
for strip in strips:
if strip.atc_object_id_conflict != is_conflict:
tag_redraw = True
strip.atc_object_id_conflict = is_conflict
if tag_redraw:
draw.tag_redraw_all_sequencer_editors()
return ids_in_use
@bpy.app.handlers.persistent
def scene_update_post_handler(scene):
compute_strip_conflicts(scene)
class ToolsPanel(Panel):
bl_label = 'Attract'
bl_space_type = 'SEQUENCE_EDITOR'
@@ -152,7 +165,7 @@ class ToolsPanel(Panel):
def draw(self, context):
strip = active_strip(context)
layout = self.layout
strip_types = {'MOVIE', 'IMAGE'}
strip_types = {'MOVIE', 'IMAGE', 'META'}
selshots = list(selected_shots(context))
if strip and strip.type in strip_types and strip.atc_object_id:
@@ -197,9 +210,9 @@ class ToolsPanel(Panel):
elif context.selected_sequences:
if len(context.selected_sequences) > 1:
noun = 'selected strips'
noun = 'Selected Strips'
else:
noun = 'this strip'
noun = 'This Strip'
layout.operator(AttractShotSubmitSelected.bl_idname,
text='Submit %s as New Shot' % noun)
layout.operator('attract.shot_relink')
@@ -282,7 +295,6 @@ class AttractOperatorMixin:
strip.atc_notes = node['properties']['notes']
strip.atc_status = node['properties']['status']
compute_strip_conflicts(bpy.context)
draw.tag_redraw_all_sequencer_editors()
def submit_update(self, strip):
@@ -308,6 +320,11 @@ class AttractOperatorMixin:
def relink(self, strip, atc_object_id, *, refresh=False):
from .. import pillar
# The node may have been deleted, so we need to send a 'relink' before we try
# to fetch the node itself.
node = Node({'_id': atc_object_id})
pillar.sync_call(node.patch, {'op': 'relink'})
try:
node = pillar.sync_call(Node.find, atc_object_id, caching=False)
except (sdk_exceptions.ResourceNotFound, sdk_exceptions.MethodNotAllowed):
@@ -317,7 +334,6 @@ class AttractOperatorMixin:
strip.atc_is_synced = False
return {'CANCELLED'}
pillar.sync_call(node.patch, {'op': 'relink'})
strip.atc_is_synced = True
if not refresh:
strip.atc_name = node.name
@@ -327,8 +343,6 @@ class AttractOperatorMixin:
strip.atc_status = node.properties.status
strip.atc_notes = node.properties.notes or ''
strip.atc_description = node.description or ''
compute_strip_conflicts(bpy.context)
draw.tag_redraw_all_sequencer_editors()
@@ -437,12 +451,13 @@ class AttractShotDelete(AttractOperatorMixin, Operator):
return {'FINISHED'}
def invoke(self, context, event):
self.confirm = False
return context.window_manager.invoke_props_dialog(self)
def draw(self, context):
layout = self.layout
col = layout.column()
col.prop(self, 'confirm', text="I hereby confirm I want to delete this shot from The Edit.")
col.prop(self, 'confirm', text="I hereby confirm: delete this shot from The Edit.")
class AttractStripUnlink(AttractOperatorMixin, Operator):
@@ -464,7 +479,7 @@ class AttractStripUnlink(AttractOperatorMixin, Operator):
# For all Object IDs that are no longer in use in the edit, let Attract know.
# This should be done with care, as the shot could have been attached to multiple
# strips.
id_to_shots = compute_strip_conflicts(context)
id_to_shots = compute_strip_conflicts(context.scene)
for oid in unlinked_ids:
if len(id_to_shots[oid]):
# Still in use
@@ -639,9 +654,13 @@ class ATTRACT_OT_make_shot_thumbnail(AttractOperatorMixin,
@contextlib.contextmanager
def temporary_current_frame(self, context):
"""Allows the context to set the scene current frame, restores it on exit.
Yields the initial current frame, so it can be used for reference in the context.
"""
current_frame = context.scene.frame_current
try:
yield
yield current_frame
finally:
context.scene.frame_current = current_frame
@@ -649,19 +668,25 @@ class ATTRACT_OT_make_shot_thumbnail(AttractOperatorMixin,
nr_of_strips = len(context.selected_sequences)
do_multishot = nr_of_strips > 1
with self.temporary_current_frame(context):
with self.temporary_current_frame(context) as original_curframe:
# The multishot and singleshot branches do pretty much the same thing,
# but report differently to the user.
if do_multishot:
context.window_manager.progress_begin(0, nr_of_strips)
try:
self.report({'INFO'}, 'Rendering thumbnails for %i selected shots.' %
nr_of_strips)
for idx, strip in enumerate(context.selected_sequences):
strips = sorted(context.selected_sequences, key=self.by_frame)
for idx, strip in enumerate(strips):
context.window_manager.progress_update(idx)
# For multi-shot we can't just use the current frame (each thumb would be
# identical), so instead we use the middle frame. The first/last frames
# cannot be reliably used due to transitions with other shots.
self.set_middle_frame(context, strip)
# Pick the middle frame, except for the strip the original current frame
# marker was over.
if not self.strip_contains(strip, original_curframe):
self.set_middle_frame(context, strip)
else:
context.scene.frame_set(original_curframe)
await self.thumbnail_strip(context, strip)
if self._state == 'QUIT':
@@ -672,7 +697,7 @@ class ATTRACT_OT_make_shot_thumbnail(AttractOperatorMixin,
else:
strip = active_strip(context)
if not strip.frame_final_start <= context.scene.frame_current <= strip.frame_final_end:
if not self.strip_contains(strip, original_curframe):
self.report({'WARNING'}, 'Rendering middle frame as thumbnail for active shot.')
self.set_middle_frame(context, strip)
else:
@@ -692,12 +717,27 @@ class ATTRACT_OT_make_shot_thumbnail(AttractOperatorMixin,
self.report({'INFO'}, 'Thumbnail uploaded to Attract')
self.quit()
def set_middle_frame(self, context, strip):
@staticmethod
def strip_contains(strip, framenr: int) -> bool:
"""Returns True iff the strip covers the given frame number"""
return strip.frame_final_start <= framenr <= strip.frame_final_end
@staticmethod
def set_middle_frame(context, strip):
"""Sets the current frame to the middle frame of the strip."""
middle = round((strip.frame_final_start + strip.frame_final_end) / 2)
context.scene.frame_set(middle)
@staticmethod
def by_frame(sequence_strip) -> int:
"""Returns the start frame number of the sequence strip.
This can be used for sorting strips by time.
"""
return sequence_strip.frame_final_start
async def thumbnail_strip(self, context, strip):
atc_object_id = getattr(strip, 'atc_object_id', None)
if not atc_object_id:
@@ -829,11 +869,14 @@ def register():
bpy.utils.register_class(ATTRACT_OT_open_meta_blendfile)
bpy.utils.register_class(ATTRACT_OT_shot_open_in_browser)
bpy.utils.register_class(ATTRACT_OT_make_shot_thumbnail)
bpy.app.handlers.scene_update_post.append(scene_update_post_handler)
draw.callback_enable()
def unregister():
draw.callback_disable()
bpy.app.handlers.scene_update_post.remove(scene_update_post_handler)
bpy.utils.unregister_module(__name__)
del bpy.types.Sequence.atc_is_synced
del bpy.types.Sequence.atc_object_id

View File

@@ -16,7 +16,7 @@
#
# ##### END GPL LICENSE BLOCK #####
# <pep8-80 compliant>
# <pep8 compliant>
import bpy
import logging
@@ -87,8 +87,7 @@ def draw_strip_conflict(strip_coords, pixel_size_x):
# Always draw the full rectangle, the conflict should be resolved and thus stand out.
bgl.glColor3f(*CONFLICT_COLOUR)
bgl.glLineWidth(3)
bgl.glPointSize(10)
bgl.glLineWidth(2)
bgl.glBegin(bgl.GL_LINE_LOOP)
bgl.glVertex2f(s_x1, s_y1)
@@ -97,13 +96,6 @@ def draw_strip_conflict(strip_coords, pixel_size_x):
bgl.glVertex2f(s_x1, s_y2)
bgl.glEnd()
bgl.glBegin(bgl.GL_POINTS)
bgl.glVertex2f(s_x1, s_y1)
bgl.glVertex2f(s_x2, s_y1)
bgl.glVertex2f(s_x2, s_y2)
bgl.glVertex2f(s_x1, s_y2)
bgl.glEnd()
bgl.glPopAttrib()

View File

@@ -285,12 +285,10 @@ class BlenderCloudPreferences(AddonPreferences):
# Image Share stuff
share_box = layout.box()
share_box.label('Image Sharing on Blender Cloud', icon_value=icon('CLOUD'))
share_box.enabled = msg_icon != 'ERROR'
share_box.prop(self, 'open_browser_after_share')
# Attract stuff
attract_box = layout.box()
attract_box.enabled = msg_icon != 'ERROR'
self.draw_attract_buttons(attract_box, self.attract_project)
def draw_subscribe_button(self, layout):

View File

@@ -553,6 +553,7 @@ class BlenderCloudBrowser(pillar.PillarOperatorMixin,
"""Draws the GUI with OpenGL."""
drawers = {
'INITIALIZING': self._draw_initializing,
'CHECKING_CREDENTIALS': self._draw_checking_credentials,
'BROWSING': self._draw_browser,
'DOWNLOADING_TEXTURE': self._draw_downloading,
@@ -653,6 +654,13 @@ class BlenderCloudBrowser(pillar.PillarOperatorMixin,
'Checking login credentials',
(0.0, 0.0, 0.2, 0.6))
def _draw_initializing(self, context):
"""OpenGL drawing code for the INITIALIZING state."""
self._draw_text_on_colour(context,
'Initializing',
(0.0, 0.0, 0.2, 0.6))
def _draw_text_on_colour(self, context, text, bgcolour):
content_height, content_width = self._window_size(context)
bgl.glEnable(bgl.GL_BLEND)

View File

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

15
update_version.sh Executable file
View File

@@ -0,0 +1,15 @@
#!/bin/bash
if [ -z "$1" ]; then
echo "Usage: $0 new-version" >&2
exit 1
fi
BL_INFO_VER=$(echo "$1" | sed 's/\./, /g')
sed "s/version='[^']*'/version='$1'/" -i setup.py
sed "s/'version': ([^)]*)/'version': ($BL_INFO_VER)/" -i blender_cloud/__init__.py
git diff
echo
echo "Don't forget to commit!"