Compare commits

...

14 Commits

Author SHA1 Message Date
Dogukan Karatas f2efbf57bb Merge pull request #274 from specklesystems/jrm/no-deps
fix(pip): install
2025-06-05 14:50:44 +02:00
Jedd Morgan 74347fe5e0 No deps 2025-06-05 13:47:25 +01:00
Dogukan Karatas a7c6969a4a Merge pull request #273 from specklesystems/dogukan/rename-installer
v2 - renames the installer
2025-06-05 12:50:05 +02:00
Dogukan Karatas 423c145984 renames the installer 2025-06-05 12:46:32 +02:00
Dogukan Karatas c6ecf55f59 Merge pull request #261 from specklesystems/bilal/cnx-1815-blender-connector-legacy-to-next-gen-transition
v2 - add legacy warnings everywhere
2025-06-02 14:43:01 +02:00
Dogukan Karatas c402a388d8 Merge pull request #266 from specklesystems/jrm/bump-deps-v2
Updated dependencies for workspaces fix
2025-05-19 12:14:52 +02:00
Jedd Morgan bc3d4d26d2 Updated dependencies for workspaces fix 2025-05-19 11:10:30 +01:00
Mucahit Bilal GOKER 9397774e24 add a warning to the panel 2025-05-15 13:56:31 +03:00
Mucahit Bilal GOKER ddbcb7ee39 update addon description 2025-05-15 13:56:16 +03:00
Mucahit Bilal GOKER 105bb00304 add legacy to n menu 2025-05-15 13:39:17 +03:00
Jedd Morgan ffd4da1ed9 fix(CI): Fix broken signing (#244)
* Poetry update

* Updated runner

* bump poetry again

* require export

* Fixed Digicerts broken sync certs
2025-04-23 11:35:23 +01:00
Jedd Morgan 26ba5c921b Poetry update (#243)
* Poetry update

* Updated runner

* bump poetry again

* require export
2025-04-22 18:26:01 +01:00
Jedd Morgan a3aaf4471c Bumped Dependencies (#215)
* Updated dependencies

* black + isort
2024-12-11 14:12:41 +00:00
Jedd Morgan 4995f02351 Fixed issue with local server builds (#212) 2024-11-22 11:57:55 +00:00
23 changed files with 1061 additions and 1404 deletions
+7 -7
View File
@@ -6,7 +6,7 @@ orbs:
jobs:
package-connector:
docker:
- image: cimg/python:3.11.0
- image: cimg/python:3.11.12
steps:
- checkout
- run:
@@ -30,7 +30,7 @@ jobs:
build-connector-zip:
docker:
- image: cimg/python:3.11.0
- image: cimg/python:3.11.12
steps:
- attach_workspace:
at: ./
@@ -44,10 +44,10 @@ jobs:
root: ./
paths:
- bpy_speckle.zip
get-ci-tools: # Clones our ci tools and persists them to the workspace
docker:
- image: cimg/base:2021.01
- image: cimg/base:2025.04
steps:
- add_ssh_keys:
fingerprints:
@@ -104,7 +104,7 @@ jobs:
- run:
name: Sync Certs
command: |
& $env:SSM\smksp_cert_sync.exe
& $env:SSM\smctl.exe windows certsync
- run:
name: Build Installer
command: speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\blender.iss /Sbyparam=$p /DSIGN_INSTALLER /DCODE_SIGNING_CERT_FINGERPRINT=%SM_CODE_SIGNING_CERT_SHA1_HASH%
@@ -166,7 +166,7 @@ jobs:
build-installer-manual:
docker:
- image: cimg/base:2021.01
- image: cimg/base:2025.04
parameters:
slug:
type: string
@@ -228,7 +228,7 @@ workflows:
filters: &build_filters
tags:
only: /([0-9]+)\.([0-9]+)\.([0-9]+)(?:-\w+)?(?:\.[0-9]+)?/
- build-connector-zip:
requires:
- package-connector
+3 -3
View File
@@ -13,13 +13,13 @@ from bpy_speckle.properties import *
from bpy_speckle.ui import *
bl_info = {
"name": "SpeckleBlender 2.0",
"name": "SpeckleBlender 2.0 (Legacy)",
"author": "Speckle Systems",
"version": (0, 2, 0),
"blender": (2, 92, 0),
"location": "3d viewport toolbar (N), under the Speckle tab.",
"description": "The Speckle Connector using specklepy 2.0!",
"warning": "This add-on is WIP and should be used with caution",
"description": "(Legacy) Speckle Connector using specklepy 2.0!",
"warning": "This is a legacy add-on. Download the new connector from https://app.speckle.systems/connectors",
"wiki_url": "https://github.com/specklesystems/speckle-blender",
"category": "Scene",
}
+3 -1
View File
@@ -5,7 +5,9 @@ from attrs import define
from bpy.types import ID, Collection, Object
from specklepy.objects.base import Base
from specklepy.objects.graph_traversal.commit_object_builder import (
ROOT, CommitObjectBuilder)
ROOT,
CommitObjectBuilder,
)
from specklepy.objects.other import Collection as SCollection
from bpy_speckle.functions import _report
+1
View File
@@ -1,6 +1,7 @@
"""
Permanent handle on all user clients
"""
from specklepy.core.api.client import SpeckleClient
speckle_clients: list[SpeckleClient] = []
+40 -21
View File
@@ -9,26 +9,43 @@ from mathutils import Matrix as MMatrix
from mathutils import Quaternion as MQuaternion
from mathutils import Vector as MVector
from specklepy.objects.base import Base
from specklepy.objects.geometry import (Arc, Circle, Curve, Ellipse, Line,
Mesh, Plane, Polycurve, Polyline)
from specklepy.objects.geometry import (
Arc,
Circle,
Curve,
Ellipse,
Line,
Mesh,
Plane,
Polycurve,
Polyline,
)
from specklepy.objects.other import BlockDefinition
from specklepy.objects.other import Collection as SCollection
from specklepy.objects.other import Instance, Transform
from bpy_speckle.convert.constants import (DISPLAY_VALUE_PROPERTY_ALIASES,
ELEMENTS_PROPERTY_ALIASES,
OBJECT_NAME_MAX_LENGTH,
OBJECT_NAME_NUMERAL_SEPARATOR,
OBJECT_NAME_SPECKLE_SEPARATOR,
SPECKLE_ID_LENGTH)
from bpy_speckle.convert.constants import (
DISPLAY_VALUE_PROPERTY_ALIASES,
ELEMENTS_PROPERTY_ALIASES,
OBJECT_NAME_MAX_LENGTH,
OBJECT_NAME_NUMERAL_SEPARATOR,
OBJECT_NAME_SPECKLE_SEPARATOR,
SPECKLE_ID_LENGTH,
)
from bpy_speckle.convert.util import ConversionSkippedException
from bpy_speckle.functions import (_report, get_default_traversal_func,
get_scale_length)
from bpy_speckle.functions import _report, get_default_traversal_func, get_scale_length
from .util import (add_colors, add_custom_properties, add_faces,
add_to_hierarchy, add_uv_coords, add_vertices,
get_render_material, get_vertex_color_material,
render_material_to_native)
from .util import (
add_colors,
add_custom_properties,
add_faces,
add_to_hierarchy,
add_uv_coords,
add_vertices,
get_render_material,
get_vertex_color_material,
render_material_to_native,
)
SUPPORTED_CURVES = (Line, Polyline, Curve, Arc, Polycurve, Ellipse, Circle)
CAN_CONVERT_TO_NATIVE = (
@@ -59,7 +76,9 @@ def can_convert_to_native(speckle_object: Base) -> bool:
return False
convert_instances_as: str = "" # HACK: This is hacky, we need a better way to pass settings down to the converter
convert_instances_as: str = (
"" # HACK: This is hacky, we need a better way to pass settings down to the converter
)
def set_convert_instances_as(value: str):
@@ -682,9 +701,9 @@ def instance_to_native_object(instance: Instance, scale: float) -> Object:
native_instance = create_new_object(None, name)
native_instance.empty_display_size = 0
converted_objects[
"__ROOT"
] = native_instance # we create a dummy root to avoid id conflicts, since revit definitions have displayValues, they are convertible
converted_objects["__ROOT"] = (
native_instance # we create a dummy root to avoid id conflicts, since revit definitions have displayValues, they are convertible
)
traversal_root = Base(elements=definition, id="__ROOT")
# Convert definition + "elements" on definition
@@ -758,9 +777,9 @@ def _instance_definition_to_native(
native_def["applicationId"] = definition.applicationId
converted_objects = {}
converted_objects[
"__ROOT"
] = native_def # we create a dummy root to avoid id conflicts, since revit definitions have displayValues, they are convertible
converted_objects["__ROOT"] = (
native_def # we create a dummy root to avoid id conflicts, since revit definitions have displayValues, they are convertible
)
dummyRoot = Base(elements=definition, id="__ROOT")
_deep_conversion(dummyRoot, converted_objects, True)
+30 -12
View File
@@ -11,22 +11,40 @@ from mathutils import Matrix as MMatrix
from mathutils import Vector as MVector
from mathutils.geometry import interpolate_bezier
from specklepy.objects import Base
from specklepy.objects.geometry import (Box, Curve, Interval, Mesh, Point,
Polyline, Vector)
from specklepy.objects.other import (BlockDefinition, BlockInstance,
RenderMaterial, Transform)
from specklepy.objects.geometry import (
Box,
Curve,
Interval,
Mesh,
Point,
Polyline,
Vector,
)
from specklepy.objects.other import (
BlockDefinition,
BlockInstance,
RenderMaterial,
Transform,
)
from bpy_speckle.blender_commit_object_builder import \
BlenderCommitObjectBuilder
from bpy_speckle.convert.constants import (OBJECT_NAME_SPECKLE_SEPARATOR,
SPECKLE_ID_LENGTH)
from bpy_speckle.convert.util import (ConversionSkippedException,
get_blender_custom_properties,
make_knots, nurb_make_curve, to_argb_int)
from bpy_speckle.blender_commit_object_builder import BlenderCommitObjectBuilder
from bpy_speckle.convert.constants import (
OBJECT_NAME_SPECKLE_SEPARATOR,
SPECKLE_ID_LENGTH,
)
from bpy_speckle.convert.util import (
ConversionSkippedException,
get_blender_custom_properties,
make_knots,
nurb_make_curve,
to_argb_int,
)
from bpy_speckle.functions import _report
Units: str = "m" # The desired final units to send
UnitsScale: float = 1 # The scale factor conversions need to apply to position data to get to the desired units
UnitsScale: float = (
1 # The scale factor conversions need to apply to position data to get to the desired units
)
CAN_CONVERT_TO_SPECKLE = ("MESH", "CURVE", "EMPTY", "CAMERA", "FONT", "SURFACE", "META")
+2 -4
View File
@@ -1,10 +1,8 @@
from typing import Callable
from specklepy.objects.base import Base
from specklepy.objects.graph_traversal.traversal import (GraphTraversal,
TraversalRule)
from specklepy.objects.units import (get_scale_factor_to_meters,
get_units_from_string)
from specklepy.objects.graph_traversal.traversal import GraphTraversal, TraversalRule
from specklepy.objects.units import get_scale_factor_to_meters, get_units_from_string
from bpy_speckle.convert.constants import ELEMENTS_PROPERTY_ALIASES
+2
View File
@@ -1,6 +1,7 @@
"""
Provides uniform and consistent path helpers for `specklepy`
"""
import os
import sys
from importlib import import_module, invalidate_caches
@@ -167,6 +168,7 @@ def install_requirements(host_application: str) -> None:
"--prefer-binary",
"--ignore-installed",
"--no-compile",
"--no-deps",
"-t",
str(path),
"-r",
+10 -21
View File
@@ -1,12 +1,15 @@
from .commit import DeleteCommit
from .misc import OpenSpeckleForum, OpenSpeckleGuide, OpenSpeckleTutorials
from .object import (DeleteObject, ResetObject, SelectIfHasCustomProperty,
SelectIfSameCustomProperty, UpdateObject,
UploadNgonsAsPolylines)
from .streams import (AddStreamFromURL, CopyBranchName, CopyCommitId,
CopyModelId, CopyStreamId, CreateStream, DeleteStream,
ReceiveStreamObjects, SelectOrphanObjects,
SendStreamObjects, ViewStreamDataApi)
from .streams import (
AddStreamFromURL,
CopyCommitId,
CopyModelId,
CopyStreamId,
CreateStream,
ReceiveStreamObjects,
SendStreamObjects,
ViewStreamDataApi,
)
from .users import LoadUsers, LoadUserStreams, ResetUsers
operator_classes = [
@@ -17,28 +20,14 @@ operator_classes = [
LoadUserStreams,
CopyStreamId,
CopyCommitId,
CopyBranchName,
CopyModelId,
]
operator_classes.extend([DeleteCommit])
operator_classes.extend(
[
UpdateObject,
ResetObject,
DeleteObject,
UploadNgonsAsPolylines,
SelectIfSameCustomProperty,
SelectIfHasCustomProperty,
]
)
operator_classes.extend(
[
ViewStreamDataApi,
DeleteStream,
SelectOrphanObjects,
AddStreamFromURL,
CreateStream,
OpenSpeckleGuide,
+1
View File
@@ -1,6 +1,7 @@
"""
Commit operators
"""
import bpy
from bpy.props import BoolProperty
from specklepy.logging import metrics
-346
View File
@@ -1,346 +0,0 @@
"""
Object operators
"""
import bpy
from bpy.props import BoolProperty, EnumProperty
from deprecated import deprecated
from specklepy.logging import metrics
from bpy_speckle.clients import speckle_clients
from bpy_speckle.convert.to_speckle import (convert_to_speckle,
ngons_to_speckle_polylines)
from bpy_speckle.functions import _report, get_scale_length
@deprecated
class UpdateObject(bpy.types.Operator):
"""
Update local (receive) or remote (send) object depending on
the update direction. If sending, updates the object on the
server in-place.
"""
bl_idname = "speckle.update_object"
bl_label = "Update Object (DEPRECATED)"
bl_options = {"REGISTER", "UNDO"}
client = None
def execute(self, context):
client = speckle_clients[int(context.scene.speckle.active_user)]
active = context.active_object
_report(active)
if active is not None and active.speckle.enabled:
if active.speckle.send_or_receive == "send" and active.speckle.stream_id:
sstream = client.streams.get(active.speckle.stream_id)
# res = client.StreamGetAsync(active.speckle.stream_id)['resource']
# res = client.streams.get(active.speckle.stream_id)
if sstream is None:
_report("Getting stream failed.")
return {"CANCELLED"}
stream_units = "Meters"
if sstream.baseProperties:
stream_units = sstream.baseProperties.units
scale = context.scene.unit_settings.scale_length / get_scale_length(
stream_units
)
sm = convert_to_speckle(active, scale)
_report("Updating object {}".format(sm["_id"]))
client.objects.update(active.speckle.object_id, sm)
metrics.track(
"Connector Action",
None,
custom_props={"name": "UpdateObject"},
)
return {"FINISHED"}
return {"CANCELLED"}
return {"CANCELLED"}
@deprecated
class ResetObject(bpy.types.Operator):
"""
Reset Speckle object settings
"""
bl_idname = "speckle.reset_object"
bl_label = "Reset Object (DEPRECATED)"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
context.object.speckle.send_or_receive = "send"
context.object.speckle.stream_id = ""
context.object.speckle.object_id = ""
context.object.speckle.enabled = False
context.view_layer.update()
metrics.track(
"Connector Action",
None,
custom_props={"name": "ResetObject"},
)
return {"FINISHED"}
@deprecated
class DeleteObject(bpy.types.Operator):
"""
Delete object from the server and update relevant stream
"""
bl_idname = "speckle.delete_object"
bl_label = "Delete Object (DEPRECATED)"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
client = speckle_clients[int(context.scene.speckle.active_user)]
active = context.object
if active.speckle.enabled:
res = client.StreamGetAsync(active.speckle.stream_id)
existing = [
x
for x in res["resource"]["objects"]
if x["_id"] == active.speckle.object_id
]
if existing is None:
return {"CANCELLED"}
new_objects = [
x
for x in res["resource"]["objects"]
if x["_id"] != active.speckle.object_id
]
res = client.GetLayers(active.speckle.stream_id)
new_layers = res["resource"]["layers"]
new_layers[-1]["objectCount"] = new_layers[-1]["objectCount"] - 1
new_layers[-1]["topology"] = "0-%s" % new_layers[-1]["objectCount"]
res = client.StreamUpdateAsync(
{"objects": new_objects, "layers": new_layers}, active.speckle.stream_id
)
res = client.ObjectDeleteAsync(active.speckle.object_id)
active.speckle.send_or_receive = "send"
active.speckle.stream_id = ""
active.speckle.object_id = ""
active.speckle.enabled = False
context.view_layer.update()
metrics.track(
"Connector Action",
None,
custom_props={"name": "DeleteObject"},
)
return {"FINISHED"}
@deprecated
class UploadNgonsAsPolylines(bpy.types.Operator):
"""
Upload mesh ngon faces as polyline outlines
TODO: move to another category of specialized operators and fix to work with API 2.0
"""
bl_idname = "speckle.upload_ngons_as_polylines"
bl_label = "Upload Ngons As Polylines (DEPRECATED)"
bl_options = {"REGISTER", "UNDO"}
clear_stream: BoolProperty(
name="Clear stream",
default=False,
)
def execute(self, context):
active = context.active_object
if active is not None and active.type == "MESH":
user = context.scene.speckle.users[int(context.scene.speckle.active_user)]
client = speckle_clients[int(context.scene.speckle.active_user)]
stream = user.streams[user.active_stream]
# scale = context.scene.unit_settings.scale_length / get_scale_length(
# stream.units
# )
scale = 1.0
sp = ngons_to_speckle_polylines(active, scale)
if sp is None:
return {"CANCELLED"}
placeholders = []
for polyline in sp:
res = client.objects.create([polyline])
if res is None:
_report(client.me)
continue
placeholders.extend(res)
if not placeholders:
return {"CANCELLED"}
# Get list of existing objects in stream and append new object to list
_report("Fetching stream...")
sstream = client.streams.get(stream.id)
if self.clear_stream:
_report("Clearing stream...")
sstream.objects = placeholders
N = 0
else:
sstream.objects.extend(placeholders)
N = sstream.layers[-1].objectCount
if self.clear_stream:
N = 0
sstream.layers[-1].objectCount = N + len(placeholders)
sstream.layers[-1].topology = "0-%s" % (N + len(placeholders))
res = client.streams.update(sstream.id, sstream)
# Update view layer
context.view_layer.update()
_report("Done.")
metrics.track(
"Connector Action",
None,
custom_props={"name": "UploadNgonsAsPolylines"},
)
return {"FINISHED"}
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
def draw(self, context):
layout = self.layout
layout.prop(self, "clear_stream")
def get_custom_speckle_props(self, context):
active = context.active_object
if not active:
return []
return [(x, "{}".format(x), "") for x in active.keys()]
@deprecated
class SelectIfSameCustomProperty(bpy.types.Operator):
"""
Select scene objects if they have the same custom property
value as the active object
"""
bl_idname = "speckle.select_if_same_custom_props"
bl_label = "Select Identical Custom Props (DEPRECATED)"
bl_options = {"REGISTER", "UNDO"}
custom_prop: EnumProperty(
name="Custom properties",
description="Available streams associated with user.",
items=get_custom_speckle_props,
)
def draw(self, context):
layout = self.layout
col = layout.column()
col.prop(self, "custom_prop")
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
def execute(self, context):
active = context.active_object
if not active:
return {"CANCELLED"}
if self.custom_prop not in active.keys():
return {"CANCELLED"}
value = active[self.custom_prop]
_report(
"Looking for '{}' property with a value of '{}'.".format(
self.custom_prop, value
)
)
for obj in bpy.data.objects:
if self.custom_prop in obj.keys() and obj[self.custom_prop] == value:
obj.select_set(True)
else:
obj.select_set(False)
metrics.track(
"Connector Action",
None,
custom_props={"name": "SelectIfSameCustomProperty"},
)
return {"FINISHED"}
@deprecated
class SelectIfHasCustomProperty(bpy.types.Operator):
"""
Select scene objects if they have the same custom property
as the active object, regardless of the value
"""
bl_idname = "speckle.select_if_has_custom_props"
bl_label = "Select Same Custom Prop (DEPRECATED)"
bl_options = {"REGISTER", "UNDO"}
custom_prop: EnumProperty(
name="Custom properties",
description="Custom properties yo",
items=get_custom_speckle_props,
)
def draw(self, context):
layout = self.layout
col = layout.column()
col.prop(self, "custom_prop")
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
def execute(self, context):
active = context.active_object
if not active:
return {"CANCELLED"}
if self.custom_prop not in active.keys():
return {"CANCELLED"}
_report("Looking for '{}' property.".format(self.custom_prop))
for obj in bpy.data.objects:
if self.custom_prop in obj.keys():
obj.select_set(True)
else:
obj.select_set(False)
metrics.track(
"Connector Action",
None,
custom_props={"name": "SelectIfHasCustomProperty"},
)
return {"FINISHED"}
+29 -181
View File
@@ -1,6 +1,7 @@
"""
Stream operators
"""
import webbrowser
from math import radians
from typing import Callable, Dict, Optional, Tuple, Union, cast
@@ -19,23 +20,25 @@ from specklepy.objects import Base
from specklepy.objects.other import Collection as SCollection
from specklepy.transports.server import ServerTransport
from bpy_speckle.blender_commit_object_builder import \
BlenderCommitObjectBuilder
from bpy_speckle.blender_commit_object_builder import BlenderCommitObjectBuilder
from bpy_speckle.clients import speckle_clients
from bpy_speckle.convert.to_native import (can_convert_to_native,
collection_to_native,
convert_to_native,
set_convert_instances_as)
from bpy_speckle.convert.to_native import (
can_convert_to_native,
collection_to_native,
convert_to_native,
set_convert_instances_as,
)
from bpy_speckle.convert.to_speckle import convert_to_speckle
from bpy_speckle.convert.util import (ConversionSkippedException,
add_to_hierarchy)
from bpy_speckle.functions import (_report, get_default_traversal_func,
get_scale_length)
from bpy_speckle.convert.util import ConversionSkippedException, add_to_hierarchy
from bpy_speckle.functions import _report, get_default_traversal_func, get_scale_length
from bpy_speckle.operators.users import LoadUserStreams, add_user_stream
from bpy_speckle.properties.scene import (SpeckleSceneSettings,
SpeckleStreamObject,
SpeckleUserObject, get_speckle,
selection_state)
from bpy_speckle.properties.scene import (
SpeckleSceneSettings,
SpeckleStreamObject,
SpeckleUserObject,
get_speckle,
selection_state,
)
ObjectCallback = Optional[Callable[[bpy.types.Context, Object, Base], Object]]
ReceiveCompleteCallback = Optional[
@@ -45,9 +48,17 @@ ReceiveCompleteCallback = Optional[
def get_project_workspace_id(client: SpeckleClient, project_id: str) -> Optional[str]:
workspace_id = None
server_version = client.project.server_version or client.server.verison()
maj = server_version[0]
min = server_version[1]
server_version = client.project.server_version or client.server.version()
# Local yarn builds of server will report a server version if "dev"
# We'll assume that local builds are up-to-date with the latest features
if server_version[0] == "dev":
maj = 999
min = 999
else:
maj = server_version[0]
min = server_version[1]
if maj > 2 or (maj == 2 and min > 20):
workspace_id = client.project.get(project_id).workspaceId
return workspace_id
@@ -181,7 +192,7 @@ class ReceiveStreamObjects(bpy.types.Operator):
).slug,
"sourceHostAppVersion": commit.source_application,
"isMultiplayer": commit.author_id != user.id,
"workspace_id": get_project_workspace_id(client, stream.id)
"workspace_id": get_project_workspace_id(client, stream.id),
# "connector_version": "unknown", #TODO
},
)
@@ -647,109 +658,6 @@ class CreateStream(bpy.types.Operator):
)
@deprecated
class DeleteStream(bpy.types.Operator):
"""
Permanently delete the selected project
"""
bl_idname = "speckle.delete_stream"
bl_label = "Delete Project"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Permanently delete the selected project"
are_you_sure: BoolProperty(
name="Confirm",
description="⚠ This action will delete your entire stream permanently ⚠",
default=False,
) # type: ignore
delete_collection: BoolProperty(name="Delete collection", default=False) # type: ignore
def draw(self, context):
layout = self.layout
col = layout.column()
col.prop(self, "are_you_sure")
col.prop(self, "delete_collection")
def invoke(self, context, event):
wm = context.window_manager
speckle = get_speckle(context)
if len(speckle.users) > 0:
return wm.invoke_props_dialog(self)
return {"CANCELLED"}
def execute(self, context):
if not self.are_you_sure:
_report(f"Cancelled by user - are_you_sure was {self.are_you_sure}")
return {"CANCELLED"}
self.are_you_sure = False
self.delete_stream(context, self.delete_collection)
return {"FINISHED"}
@staticmethod
def delete_stream(context: Context, delete_collection: bool) -> None:
speckle = get_speckle(context)
(_, stream) = speckle.validate_stream_selection()
client = speckle_clients[int(speckle.active_user)]
client.stream.delete(id=stream.id)
if delete_collection:
# This may not work anymore since we changed the collection naming...
col_name = "SpeckleStream_{}_{}".format(stream.name, stream.id)
if col_name in bpy.data.collections:
collection = bpy.data.collections[col_name]
bpy.data.collections.remove(collection)
bpy.ops.speckle.load_user_streams()
context.view_layer.update()
if context.area:
context.area.tag_redraw()
metrics.track(
"Connector Action",
client.account,
custom_props={"name": "delete_stream"},
)
@deprecated
class SelectOrphanObjects(bpy.types.Operator):
"""
Select Speckle objects that don't belong to any stream
"""
bl_idname = "speckle.select_orphans"
bl_label = "Select Orphaned Objects (DEPRECATED)"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Select Speckle objects that don't belong to any stream"
def draw(self, context):
layout = self.layout
def execute(self, context):
for o in context.scene.objects:
if (
o.speckle.stream_id
and o.speckle.stream_id not in context.scene["speckle_streams"]
):
o.select = True
else:
o.select = False
metrics.track(
"Connector Action",
custom_props={"name": "SelectOrphanObjects"},
)
return {"FINISHED"}
class CopyStreamId(bpy.types.Operator):
"""
Copy the selected project id to clipboard
@@ -827,63 +735,3 @@ class CopyModelId(bpy.types.Operator):
"Connector Action",
custom_props={"name": "copy_branch_id"},
)
@deprecated
class CopyBranchName(bpy.types.Operator):
"""
Copy branch name to clipboard
"""
bl_idname = "speckle.branch_copy_name"
bl_label = "Copy branch name"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Copy branch name to clipboard"
def execute(self, context):
self.copy_branch_id(context)
return {"FINISHED"}
def copy_branch_id(self, context) -> None:
speckle = get_speckle(context)
(_, _, branch) = speckle.validate_branch_selection()
bpy.context.window_manager.clipboard = branch.name
metrics.track(
"Connector Action",
custom_props={"name": "copy_branch_id"},
)
@deprecated
class SelectOrphanObjects(bpy.types.Operator):
"""
Select Speckle objects that don't belong to any stream
"""
bl_idname = "speckle.select_orphans"
bl_label = "Select orphaned objects"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Select Speckle objects that don't belong to any stream"
def draw(self, context):
pass
def execute(self, context):
for o in context.scene.objects:
if (
o.speckle.stream_id
and o.speckle.stream_id not in context.scene["speckle_streams"]
):
o.select = True
else:
o.select = False
metrics.track(
"Connector Action",
custom_props={"name": "SelectOrphanObjects"},
)
return {"FINISHED"}
+8 -4
View File
@@ -1,6 +1,7 @@
"""
User account operators
"""
from typing import cast
import bpy
@@ -12,10 +13,13 @@ from specklepy.logging import metrics
from bpy_speckle.clients import speckle_clients
from bpy_speckle.functions import _report
from bpy_speckle.properties.scene import (SpeckleSceneSettings,
SpeckleStreamObject,
SpeckleUserObject, get_speckle,
restore_selection_state)
from bpy_speckle.properties.scene import (
SpeckleSceneSettings,
SpeckleStreamObject,
SpeckleUserObject,
get_speckle,
restore_selection_state,
)
class ResetUsers(bpy.types.Operator):
+8 -3
View File
@@ -1,9 +1,14 @@
from .addon import SpeckleAddonPreferences
from .collection import SpeckleCollectionSettings
from .object import SpeckleObjectSettings
from .scene import (SpeckleBranchObject, SpeckleCommitObject,
SpeckleSceneObject, SpeckleSceneSettings,
SpeckleStreamObject, SpeckleUserObject)
from .scene import (
SpeckleBranchObject,
SpeckleCommitObject,
SpeckleSceneObject,
SpeckleSceneSettings,
SpeckleStreamObject,
SpeckleUserObject,
)
property_classes = [
SpeckleSceneObject,
+1
View File
@@ -1,6 +1,7 @@
"""
Addon properties
"""
import bpy
+1
View File
@@ -1,6 +1,7 @@
"""
Collection properties
"""
import bpy
+1
View File
@@ -1,6 +1,7 @@
"""
Object properties
"""
import bpy
+8 -2
View File
@@ -1,12 +1,18 @@
"""
Scene properties
"""
from dataclasses import dataclass
from typing import Iterable, Optional, Tuple, Union, cast
import bpy
from bpy.props import (CollectionProperty, EnumProperty, FloatProperty,
IntProperty, StringProperty)
from bpy.props import (
CollectionProperty,
EnumProperty,
FloatProperty,
IntProperty,
StringProperty,
)
from specklepy.core.api.models import Stream
from bpy_speckle.clients import speckle_clients
+8 -3
View File
@@ -1,7 +1,12 @@
from .object import OBJECT_PT_speckle
from .view3d import (VIEW3D_PT_SpeckleActiveStream, VIEW3D_PT_SpeckleHelp,
VIEW3D_PT_SpeckleStreams, VIEW3D_PT_SpeckleUser,
VIEW3D_UL_SpeckleStreams, VIEW3D_UL_SpeckleUsers)
from .view3d import (
VIEW3D_PT_SpeckleActiveStream,
VIEW3D_PT_SpeckleHelp,
VIEW3D_PT_SpeckleStreams,
VIEW3D_PT_SpeckleUser,
VIEW3D_UL_SpeckleStreams,
VIEW3D_UL_SpeckleUsers,
)
ui_classes = [
VIEW3D_PT_SpeckleUser,
+1 -1
View File
@@ -12,7 +12,7 @@ class OBJECT_PT_speckle(bpy.types.Panel):
# bl_idname = 'OBJECT_PT_speckle'
bl_region_type = "WINDOW"
bl_context = "object"
bl_label = "Speckle"
bl_label = "Speckle (Legacy)"
def draw_header(self, context):
self.layout.prop(context.object.speckle, "enabled", text="")
+11 -5
View File
@@ -2,7 +2,6 @@
Speckle UI elements for the 3d viewport
"""
from datetime import datetime
import bpy
@@ -94,7 +93,7 @@ class VIEW3D_PT_SpeckleUser(bpy.types.Panel):
bl_space_type = "VIEW_3D"
bl_region_type = Region
bl_category = "Speckle"
bl_category = "Speckle (Legacy)"
bl_context = "objectmode"
bl_label = "User Account"
@@ -102,6 +101,13 @@ class VIEW3D_PT_SpeckleUser(bpy.types.Panel):
speckle = get_speckle(context)
layout = self.layout
# Draw a box with the text "Warning: This is a legacy add-on. Download the new connector from https://app.speckle.systems/connectors"
box = layout.box()
box.label(text="Warning: This is a legacy add-on.", icon="ERROR")
box.label(text="Download the new connector from")
box.label(text="https://app.speckle.systems/connectors")
layout.separator()
col = layout.column()
if len(speckle.users) < 1:
@@ -122,7 +128,7 @@ class VIEW3D_PT_SpeckleStreams(bpy.types.Panel):
bl_space_type = "VIEW_3D"
bl_region_type = Region
bl_category = "Speckle"
bl_category = "Speckle (Legacy)"
bl_context = "objectmode"
bl_label = "Projects"
@@ -150,7 +156,7 @@ class VIEW3D_PT_SpeckleActiveStream(bpy.types.Panel):
bl_space_type = "VIEW_3D"
bl_region_type = Region
bl_category = "Speckle"
bl_category = "Speckle (Legacy)"
bl_context = "objectmode"
bl_label = "Active Project"
@@ -246,7 +252,7 @@ class VIEW3D_PT_SpeckleHelp(bpy.types.Panel):
bl_space_type = "VIEW_3D"
bl_region_type = Region
bl_category = "Speckle"
bl_category = "Speckle (Legacy)"
bl_context = "objectmode"
bl_label = "Help"
Generated
+853 -783
View File
File diff suppressed because it is too large Load Diff
+33 -7
View File
@@ -4,21 +4,47 @@ version = "2.0.0"
description = "the Speckle 2.0 connector for Blender!"
authors = ["izzy lyseggen <izzy.lyseggen@gmail.com>", "Gergő Jedlicska <gergo@jedlicska.com>"]
license = "Apache-2.0"
package-mode = false
[tool.poetry.requires-plugins]
poetry-plugin-export = ">=1.8"
[tool.poetry.dependencies]
python = ">=3.8, <4.0.0"
specklepy = "^2.20.1"
attrs = "^23.1.0"
python = ">=3.9.0, <4.0.0"
specklepy = "^2.23.0"
# [tool.poetry.group.local_specklepy.dependencies]
# specklepy = {path = "../specklepy", develop = true}
[tool.poetry.group.dev.dependencies]
fake-bpy-module-latest = "^20240524"
black = "24.3.0"
pylint = "^2.15.7"
ruff = "^0.4.4"
fake-bpy-module-latest = "^20241010"
black = "24.10.0"
isort = "^5.13.2"
pylint = "^3.3.2"
ruff = "^0.8.2"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
[tool.black]
exclude = '''
/(
\.eggs
| \.git
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| _build
| buck-out
| build
| dist
)/
'''
include = '\.pyi?$'
line-length = 88
target-version = ["py39", "py310", "py311", "py312", "py313"]
[tool.isort]
profile = "black"