Remove code to support Blender 2.79 and older
This commit is contained in:
parent
8b49c5505e
commit
14778e5c08
@ -47,15 +47,11 @@ if "bpy" in locals():
|
||||
pillar = importlib.reload(pillar)
|
||||
async_loop = importlib.reload(async_loop)
|
||||
blender = importlib.reload(blender)
|
||||
compatibility = importlib.reload(compatibility)
|
||||
else:
|
||||
import bpy
|
||||
|
||||
if bpy.app.version < (2, 80):
|
||||
from . import draw_27 as draw
|
||||
else:
|
||||
from . import draw
|
||||
from .. import pillar, async_loop, blender, compatibility
|
||||
from . import draw
|
||||
from .. import pillar, async_loop, blender
|
||||
|
||||
import bpy
|
||||
import pillarsdk
|
||||
@ -266,7 +262,7 @@ class ATTRACT_PT_tools(AttractPollMixin, Panel):
|
||||
)
|
||||
|
||||
# Group more dangerous operations.
|
||||
dangerous_sub = layout.split(**compatibility.factor(0.6), align=True)
|
||||
dangerous_sub = layout.split(factor=0.6, align=True)
|
||||
dangerous_sub.operator(
|
||||
"attract.strip_unlink", text="Unlink %s" % noun, icon="PANEL_CLOSE"
|
||||
)
|
||||
@ -433,12 +429,11 @@ class ATTRACT_OT_shot_fetch_update(AttractOperatorMixin, Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@compatibility.convert_properties
|
||||
class ATTRACT_OT_shot_relink(AttractOperatorMixin, Operator):
|
||||
bl_idname = "attract.shot_relink"
|
||||
bl_label = "Relink With Attract"
|
||||
|
||||
strip_atc_object_id = bpy.props.StringProperty()
|
||||
strip_atc_object_id: bpy.props.StringProperty()
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
@ -505,13 +500,12 @@ class ATTRACT_OT_shot_open_in_browser(AttractOperatorMixin, Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@compatibility.convert_properties
|
||||
class ATTRACT_OT_shot_delete(AttractOperatorMixin, Operator):
|
||||
bl_idname = "attract.shot_delete"
|
||||
bl_label = "Delete Shot"
|
||||
bl_description = "Remove this shot from Attract"
|
||||
|
||||
confirm = bpy.props.BoolProperty(name="confirm")
|
||||
confirm: bpy.props.BoolProperty(name="confirm")
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
@ -971,13 +965,12 @@ class ATTRACT_OT_copy_id_to_clipboard(AttractOperatorMixin, Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@compatibility.convert_properties
|
||||
class ATTRACT_OT_project_open_in_browser(Operator):
|
||||
bl_idname = "attract.project_open_in_browser"
|
||||
bl_label = "Open Project in Browser"
|
||||
bl_description = "Opens a webbrowser to show the project in Attract"
|
||||
|
||||
project_id = bpy.props.StringProperty(name="Project ID", default="")
|
||||
project_id: bpy.props.StringProperty(name="Project ID", default="")
|
||||
|
||||
def execute(self, context):
|
||||
import webbrowser
|
||||
|
@ -1,189 +0,0 @@
|
||||
# ##### 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>
|
||||
|
||||
import bpy
|
||||
import logging
|
||||
import collections
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
strip_status_colour = {
|
||||
None: (0.7, 0.7, 0.7),
|
||||
"approved": (0.6392156862745098, 0.8784313725490196, 0.30196078431372547),
|
||||
"final": (0.9058823529411765, 0.9607843137254902, 0.8274509803921568),
|
||||
"in_progress": (1.0, 0.7450980392156863, 0.0),
|
||||
"on_hold": (0.796078431372549, 0.6196078431372549, 0.08235294117647059),
|
||||
"review": (0.8941176470588236, 0.9607843137254902, 0.9764705882352941),
|
||||
"todo": (1.0, 0.5019607843137255, 0.5019607843137255),
|
||||
}
|
||||
|
||||
CONFLICT_COLOUR = (0.576, 0.118, 0.035) # RGB tuple
|
||||
|
||||
|
||||
def get_strip_rectf(strip):
|
||||
# Get x and y in terms of the grid's frames and channels
|
||||
x1 = strip.frame_final_start
|
||||
x2 = strip.frame_final_end
|
||||
y1 = strip.channel + 0.2
|
||||
y2 = strip.channel - 0.2 + 1
|
||||
|
||||
return x1, y1, x2, y2
|
||||
|
||||
|
||||
def draw_underline_in_strip(strip_coords, pixel_size_x, color):
|
||||
from bgl import glColor4f, glRectf, glEnable, glDisable, GL_BLEND
|
||||
import bgl
|
||||
|
||||
context = bpy.context
|
||||
|
||||
# Strip coords
|
||||
s_x1, s_y1, s_x2, s_y2 = strip_coords
|
||||
|
||||
# be careful not to draw over the current frame line
|
||||
cf_x = context.scene.frame_current_final
|
||||
|
||||
bgl.glPushAttrib(bgl.GL_COLOR_BUFFER_BIT | bgl.GL_LINE_BIT)
|
||||
|
||||
glColor4f(*color)
|
||||
glEnable(GL_BLEND)
|
||||
bgl.glLineWidth(2)
|
||||
bgl.glBegin(bgl.GL_LINES)
|
||||
|
||||
bgl.glVertex2f(s_x1, s_y1)
|
||||
if s_x1 < cf_x < s_x2:
|
||||
# Bad luck, the line passes our strip
|
||||
bgl.glVertex2f(cf_x - pixel_size_x, s_y1)
|
||||
bgl.glVertex2f(cf_x + pixel_size_x, s_y1)
|
||||
bgl.glVertex2f(s_x2, s_y1)
|
||||
|
||||
bgl.glEnd()
|
||||
bgl.glPopAttrib()
|
||||
|
||||
|
||||
def draw_strip_conflict(strip_coords, pixel_size_x):
|
||||
"""Draws conflicting states between strips."""
|
||||
|
||||
import bgl
|
||||
|
||||
s_x1, s_y1, s_x2, s_y2 = strip_coords
|
||||
bgl.glPushAttrib(bgl.GL_COLOR_BUFFER_BIT | bgl.GL_LINE_BIT)
|
||||
|
||||
# Always draw the full rectangle, the conflict should be resolved and thus stand out.
|
||||
bgl.glColor3f(*CONFLICT_COLOUR)
|
||||
bgl.glLineWidth(2)
|
||||
|
||||
bgl.glBegin(bgl.GL_LINE_LOOP)
|
||||
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()
|
||||
|
||||
|
||||
def draw_callback_px():
|
||||
context = bpy.context
|
||||
|
||||
if not context.scene.sequence_editor:
|
||||
return
|
||||
|
||||
from . import shown_strips
|
||||
|
||||
region = context.region
|
||||
xwin1, ywin1 = region.view2d.region_to_view(0, 0)
|
||||
xwin2, ywin2 = region.view2d.region_to_view(region.width, region.height)
|
||||
one_pixel_further_x, one_pixel_further_y = region.view2d.region_to_view(1, 1)
|
||||
pixel_size_x = one_pixel_further_x - xwin1
|
||||
|
||||
strips = shown_strips(context)
|
||||
|
||||
for strip in strips:
|
||||
if not strip.atc_object_id:
|
||||
continue
|
||||
|
||||
# Get corners (x1, y1), (x2, y2) of the strip rectangle in px region coords
|
||||
strip_coords = get_strip_rectf(strip)
|
||||
|
||||
# check if any of the coordinates are out of bounds
|
||||
if (
|
||||
strip_coords[0] > xwin2
|
||||
or strip_coords[2] < xwin1
|
||||
or strip_coords[1] > ywin2
|
||||
or strip_coords[3] < ywin1
|
||||
):
|
||||
continue
|
||||
|
||||
# Draw
|
||||
status = strip.atc_status
|
||||
if status in strip_status_colour:
|
||||
color = strip_status_colour[status]
|
||||
else:
|
||||
color = strip_status_colour[None]
|
||||
|
||||
alpha = 1.0 if strip.atc_is_synced else 0.5
|
||||
|
||||
draw_underline_in_strip(strip_coords, pixel_size_x, color + (alpha,))
|
||||
if strip.atc_is_synced and strip.atc_object_id_conflict:
|
||||
draw_strip_conflict(strip_coords, pixel_size_x)
|
||||
|
||||
|
||||
def tag_redraw_all_sequencer_editors():
|
||||
context = bpy.context
|
||||
|
||||
# Py cant access notifiers
|
||||
for window in context.window_manager.windows:
|
||||
for area in window.screen.areas:
|
||||
if area.type == "SEQUENCE_EDITOR":
|
||||
for region in area.regions:
|
||||
if region.type == "WINDOW":
|
||||
region.tag_redraw()
|
||||
|
||||
|
||||
# This is a list so it can be changed instead of set
|
||||
# if it is only changed, it does not have to be declared as a global everywhere
|
||||
cb_handle = []
|
||||
|
||||
|
||||
def callback_enable():
|
||||
if cb_handle:
|
||||
return
|
||||
|
||||
cb_handle[:] = (
|
||||
bpy.types.SpaceSequenceEditor.draw_handler_add(
|
||||
draw_callback_px, (), "WINDOW", "POST_VIEW"
|
||||
),
|
||||
)
|
||||
|
||||
tag_redraw_all_sequencer_editors()
|
||||
|
||||
|
||||
def callback_disable():
|
||||
if not cb_handle:
|
||||
return
|
||||
|
||||
try:
|
||||
bpy.types.SpaceSequenceEditor.draw_handler_remove(cb_handle[0], "WINDOW")
|
||||
except ValueError:
|
||||
# Thrown when already removed.
|
||||
pass
|
||||
cb_handle.clear()
|
||||
|
||||
tag_redraw_all_sequencer_editors()
|
@ -36,7 +36,7 @@ from bpy.props import (
|
||||
)
|
||||
import rna_prop_ui
|
||||
|
||||
from . import compatibility, pillar, async_loop, flamenco, project_specific
|
||||
from . import pillar, async_loop, flamenco, project_specific
|
||||
from .utils import pyside_cache, redraw
|
||||
|
||||
PILLAR_WEB_SERVER_URL = os.environ.get("BCLOUD_SERVER", "https://cloud.blender.org/")
|
||||
@ -58,9 +58,8 @@ def blender_syncable_versions(self, context):
|
||||
return [(v, v, "") for v in versions]
|
||||
|
||||
|
||||
@compatibility.convert_properties
|
||||
class SyncStatusProperties(PropertyGroup):
|
||||
status = EnumProperty(
|
||||
status: EnumProperty(
|
||||
items=[
|
||||
("NONE", "NONE", "We have done nothing at all yet."),
|
||||
(
|
||||
@ -75,14 +74,14 @@ class SyncStatusProperties(PropertyGroup):
|
||||
update=redraw,
|
||||
)
|
||||
|
||||
version = EnumProperty(
|
||||
version: EnumProperty(
|
||||
items=blender_syncable_versions,
|
||||
name="Version of Blender from which to pull",
|
||||
description="Version of Blender from which to pull",
|
||||
)
|
||||
|
||||
message = StringProperty(name="message", update=redraw)
|
||||
level = EnumProperty(
|
||||
message: StringProperty(name="message", update=redraw)
|
||||
level: EnumProperty(
|
||||
items=[
|
||||
("INFO", "INFO", ""),
|
||||
("WARNING", "WARNING", ""),
|
||||
@ -152,9 +151,8 @@ def project_extensions(project_id) -> set:
|
||||
return set(proj.get("enabled_for", ()))
|
||||
|
||||
|
||||
@compatibility.convert_properties
|
||||
class BlenderCloudProjectGroup(PropertyGroup):
|
||||
status = EnumProperty(
|
||||
status: EnumProperty(
|
||||
items=[
|
||||
("NONE", "NONE", "We have done nothing at all yet"),
|
||||
(
|
||||
@ -168,7 +166,7 @@ class BlenderCloudProjectGroup(PropertyGroup):
|
||||
update=redraw,
|
||||
)
|
||||
|
||||
project = EnumProperty(
|
||||
project: EnumProperty(
|
||||
items=bcloud_available_projects,
|
||||
name="Cloud project",
|
||||
description="Which Blender Cloud project to work with",
|
||||
@ -187,26 +185,25 @@ class BlenderCloudProjectGroup(PropertyGroup):
|
||||
project_specific.handle_project_update()
|
||||
|
||||
|
||||
@compatibility.convert_properties
|
||||
class BlenderCloudPreferences(AddonPreferences):
|
||||
bl_idname = ADDON_NAME
|
||||
|
||||
# The following property is read-only to limit the scope of the
|
||||
# addon and allow for proper testing within this scope.
|
||||
pillar_server = StringProperty(
|
||||
pillar_server: StringProperty(
|
||||
name="Blender Cloud Server",
|
||||
description="URL of the Blender Cloud backend server",
|
||||
default=PILLAR_SERVER_URL,
|
||||
get=lambda self: PILLAR_SERVER_URL,
|
||||
)
|
||||
|
||||
local_texture_dir = StringProperty(
|
||||
local_texture_dir: StringProperty(
|
||||
name="Default Blender Cloud Texture Storage Directory",
|
||||
subtype="DIR_PATH",
|
||||
default="//textures",
|
||||
)
|
||||
|
||||
open_browser_after_share = BoolProperty(
|
||||
open_browser_after_share: BoolProperty(
|
||||
name="Open Browser after Sharing File",
|
||||
description="When enabled, Blender will open a webbrowser",
|
||||
default=True,
|
||||
@ -214,9 +211,9 @@ class BlenderCloudPreferences(AddonPreferences):
|
||||
|
||||
# TODO: store project-dependent properties with the project, so that people
|
||||
# can switch projects and the Attract and Flamenco properties switch with it.
|
||||
project = PointerProperty(type=BlenderCloudProjectGroup)
|
||||
project: PointerProperty(type=BlenderCloudProjectGroup)
|
||||
|
||||
cloud_project_local_path = StringProperty(
|
||||
cloud_project_local_path: StringProperty(
|
||||
name="Local Project Path",
|
||||
description="Local path of your Attract project, used to search for blend files; "
|
||||
"usually best to set to an absolute path",
|
||||
@ -225,29 +222,29 @@ class BlenderCloudPreferences(AddonPreferences):
|
||||
update=project_specific.store,
|
||||
)
|
||||
|
||||
flamenco_manager = PointerProperty(type=flamenco.FlamencoManagerGroup)
|
||||
flamenco_exclude_filter = StringProperty(
|
||||
flamenco_manager: PointerProperty(type=flamenco.FlamencoManagerGroup)
|
||||
flamenco_exclude_filter: StringProperty(
|
||||
name="File Exclude Filter",
|
||||
description='Space-separated list of filename filters, like "*.abc *.mkv", to prevent '
|
||||
"matching files from being packed into the output directory",
|
||||
default="",
|
||||
update=project_specific.store,
|
||||
)
|
||||
flamenco_job_file_path = StringProperty(
|
||||
flamenco_job_file_path: StringProperty(
|
||||
name="Job Storage Path",
|
||||
description="Path where to store job files, should be accesible for Workers too",
|
||||
subtype="DIR_PATH",
|
||||
default=tempfile.gettempdir(),
|
||||
update=project_specific.store,
|
||||
)
|
||||
flamenco_job_output_path = StringProperty(
|
||||
flamenco_job_output_path: StringProperty(
|
||||
name="Job Output Path",
|
||||
description="Path where to store output files, should be accessible for Workers",
|
||||
subtype="DIR_PATH",
|
||||
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",
|
||||
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 "
|
||||
@ -257,7 +254,7 @@ class BlenderCloudPreferences(AddonPreferences):
|
||||
soft_max=4,
|
||||
update=project_specific.store,
|
||||
)
|
||||
flamenco_relative_only = BoolProperty(
|
||||
flamenco_relative_only: BoolProperty(
|
||||
name="Relative Paths Only",
|
||||
description="When enabled, only assets that are referred to with a relative path are "
|
||||
"packed, and assets referred to by an absolute path are excluded from the "
|
||||
@ -266,12 +263,12 @@ class BlenderCloudPreferences(AddonPreferences):
|
||||
update=project_specific.store,
|
||||
)
|
||||
|
||||
flamenco_open_browser_after_submit = BoolProperty(
|
||||
flamenco_open_browser_after_submit: BoolProperty(
|
||||
name="Open Browser after Submitting Job",
|
||||
description="When enabled, Blender will open a webbrowser",
|
||||
default=True,
|
||||
)
|
||||
flamenco_show_quit_after_submit_button = BoolProperty(
|
||||
flamenco_show_quit_after_submit_button: BoolProperty(
|
||||
name='Show "Submit & Quit" button',
|
||||
description='When enabled, next to the "Render on Flamenco" button there will be a button '
|
||||
'"Submit & Quit" that silently quits Blender after submitting the render job '
|
||||
@ -343,7 +340,7 @@ class BlenderCloudPreferences(AddonPreferences):
|
||||
bss = context.window_manager.blender_sync_status
|
||||
bsync_box = layout.box()
|
||||
bsync_box.enabled = msg_icon != "ERROR"
|
||||
row = bsync_box.row().split(**compatibility.factor(0.33))
|
||||
row = bsync_box.row().split(factor=0.33)
|
||||
row.label(text="Blender Sync with Blender Cloud", icon_value=icon("CLOUD"))
|
||||
|
||||
icon_for_level = {
|
||||
@ -386,7 +383,7 @@ class BlenderCloudPreferences(AddonPreferences):
|
||||
layout.enabled = bss.status in {"NONE", "IDLE"}
|
||||
|
||||
buttons = layout.column()
|
||||
row_buttons = buttons.row().split(**compatibility.factor(0.5))
|
||||
row_buttons = buttons.row().split(factor=0.5)
|
||||
row_push = row_buttons.row()
|
||||
row_pull = row_buttons.row(align=True)
|
||||
|
||||
@ -411,7 +408,7 @@ class BlenderCloudPreferences(AddonPreferences):
|
||||
props.action = "PULL"
|
||||
props.blender_version = bss.version
|
||||
row_pull.operator(
|
||||
"pillar.sync", text="", icon=compatibility.SYNC_SELECT_VERSION_ICON
|
||||
"pillar.sync", text="", icon="DOWNARROW_HLT"
|
||||
).action = "SELECT"
|
||||
else:
|
||||
row_pull.label(text="Cloud Sync is running.")
|
||||
@ -460,7 +457,7 @@ class BlenderCloudPreferences(AddonPreferences):
|
||||
header_row = flamenco_box.row(align=True)
|
||||
header_row.label(text="Flamenco:", icon_value=icon("CLOUD"))
|
||||
|
||||
manager_split = flamenco_box.split(**compatibility.factor(0.32), align=True)
|
||||
manager_split = flamenco_box.split(factor=0.32, align=True)
|
||||
manager_split.label(text="Manager:")
|
||||
manager_box = manager_split.row(align=True)
|
||||
|
||||
@ -477,7 +474,7 @@ class BlenderCloudPreferences(AddonPreferences):
|
||||
else:
|
||||
manager_box.label(text="Fetching available managers.")
|
||||
|
||||
path_split = flamenco_box.split(**compatibility.factor(0.32), align=True)
|
||||
path_split = flamenco_box.split(factor=0.32, align=True)
|
||||
path_split.label(text="Job File Path:")
|
||||
path_box = path_split.row(align=True)
|
||||
path_box.prop(self, "flamenco_job_file_path", text="")
|
||||
@ -487,7 +484,7 @@ class BlenderCloudPreferences(AddonPreferences):
|
||||
props.path = self.flamenco_job_file_path
|
||||
|
||||
job_output_box = flamenco_box.column(align=True)
|
||||
path_split = job_output_box.split(**compatibility.factor(0.32), align=True)
|
||||
path_split = job_output_box.split(factor=0.32, align=True)
|
||||
path_split.label(text="Job Output Path:")
|
||||
path_box = path_split.row(align=True)
|
||||
path_box.prop(self, "flamenco_job_output_path", text="")
|
||||
@ -497,7 +494,7 @@ class BlenderCloudPreferences(AddonPreferences):
|
||||
props.path = self.flamenco_job_output_path
|
||||
job_output_box.prop(self, "flamenco_exclude_filter")
|
||||
|
||||
prop_split = job_output_box.split(**compatibility.factor(0.32), align=True)
|
||||
prop_split = job_output_box.split(factor=0.32, align=True)
|
||||
prop_split.label(text="Strip Components:")
|
||||
prop_split.prop(self, "flamenco_job_output_strip_components", text="")
|
||||
|
||||
@ -586,13 +583,12 @@ class PILLAR_OT_subscribe(Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@compatibility.convert_properties
|
||||
class PILLAR_OT_project_open_in_browser(Operator):
|
||||
bl_idname = "pillar.project_open_in_browser"
|
||||
bl_label = "Open in Browser"
|
||||
bl_description = "Opens a webbrowser to show the project"
|
||||
|
||||
project_id = StringProperty(name="Project ID")
|
||||
project_id: StringProperty(name="Project ID")
|
||||
|
||||
def execute(self, context):
|
||||
if not self.project_id:
|
||||
|
@ -1,71 +0,0 @@
|
||||
"""Compatibility functions to support Blender 2.79 and 2.80+ in one code base."""
|
||||
import functools
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
if bpy.app.version < (2, 80):
|
||||
SYNC_SELECT_VERSION_ICON = "DOTSDOWN"
|
||||
else:
|
||||
SYNC_SELECT_VERSION_ICON = "DOWNARROW_HLT"
|
||||
|
||||
|
||||
# Get references to all property definition functions in bpy.props,
|
||||
# so that they can be used to replace 'x = IntProperty()' to 'x: IntProperty()'
|
||||
# dynamically when working on Blender 2.80+
|
||||
__all_prop_funcs = {
|
||||
getattr(bpy.props, propname)
|
||||
for propname in dir(bpy.props)
|
||||
if propname.endswith("Property")
|
||||
}
|
||||
|
||||
|
||||
def convert_properties(class_):
|
||||
"""Class decorator to avoid warnings in Blender 2.80+
|
||||
|
||||
This decorator replaces property definitions like this:
|
||||
|
||||
someprop = bpy.props.IntProperty()
|
||||
|
||||
to annotations, as introduced in Blender 2.80:
|
||||
|
||||
someprop: bpy.props.IntProperty()
|
||||
|
||||
No-op if running on Blender 2.79 or older.
|
||||
"""
|
||||
|
||||
if bpy.app.version < (2, 80):
|
||||
return class_
|
||||
|
||||
if not hasattr(class_, "__annotations__"):
|
||||
class_.__annotations__ = {}
|
||||
|
||||
attrs_to_delete = []
|
||||
for name, value in class_.__dict__.items():
|
||||
if not isinstance(value, tuple) or len(value) != 2:
|
||||
continue
|
||||
|
||||
prop_func, kwargs = value
|
||||
if prop_func not in __all_prop_funcs:
|
||||
continue
|
||||
|
||||
# This is a property definition, replace it with annotation.
|
||||
attrs_to_delete.append(name)
|
||||
class_.__annotations__[name] = value
|
||||
|
||||
for attr_name in attrs_to_delete:
|
||||
delattr(class_, attr_name)
|
||||
|
||||
return class_
|
||||
|
||||
|
||||
@functools.lru_cache()
|
||||
def factor(factor: float) -> dict:
|
||||
"""Construct keyword argument for UILayout.split().
|
||||
|
||||
On Blender 2.8 this returns {'factor': factor}, and on earlier Blenders it returns
|
||||
{'percentage': factor}.
|
||||
"""
|
||||
if bpy.app.version < (2, 80, 0):
|
||||
return {"percentage": factor}
|
||||
return {"factor": factor}
|
@ -34,13 +34,12 @@ if "bpy" in locals():
|
||||
bat_interface = importlib.reload(bat_interface)
|
||||
sdk = importlib.reload(sdk)
|
||||
blender = importlib.reload(blender)
|
||||
compatibility = importlib.reload(compatibility)
|
||||
except NameError:
|
||||
from . import bat_interface, sdk
|
||||
from .. import blender, compatibility
|
||||
from .. import blender
|
||||
else:
|
||||
from . import bat_interface, sdk
|
||||
from .. import blender, compatibility
|
||||
from .. import blender
|
||||
|
||||
import bpy
|
||||
from bpy.types import AddonPreferences, Operator, WindowManager, Scene, PropertyGroup
|
||||
@ -130,31 +129,20 @@ def manager_updated(self: "FlamencoManagerGroup", context):
|
||||
def silently_quit_blender():
|
||||
"""Quit Blender without any confirmation popup."""
|
||||
|
||||
try:
|
||||
prefs = bpy.context.preferences
|
||||
except AttributeError:
|
||||
# Backward compatibility with Blender < 2.80
|
||||
prefs = bpy.context.user_preferences
|
||||
|
||||
try:
|
||||
prefs.view.use_save_prompt = False
|
||||
except AttributeError:
|
||||
# Backward compatibility with Blender < 2.80
|
||||
prefs.view.use_quit_dialog = False
|
||||
|
||||
prefs = bpy.context.preferences
|
||||
prefs.view.use_save_prompt = False
|
||||
bpy.ops.wm.quit_blender()
|
||||
|
||||
|
||||
@compatibility.convert_properties
|
||||
class FlamencoManagerGroup(PropertyGroup):
|
||||
manager = EnumProperty(
|
||||
manager: EnumProperty(
|
||||
items=available_managers,
|
||||
name="Flamenco Manager",
|
||||
description="Which Flamenco Manager to use for jobs",
|
||||
update=manager_updated,
|
||||
)
|
||||
|
||||
status = EnumProperty(
|
||||
status: EnumProperty(
|
||||
items=[
|
||||
("NONE", "NONE", "We have done nothing at all yet"),
|
||||
(
|
||||
@ -293,7 +281,6 @@ def is_file_inside_job_storage(prefs, current_file: typing.Union[str, Path]) ->
|
||||
return True
|
||||
|
||||
|
||||
@compatibility.convert_properties
|
||||
class FLAMENCO_OT_render(
|
||||
async_loop.AsyncModalOperatorMixin,
|
||||
pillar.AuthenticatedPillarOperatorMixin,
|
||||
@ -309,7 +296,7 @@ class FLAMENCO_OT_render(
|
||||
stop_upon_exception = True
|
||||
log = logging.getLogger("%s.FLAMENCO_OT_render" % __name__)
|
||||
|
||||
quit_after_submit = BoolProperty()
|
||||
quit_after_submit: BoolProperty()
|
||||
|
||||
async def async_execute(self, context):
|
||||
# Refuse to start if the file hasn't been saved. It's okay if
|
||||
@ -864,7 +851,6 @@ class FLAMENCO_OT_abort(Operator, FlamencoPollMixin):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@compatibility.convert_properties
|
||||
class FLAMENCO_OT_explore_file_path(FlamencoPollMixin, Operator):
|
||||
"""Opens the Flamenco job storage path in a file explorer.
|
||||
|
||||
@ -875,9 +861,7 @@ class FLAMENCO_OT_explore_file_path(FlamencoPollMixin, Operator):
|
||||
bl_label = "Open in file explorer"
|
||||
bl_description = __doc__.rstrip(".")
|
||||
|
||||
path = StringProperty(
|
||||
name="Path", description="Path to explore", subtype="DIR_PATH"
|
||||
)
|
||||
path: StringProperty(name="Path", description="Path to explore", subtype="DIR_PATH")
|
||||
|
||||
def execute(self, context):
|
||||
import platform
|
||||
@ -940,13 +924,12 @@ class FLAMENCO_OT_disable_output_path_override(Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@compatibility.convert_properties
|
||||
class FLAMENCO_OT_set_recommended_sample_cap(Operator):
|
||||
bl_idname = "flamenco.set_recommended_sample_cap"
|
||||
bl_label = "Set Recommended Maximum Sample Count"
|
||||
bl_description = "Set the recommended maximum samples per render task"
|
||||
|
||||
sample_cap = IntProperty()
|
||||
sample_cap: IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.flamenco_render_chunk_sample_cap = self.sample_cap
|
||||
@ -1112,7 +1095,7 @@ class FLAMENCO_PT_render(bpy.types.Panel, FlamencoPollMixin):
|
||||
|
||||
prefs = preferences()
|
||||
|
||||
labeled_row = layout.split(**compatibility.factor(0.25), align=True)
|
||||
labeled_row = layout.split(factor=0.25, align=True)
|
||||
labeled_row.label(text="Manager:")
|
||||
prop_btn_row = labeled_row.row(align=True)
|
||||
|
||||
@ -1130,7 +1113,7 @@ class FLAMENCO_PT_render(bpy.types.Panel, FlamencoPollMixin):
|
||||
else:
|
||||
prop_btn_row.label(text="Fetching available managers.")
|
||||
|
||||
labeled_row = layout.split(**compatibility.factor(0.25), align=True)
|
||||
labeled_row = layout.split(factor=0.25, align=True)
|
||||
labeled_row.label(text="Job Type:")
|
||||
labeled_row.prop(context.scene, "flamenco_render_job_type", text="")
|
||||
|
||||
@ -1160,7 +1143,7 @@ class FLAMENCO_PT_render(bpy.types.Panel, FlamencoPollMixin):
|
||||
sample_count = scene_sample_count(context.scene)
|
||||
recommended_cap = sample_count // 4
|
||||
|
||||
split = box.split(**compatibility.factor(0.4))
|
||||
split = box.split(factor=0.4)
|
||||
split.label(text="Total Sample Count: %d" % sample_count)
|
||||
props = split.operator(
|
||||
"flamenco.set_recommended_sample_cap",
|
||||
@ -1181,7 +1164,7 @@ class FLAMENCO_PT_render(bpy.types.Panel, FlamencoPollMixin):
|
||||
else:
|
||||
box.prop(context.scene, "flamenco_render_fchunk_size")
|
||||
|
||||
labeled_row = layout.split(**compatibility.factor(0.25), align=True)
|
||||
labeled_row = layout.split(factor=0.25, align=True)
|
||||
labeled_row.label(text="Frame Range:")
|
||||
prop_btn_row = labeled_row.row(align=True)
|
||||
prop_btn_row.prop(context.scene, "flamenco_render_frame_range", text="")
|
||||
@ -1194,7 +1177,7 @@ class FLAMENCO_PT_render(bpy.types.Panel, FlamencoPollMixin):
|
||||
|
||||
paths_layout = layout.column(align=True)
|
||||
|
||||
labeled_row = paths_layout.split(**compatibility.factor(0.25), align=True)
|
||||
labeled_row = paths_layout.split(factor=0.25, align=True)
|
||||
labeled_row.label(text="Storage:")
|
||||
prop_btn_row = labeled_row.row(align=True)
|
||||
prop_btn_row.label(text=prefs.flamenco_job_file_path)
|
||||
@ -1217,7 +1200,7 @@ class FLAMENCO_PT_render(bpy.types.Panel, FlamencoPollMixin):
|
||||
)
|
||||
return
|
||||
|
||||
labeled_row = paths_layout.split(**compatibility.factor(0.25), align=True)
|
||||
labeled_row = paths_layout.split(factor=0.25, align=True)
|
||||
labeled_row.label(text="Output:")
|
||||
prop_btn_row = labeled_row.row(align=True)
|
||||
|
||||
@ -1237,7 +1220,7 @@ class FLAMENCO_PT_render(bpy.types.Panel, FlamencoPollMixin):
|
||||
props.path = str(render_output.parent)
|
||||
|
||||
if context.scene.flamenco_do_override_output_path:
|
||||
labeled_row = paths_layout.split(**compatibility.factor(0.25), align=True)
|
||||
labeled_row = paths_layout.split(factor=0.25, align=True)
|
||||
labeled_row.label(text="Effective Output Path:")
|
||||
labeled_row.label(text=str(render_output))
|
||||
|
||||
@ -1247,7 +1230,7 @@ class FLAMENCO_PT_render(bpy.types.Panel, FlamencoPollMixin):
|
||||
flamenco_status = context.window_manager.flamenco_status
|
||||
if flamenco_status in {"IDLE", "ABORTED", "DONE"}:
|
||||
if prefs.flamenco_show_quit_after_submit_button:
|
||||
ui = layout.split(**compatibility.factor(0.75), align=True)
|
||||
ui = layout.split(factor=0.75, align=True)
|
||||
else:
|
||||
ui = layout
|
||||
ui.operator(
|
||||
|
@ -25,7 +25,7 @@ import bpy
|
||||
import pillarsdk
|
||||
from pillarsdk import exceptions as sdk_exceptions
|
||||
from .pillar import pillar_call
|
||||
from . import async_loop, compatibility, pillar, home_project, blender
|
||||
from . import async_loop, pillar, home_project, blender
|
||||
|
||||
REQUIRES_ROLES_FOR_IMAGE_SHARING = {"subscriber", "demo"}
|
||||
IMAGE_SHARING_GROUP_NODE_NAME = "Image sharing"
|
||||
@ -56,7 +56,6 @@ async def find_image_sharing_group_id(home_project_id, user_id):
|
||||
return share_group["_id"]
|
||||
|
||||
|
||||
@compatibility.convert_properties
|
||||
class PILLAR_OT_image_share(
|
||||
pillar.PillarOperatorMixin, async_loop.AsyncModalOperatorMixin, bpy.types.Operator
|
||||
):
|
||||
@ -71,7 +70,7 @@ class PILLAR_OT_image_share(
|
||||
share_group_id = None # top-level share group node ID
|
||||
user_id = None
|
||||
|
||||
target = bpy.props.EnumProperty(
|
||||
target: bpy.props.EnumProperty(
|
||||
items=[
|
||||
("FILE", "File", "Share an image file"),
|
||||
("DATABLOCK", "Datablock", "Share an image datablock"),
|
||||
@ -81,19 +80,19 @@ class PILLAR_OT_image_share(
|
||||
default="SCREENSHOT",
|
||||
)
|
||||
|
||||
name = bpy.props.StringProperty(
|
||||
name: bpy.props.StringProperty(
|
||||
name="name", description="File or datablock name to sync"
|
||||
)
|
||||
|
||||
screenshot_show_multiview = bpy.props.BoolProperty(
|
||||
screenshot_show_multiview: bpy.props.BoolProperty(
|
||||
name="screenshot_show_multiview", description="Enable Multi-View", default=False
|
||||
)
|
||||
|
||||
screenshot_use_multiview = bpy.props.BoolProperty(
|
||||
screenshot_use_multiview: bpy.props.BoolProperty(
|
||||
name="screenshot_use_multiview", description="Use Multi-View", default=False
|
||||
)
|
||||
|
||||
screenshot_full = bpy.props.BoolProperty(
|
||||
screenshot_full: bpy.props.BoolProperty(
|
||||
name="screenshot_full",
|
||||
description="Full Screen, Capture the whole window (otherwise only capture the active area)",
|
||||
default=False,
|
||||
|
@ -35,7 +35,7 @@ import asyncio
|
||||
import pillarsdk
|
||||
from pillarsdk import exceptions as sdk_exceptions
|
||||
from .pillar import pillar_call
|
||||
from . import async_loop, blender, compatibility, pillar, cache, blendfile, home_project
|
||||
from . import async_loop, blender, pillar, cache, blendfile, home_project
|
||||
|
||||
SETTINGS_FILES_TO_UPLOAD = ["userpref.blend", "startup.blend"]
|
||||
|
||||
@ -214,7 +214,6 @@ async def available_blender_versions(home_project_id: str, user_id: str) -> list
|
||||
|
||||
|
||||
# noinspection PyAttributeOutsideInit
|
||||
@compatibility.convert_properties
|
||||
class PILLAR_OT_sync(
|
||||
pillar.PillarOperatorMixin, async_loop.AsyncModalOperatorMixin, bpy.types.Operator
|
||||
):
|
||||
@ -227,7 +226,7 @@ class PILLAR_OT_sync(
|
||||
sync_group_id = "" # top-level sync group node ID
|
||||
sync_group_versioned_id = "" # sync group node ID for the given Blender version.
|
||||
|
||||
action = bpy.props.EnumProperty(
|
||||
action: bpy.props.EnumProperty(
|
||||
items=[
|
||||
("PUSH", "Push", "Push settings to the Blender Cloud"),
|
||||
("PULL", "Pull", "Pull settings from the Blender Cloud"),
|
||||
@ -238,7 +237,7 @@ class PILLAR_OT_sync(
|
||||
)
|
||||
|
||||
CURRENT_BLENDER_VERSION = "%i.%i" % bpy.app.version[:2]
|
||||
blender_version = bpy.props.StringProperty(
|
||||
blender_version: bpy.props.StringProperty(
|
||||
name="blender_version",
|
||||
description="Blender version to sync for",
|
||||
default=CURRENT_BLENDER_VERSION,
|
||||
|
@ -26,16 +26,11 @@ import bpy
|
||||
import bgl
|
||||
|
||||
import pillarsdk
|
||||
from .. import async_loop, compatibility, pillar, cache, blender, utils
|
||||
from .. import async_loop, pillar, cache, blender, utils
|
||||
from . import (
|
||||
menu_item as menu_item_mod,
|
||||
) # so that we can have menu items called 'menu_item'
|
||||
from . import nodes
|
||||
|
||||
if bpy.app.version < (2, 80):
|
||||
from . import draw_27 as draw
|
||||
else:
|
||||
from . import draw
|
||||
from . import draw, nodes
|
||||
|
||||
REQUIRED_ROLES_FOR_TEXTURE_BROWSER = {"subscriber", "demo"}
|
||||
MOUSE_SCROLL_PIXELS_PER_TICK = 50
|
||||
@ -686,7 +681,6 @@ class BlenderCloudBrowser(
|
||||
self.scroll_offset_target = self.scroll_offset = 0
|
||||
|
||||
|
||||
@compatibility.convert_properties
|
||||
class PILLAR_OT_switch_hdri(
|
||||
pillar.PillarOperatorMixin, async_loop.AsyncModalOperatorMixin, bpy.types.Operator
|
||||
):
|
||||
@ -698,11 +692,11 @@ class PILLAR_OT_switch_hdri(
|
||||
|
||||
log = logging.getLogger("bpy.ops.%s" % bl_idname)
|
||||
|
||||
image_name = bpy.props.StringProperty(
|
||||
image_name: bpy.props.StringProperty(
|
||||
name="image_name", description="Name of the image block to replace"
|
||||
)
|
||||
|
||||
file_uuid = bpy.props.StringProperty(
|
||||
file_uuid: bpy.props.StringProperty(
|
||||
name="file_uuid", description="File ID to download"
|
||||
)
|
||||
|
||||
@ -834,7 +828,7 @@ def _hdri_download_panel(self, current_image):
|
||||
)
|
||||
return
|
||||
|
||||
row = self.layout.row(align=True).split(**compatibility.factor(0.3))
|
||||
row = self.layout.row(align=True).split(factor=0.3)
|
||||
row.label(text="HDRi", icon_value=blender.icon("CLOUD"))
|
||||
row.prop(current_image, "hdri_variation", text="")
|
||||
|
||||
|
@ -1,98 +0,0 @@
|
||||
"""OpenGL drawing code for the texture browser.
|
||||
|
||||
Requires Blender 2.79 or older.
|
||||
"""
|
||||
|
||||
import typing
|
||||
|
||||
import bgl
|
||||
import blf
|
||||
import bpy
|
||||
|
||||
Float2 = typing.Tuple[float, float]
|
||||
Float3 = typing.Tuple[float, float, float]
|
||||
Float4 = typing.Tuple[float, float, float, float]
|
||||
|
||||
|
||||
def text(
|
||||
pos2d: Float2,
|
||||
display_text: typing.Union[str, typing.List[str]],
|
||||
rgba: Float4 = (1.0, 1.0, 1.0, 1.0),
|
||||
fsize=12,
|
||||
align="L",
|
||||
):
|
||||
"""Draw text with the top-left corner at 'pos2d'."""
|
||||
|
||||
dpi = bpy.context.user_preferences.system.dpi
|
||||
gap = 12
|
||||
x_pos, y_pos = pos2d
|
||||
font_id = 0
|
||||
blf.size(font_id, fsize, dpi)
|
||||
|
||||
# Compute the height of one line.
|
||||
mwidth, mheight = blf.dimensions(font_id, "Tp") # Use high and low letters.
|
||||
mheight *= 1.5
|
||||
|
||||
# Split text into lines.
|
||||
if isinstance(display_text, str):
|
||||
mylines = display_text.split("\n")
|
||||
else:
|
||||
mylines = display_text
|
||||
maxwidth = 0
|
||||
maxheight = len(mylines) * mheight
|
||||
|
||||
for idx, line in enumerate(mylines):
|
||||
text_width, text_height = blf.dimensions(font_id, line)
|
||||
if align == "C":
|
||||
newx = x_pos - text_width / 2
|
||||
elif align == "R":
|
||||
newx = x_pos - text_width - gap
|
||||
else:
|
||||
newx = x_pos
|
||||
|
||||
# Draw
|
||||
blf.position(font_id, newx, y_pos - mheight * idx, 0)
|
||||
bgl.glColor4f(*rgba)
|
||||
blf.draw(font_id, " " + line)
|
||||
|
||||
# saves max width
|
||||
if maxwidth < text_width:
|
||||
maxwidth = text_width
|
||||
|
||||
return maxwidth, maxheight
|
||||
|
||||
|
||||
def aabox(v1: Float2, v2: Float2, rgba: Float4):
|
||||
"""Draw an axis-aligned box."""
|
||||
|
||||
bgl.glColor4f(*rgba)
|
||||
bgl.glRectf(*v1, *v2)
|
||||
|
||||
|
||||
def aabox_with_texture(v1: Float2, v2: Float2):
|
||||
"""Draw an axis-aligned box with a texture."""
|
||||
|
||||
bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
|
||||
|
||||
bgl.glEnable(bgl.GL_TEXTURE_2D)
|
||||
bgl.glBegin(bgl.GL_QUADS)
|
||||
bgl.glTexCoord2d(0, 0)
|
||||
bgl.glVertex2d(v1[0], v1[1])
|
||||
bgl.glTexCoord2d(0, 1)
|
||||
bgl.glVertex2d(v1[0], v2[1])
|
||||
bgl.glTexCoord2d(1, 1)
|
||||
bgl.glVertex2d(v2[0], v2[1])
|
||||
bgl.glTexCoord2d(1, 0)
|
||||
bgl.glVertex2d(v2[0], v1[1])
|
||||
bgl.glEnd()
|
||||
bgl.glDisable(bgl.GL_TEXTURE_2D)
|
||||
|
||||
|
||||
def bind_texture(texture: bpy.types.Image):
|
||||
"""Bind a Blender image to a GL texture slot."""
|
||||
bgl.glBindTexture(bgl.GL_TEXTURE_2D, texture.bindcode[0])
|
||||
|
||||
|
||||
def load_texture(texture: bpy.types.Image) -> int:
|
||||
"""Load the texture, return OpenGL error code."""
|
||||
return texture.gl_load(filter=bgl.GL_NEAREST, mag=bgl.GL_NEAREST)
|
Reference in New Issue
Block a user