Remove code to support Blender 2.79 and older

This commit is contained in:
Sybren A. Stüvel 2021-02-16 11:33:48 +01:00
parent 8b49c5505e
commit 14778e5c08
9 changed files with 65 additions and 459 deletions

View File

@ -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

View File

@ -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()

View File

@ -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:

View File

@ -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}

View File

@ -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(

View File

@ -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,

View File

@ -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,

View File

@ -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="")

View File

@ -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)