Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 97d20ad7b1 | |||
| 511d69314e | |||
| 21281e5d77 | |||
| 29bbdc69a2 | |||
| efe6e6a4a0 | |||
| f036109020 |
+37
-14
@@ -71,24 +71,44 @@ jobs:
|
||||
build-installer-win:
|
||||
executor:
|
||||
name: win/default
|
||||
shell: cmd.exe
|
||||
environment:
|
||||
SSM: 'C:\Program Files\DigiCert\DigiCert One Signing Manager Tools'
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: ./
|
||||
- run:
|
||||
name: Patch installer
|
||||
shell: powershell.exe
|
||||
command: python patch_installer.py (Get-Content -Raw SEMVER)
|
||||
- run:
|
||||
name: Create Innosetup signing cert
|
||||
shell: powershell.exe
|
||||
command: |
|
||||
echo $env:PFX_B64 > "speckle-sharp-ci-tools\SignTool\AEC Systems Ltd.txt"
|
||||
certutil -decode "speckle-sharp-ci-tools\SignTool\AEC Systems Ltd.txt" "speckle-sharp-ci-tools\SignTool\AEC Systems Ltd.pfx"
|
||||
- run:
|
||||
name: Installer
|
||||
shell: cmd.exe #does not work in powershell
|
||||
command: speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\blender.iss /Sbyparam=$p
|
||||
- unless: # Build installers unsigned on non-tagged builds
|
||||
condition: << pipeline.git.tag >>
|
||||
steps:
|
||||
- run:
|
||||
name: Build Installer
|
||||
command: speckle-sharp-ci-tools\InnoSetup\ISCC.exe speckle-sharp-ci-tools\blender.iss /Sbyparam=$p
|
||||
shell: cmd.exe #does not work in powershell
|
||||
- when: # Setup certificates and build installers signed for tagged builds
|
||||
condition: << pipeline.git.tag >>
|
||||
steps:
|
||||
- run:
|
||||
name: "Digicert Signing Manager Setup"
|
||||
command: |
|
||||
cd C:\
|
||||
curl.exe -X GET https://one.digicert.com/signingmanager/api-ui/v1/releases/smtools-windows-x64.msi/download -H "x-api-key:$env:SM_API_KEY" -o smtools-windows-x64.msi
|
||||
msiexec.exe /i smtools-windows-x64.msi /quiet /qn | Wait-Process
|
||||
- run:
|
||||
name: Create Auth & OV Signing Cert
|
||||
command: |
|
||||
cd C:\
|
||||
echo $env:SM_CLIENT_CERT_FILE_B64 > certificate.txt
|
||||
certutil -decode certificate.txt certificate.p12
|
||||
- run:
|
||||
name: Sync Certs
|
||||
command: |
|
||||
& $env:SSM\smksp_cert_sync.exe
|
||||
- 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%
|
||||
shell: cmd.exe #does not work in powershell
|
||||
- persist_to_workspace:
|
||||
root: ./
|
||||
paths:
|
||||
@@ -109,6 +129,9 @@ jobs:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: ./
|
||||
- run:
|
||||
name: Exit if External PR
|
||||
command: if [ "$CIRCLE_PR_REPONAME" ]; then circleci-agent step halt; fi
|
||||
- run:
|
||||
name: Install mono
|
||||
command: |
|
||||
@@ -214,7 +237,7 @@ workflows:
|
||||
filters: *build_filters
|
||||
|
||||
- build-installer-win:
|
||||
context: innosetup
|
||||
context: digicert-keylocker
|
||||
name: Windows Installer Build
|
||||
requires:
|
||||
- package-connector
|
||||
@@ -304,4 +327,4 @@ workflows:
|
||||
- Windows Installer Build
|
||||
- Mac Intel Build
|
||||
- Mac ARM Build
|
||||
filters: *deploy_filters
|
||||
filters: *deploy_filters
|
||||
|
||||
@@ -41,7 +41,7 @@ class BlenderCommitObjectBuilder(CommitObjectBuilder[Object]):
|
||||
# Set the Child -> Parent relationships
|
||||
parent = native_object.parent
|
||||
|
||||
parent_collections: Tuple[Collection] = native_object.users_collection # type: ignore
|
||||
parent_collections = native_object.users_collection
|
||||
parent_collection = parent_collections[0] if len(parent_collections) > 0 else None #NOTE: we don't support objects appearing in more than one collection, for now, we will just take the zeroth one
|
||||
|
||||
app_id = _id(native_object)
|
||||
@@ -79,7 +79,7 @@ class BlenderCommitObjectBuilder(CommitObjectBuilder[Object]):
|
||||
# Create all collections
|
||||
root_col = self.ensure_collection(bpy.context.scene.collection)
|
||||
root_col.collectionType = "Scene Collection"
|
||||
for col in bpy.context.scene.collection.children_recursive: #type: ignore
|
||||
for col in bpy.context.scene.collection.children_recursive:
|
||||
self.ensure_collection(col)
|
||||
|
||||
objects_to_build = set(self.converted.values())
|
||||
|
||||
@@ -540,7 +540,7 @@ def transform_to_native(transform: Transform, scale: float) -> MMatrix:
|
||||
)
|
||||
# scale the translation
|
||||
for i in range(3):
|
||||
mat[i][3] *= scale # type: ignore
|
||||
mat[i][3] *= scale
|
||||
return mat
|
||||
|
||||
def plane_to_native_transform(plane: Plane, fallback_scale:float = 1) -> MMatrix:
|
||||
|
||||
@@ -34,7 +34,7 @@ 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
|
||||
|
||||
CAN_CONVERT_TO_SPECKLE = ("MESH", "CURVE", "EMPTY", "CAMERA")
|
||||
CAN_CONVERT_TO_SPECKLE = ("MESH", "CURVE", "EMPTY", "CAMERA", "FONT", "SURFACE", "META")
|
||||
|
||||
|
||||
def convert_to_speckle(raw_blender_object: Object, units_scale: float, units: str, depsgraph: Optional[Depsgraph]) -> Base:
|
||||
@@ -69,6 +69,8 @@ def convert_to_speckle(raw_blender_object: Object, units_scale: float, units: st
|
||||
converted = empty_to_speckle(blender_object)
|
||||
elif blender_type == "CAMERA":
|
||||
converted = camera_to_speckle_view(blender_object, cast(NCamera, blender_object.data))
|
||||
elif blender_type == "FONT" or "SURFACE" or "META":
|
||||
converted = anything_to_speckle_mesh(blender_object)
|
||||
if not converted:
|
||||
raise Exception("Conversion returned None")
|
||||
|
||||
@@ -99,7 +101,7 @@ def mesh_to_speckle_meshes(blender_object: Object, data: bpy.types.Mesh) -> List
|
||||
submesh_data[p.material_index].append(p)
|
||||
|
||||
transform = cast(MMatrix, blender_object.matrix_world)
|
||||
scaled_vertices = [tuple(transform @ x.co * UnitsScale) for x in data.vertices] # type: ignore
|
||||
scaled_vertices = [tuple(transform @ x.co * UnitsScale) for x in data.vertices]
|
||||
|
||||
# Create Speckle meshes for each material
|
||||
submeshes = []
|
||||
@@ -370,6 +372,12 @@ def curve_to_speckle_geometry(blender_object: Object, data: bpy.types.Curve) ->
|
||||
|
||||
return (meshes, curves)
|
||||
|
||||
def anything_to_speckle_mesh(blender_object: Object) -> Base:
|
||||
|
||||
mesh = mesh_to_speckle(blender_object, blender_object.to_mesh())
|
||||
blender_object.to_mesh_clear()
|
||||
return mesh
|
||||
|
||||
@deprecated
|
||||
def ngons_to_speckle_polylines(blender_object: Object, data: bpy.types.Mesh) -> Optional[List[Polyline]]:
|
||||
UNITS = "m" if bpy.context.scene.unit_settings.system == "METRIC" else "ft"
|
||||
@@ -437,8 +445,8 @@ def camera_to_speckle_view(blender_object: Object, data: NCamera) -> Base:
|
||||
raise Exception(f"Cameras of type {data.type} are not currently supported")
|
||||
|
||||
matrix = cast(MMatrix, blender_object.matrix_world)
|
||||
up = matrix.col[1].xyz # type: ignore
|
||||
forwards = -matrix.col[2].xyz # type: ignore
|
||||
up = cast(MVector, matrix.col[1].xyz)
|
||||
forwards = cast(MVector, -matrix.col[2].xyz)
|
||||
translation = matrix.translation
|
||||
|
||||
view = Base.of_type("Objects.BuiltElements.View:Objects.BuiltElements.View3D") #HACK: views are not in specklepy yet!
|
||||
|
||||
@@ -168,7 +168,7 @@ def add_faces(speckle_mesh: Mesh, blender_mesh: BMesh, indexOffset: int, materia
|
||||
i += 1
|
||||
try:
|
||||
f = blender_mesh.faces.new(
|
||||
[blender_mesh.verts[x + indexOffset] for x in sfaces[i : i + n]] # type: ignore
|
||||
[blender_mesh.verts[x + indexOffset] for x in sfaces[i : i + n]]
|
||||
)
|
||||
f.material_index = materialIndex
|
||||
f.smooth = smooth
|
||||
@@ -198,10 +198,10 @@ def add_colors(speckle_mesh: Mesh, blender_mesh: BMesh):
|
||||
)
|
||||
|
||||
# Make vertex colors
|
||||
if len(scolors) == len(blender_mesh.verts): # type: ignore
|
||||
if len(scolors) == len(blender_mesh.verts):
|
||||
color_layer = blender_mesh.loops.layers.color.new("Col")
|
||||
|
||||
for face in blender_mesh.faces: # type: ignore
|
||||
for face in blender_mesh.faces:
|
||||
for loop in face.loops:
|
||||
loop[color_layer] = colors[loop.vert.index]
|
||||
|
||||
@@ -220,21 +220,21 @@ def add_uv_coords(speckle_mesh: Mesh, blender_mesh: BMesh):
|
||||
try:
|
||||
uv = []
|
||||
|
||||
if len(s_uvs) // 2 == len(blender_mesh.verts): # type: ignore
|
||||
if len(s_uvs) // 2 == len(blender_mesh.verts):
|
||||
uv.extend(
|
||||
(float(s_uvs[i]), float(s_uvs[i + 1]))
|
||||
for i in range(0, len(s_uvs), 2)
|
||||
)
|
||||
else:
|
||||
_report(
|
||||
f"Failed to match UV coordinates to vert data. Blender mesh verts: {len(blender_mesh.verts)}, Speckle UVs: {len(s_uvs) // 2}" # type: ignore
|
||||
f"Failed to match UV coordinates to vert data. Blender mesh verts: {len(blender_mesh.verts)}, Speckle UVs: {len(s_uvs) // 2}"
|
||||
)
|
||||
return
|
||||
|
||||
# Make UVs
|
||||
uv_layer = blender_mesh.loops.layers.uv.verify()
|
||||
|
||||
for f in blender_mesh.faces: # type: ignore
|
||||
for f in blender_mesh.faces:
|
||||
for l in f.loops:
|
||||
luv = l[uv_layer]
|
||||
luv.uv = uv[l.vert.index]
|
||||
@@ -450,7 +450,7 @@ def link_object_to_collection_nested(obj: Object, col: BCollection):
|
||||
if obj.name not in col.objects: #type: ignore
|
||||
col.objects.link(obj)
|
||||
|
||||
for child in obj.children: #type: ignore
|
||||
for child in obj.children:
|
||||
link_object_to_collection_nested(child, col)
|
||||
|
||||
def add_to_hierarchy(converted: Union[Object, BCollection], traversalContext : 'TraversalContext', converted_objects: Dict[str, Union[Object, BCollection]], preserve_transform: bool) -> None:
|
||||
|
||||
@@ -4,6 +4,7 @@ Commit operators
|
||||
import bpy
|
||||
from bpy.props import BoolProperty
|
||||
from bpy_speckle.clients import speckle_clients
|
||||
from bpy_speckle.functions import _report
|
||||
from bpy_speckle.properties.scene import get_speckle
|
||||
from specklepy.logging import metrics
|
||||
|
||||
@@ -38,20 +39,16 @@ class DeleteCommit(bpy.types.Operator):
|
||||
return {"CANCELLED"}
|
||||
|
||||
def execute(self, context):
|
||||
try:
|
||||
self.delete_commit(context)
|
||||
return {"FINISHED"}
|
||||
except Exception as ex:
|
||||
print(f"{self.bl_idname}: failed: {ex}")
|
||||
return {"CANCELLED"}
|
||||
|
||||
def delete_commit(self, context: bpy.types.Context) -> None:
|
||||
|
||||
if not self.are_you_sure:
|
||||
raise Exception("Cancelled by user")
|
||||
|
||||
_report("Cancelled by user")
|
||||
return {"CANCELLED"}
|
||||
self.are_you_sure = False
|
||||
|
||||
self.delete_commit(context)
|
||||
return {"FINISHED"}
|
||||
|
||||
@staticmethod
|
||||
def delete_commit(context: bpy.types.Context) -> None:
|
||||
speckle = get_speckle(context)
|
||||
|
||||
(_, stream, _, commit) = speckle.validate_commit_selection()
|
||||
@@ -71,5 +68,5 @@ class DeleteCommit(bpy.types.Operator):
|
||||
if not deleted:
|
||||
raise Exception("Delete operation failed")
|
||||
|
||||
print(f"{self.bl_idname}: succeeded - commit {commit.id} ({commit.message}) has been deleted from stream {stream.id}")
|
||||
print(f"Commit {commit.id} ({commit.message}) has been deleted from stream {stream.id}")
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ import webbrowser
|
||||
from specklepy.logging import metrics
|
||||
|
||||
|
||||
|
||||
|
||||
class OpenSpeckleGuide(bpy.types.Operator):
|
||||
bl_idname = "speckle.open_speckle_guide"
|
||||
bl_label = "Speckle Guide"
|
||||
|
||||
@@ -13,6 +13,7 @@ from bpy_speckle.functions import get_scale_length, _report
|
||||
from bpy_speckle.clients import speckle_clients
|
||||
from specklepy.logging import metrics
|
||||
|
||||
@deprecated
|
||||
class UpdateObject(bpy.types.Operator):
|
||||
"""
|
||||
Update local (receive) or remote (send) object depending on
|
||||
@@ -68,7 +69,7 @@ class UpdateObject(bpy.types.Operator):
|
||||
return {"CANCELLED"}
|
||||
return {"CANCELLED"}
|
||||
|
||||
|
||||
@deprecated
|
||||
class ResetObject(bpy.types.Operator):
|
||||
"""
|
||||
Reset Speckle object settings
|
||||
@@ -96,7 +97,7 @@ class ResetObject(bpy.types.Operator):
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@deprecated
|
||||
class DeleteObject(bpy.types.Operator):
|
||||
"""
|
||||
Delete object from the server and update relevant stream
|
||||
@@ -247,7 +248,7 @@ def get_custom_speckle_props(self, context):
|
||||
|
||||
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
|
||||
@@ -307,7 +308,7 @@ class SelectIfSameCustomProperty(bpy.types.Operator):
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@deprecated
|
||||
class SelectIfHasCustomProperty(bpy.types.Operator):
|
||||
"""
|
||||
Select scene objects if they have the same custom property
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Stream operators
|
||||
"""
|
||||
from math import radians
|
||||
from typing import Callable, Dict, Optional, Union, cast
|
||||
from typing import Callable, Dict, Optional, Tuple, Union, cast
|
||||
import webbrowser
|
||||
import bpy
|
||||
from bpy.props import (
|
||||
@@ -15,6 +15,7 @@ from bpy.types import (
|
||||
Object,
|
||||
Collection
|
||||
)
|
||||
from deprecated import deprecated
|
||||
from bpy_speckle.blender_commit_object_builder import BlenderCommitObjectBuilder
|
||||
from bpy_speckle.convert.to_native import (
|
||||
can_convert_to_native,
|
||||
@@ -31,8 +32,8 @@ from bpy_speckle.functions import (
|
||||
get_scale_length,
|
||||
)
|
||||
from bpy_speckle.clients import speckle_clients
|
||||
from bpy_speckle.operators.users import add_user_stream
|
||||
from bpy_speckle.properties.scene import SpeckleSceneSettings, SpeckleUserObject, get_speckle
|
||||
from bpy_speckle.operators.users import LoadUserStreams, add_user_stream
|
||||
from bpy_speckle.properties.scene import SpeckleSceneSettings, SpeckleStreamObject, SpeckleUserObject, get_speckle
|
||||
from bpy_speckle.convert.util import ConversionSkippedException, add_to_hierarchy
|
||||
from specklepy.core.api.models import Commit
|
||||
from specklepy.core.api import operations, host_applications
|
||||
@@ -88,11 +89,10 @@ class ReceiveStreamObjects(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Receive objects from active stream"
|
||||
|
||||
|
||||
clean_meshes: BoolProperty(name="Clean Meshes", default=False)
|
||||
clean_meshes: BoolProperty(name="Clean Meshes", default=False) # type: ignore
|
||||
|
||||
#receive_mode: EnumProperty(items=RECEIVE_MODES, name="Receive Type", default="replace", description="The behaviour of the receive operation")
|
||||
receive_instances_as: EnumProperty(items=INSTANCES_SETTINGS, name="Receive Instances As", default="collection_instance", description="How to receive speckle Instances")
|
||||
receive_instances_as: EnumProperty(items=INSTANCES_SETTINGS, name="Receive Instances As", default="collection_instance", description="How to receive speckle Instances") # type: ignore
|
||||
|
||||
|
||||
def draw(self, context):
|
||||
@@ -130,23 +130,19 @@ class ReceiveStreamObjects(bpy.types.Operator):
|
||||
# Reset state to previous (not quite sure if this is 100% necessary)
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
bpy.context.view_layer.objects.active = None
|
||||
bpy.context.view_layer.objects.active = None # type: ignore
|
||||
|
||||
def execute(self, context):
|
||||
try:
|
||||
self.receive(context)
|
||||
return {"FINISHED"}
|
||||
except Exception as ex:
|
||||
_report(f"Failed to receive objects: {type(ex)} {ex}")
|
||||
return {"CANCELLED"}
|
||||
self.receive(context)
|
||||
return {"FINISHED"}
|
||||
|
||||
def receive(self, context: Context) -> None:
|
||||
bpy.context.view_layer.objects.active = None
|
||||
bpy.context.view_layer.objects.active = None # type: ignore
|
||||
|
||||
speckle = get_speckle(context)
|
||||
|
||||
(user, stream, branch, commit) = speckle.validate_commit_selection()
|
||||
|
||||
|
||||
client = speckle_clients[int(speckle.active_user)]
|
||||
|
||||
transport = ServerTransport(stream.id, client)
|
||||
@@ -201,7 +197,8 @@ class ReceiveStreamObjects(bpy.types.Operator):
|
||||
|
||||
if can_convert_to_native(current) or isinstance(current, SCollection):
|
||||
try:
|
||||
if not current or not current.id: raise Exception(f"{current} was an invalid speckle object")
|
||||
if not current or not current.id:
|
||||
raise Exception(f"{current} was an invalid speckle object")
|
||||
|
||||
#Convert the object!
|
||||
converted_data_type: str
|
||||
@@ -256,11 +253,11 @@ class SendStreamObjects(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Send selected objects to active stream"
|
||||
|
||||
apply_modifiers: BoolProperty(name="Apply modifiers", default=True)
|
||||
apply_modifiers: BoolProperty(name="Apply modifiers", default=True) # type: ignore
|
||||
commit_message: StringProperty(
|
||||
name="Message",
|
||||
default="Pushed elements from Blender.",
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
@@ -270,8 +267,11 @@ class SendStreamObjects(bpy.types.Operator):
|
||||
|
||||
def invoke(self, context, event):
|
||||
wm = context.window_manager
|
||||
if len(context.scene.speckle.users) <= 0: return {"CANCELLED"}
|
||||
|
||||
speckle = get_speckle(context)
|
||||
if len(speckle.users) <= 0:
|
||||
_report("No user accounts")
|
||||
return {"CANCELLED"}
|
||||
|
||||
N = len(context.selected_objects)
|
||||
if N == 1:
|
||||
self.commit_message = f"Pushed {N} element from Blender."
|
||||
@@ -281,12 +281,8 @@ class SendStreamObjects(bpy.types.Operator):
|
||||
|
||||
|
||||
def execute(self, context):
|
||||
try:
|
||||
self.send(context)
|
||||
return {"FINISHED"}
|
||||
except Exception as ex:
|
||||
_report(f"Send failed: {ex}")
|
||||
return {"CANCELLED"}
|
||||
self.send(context)
|
||||
return {"FINISHED"}
|
||||
|
||||
def send(self, context: Context) -> None:
|
||||
|
||||
@@ -376,7 +372,13 @@ class SendStreamObjects(bpy.types.Operator):
|
||||
message=self.commit_message,
|
||||
source_application="blender",
|
||||
)
|
||||
_report(f"Commit Created {user.server_url}/streams/{stream.id}/commits/{COMMIT_ID}")
|
||||
|
||||
if client.account.serverInfo.frontend2:
|
||||
sent_url = f"{user.server_url}/projects/{stream.id}/models/{branch.id}@{COMMIT_ID}"
|
||||
else:
|
||||
sent_url = f"{user.server_url}/streams/{stream.id}/commits/{COMMIT_ID}"
|
||||
|
||||
_report(f"Commit Created {sent_url}")
|
||||
|
||||
bpy.ops.speckle.load_user_streams() # refresh loaded commits
|
||||
context.view_layer.update()
|
||||
@@ -393,19 +395,21 @@ class ViewStreamDataApi(bpy.types.Operator):
|
||||
bl_description = "View the stream in the web browser"
|
||||
|
||||
def execute(self, context):
|
||||
try:
|
||||
self.view_stream_data_api(context)
|
||||
return {"FINISHED"}
|
||||
except Exception as ex:
|
||||
_report(f"{self.bl_idname} failed: {ex}")
|
||||
return {"CANCELLED"}
|
||||
self.view_stream_data_api(context)
|
||||
return {"FINISHED"}
|
||||
|
||||
def view_stream_data_api(self, context: Context) -> None:
|
||||
speckle = get_speckle(context)
|
||||
|
||||
(user, stream) = speckle.validate_stream_selection()
|
||||
|
||||
if not webbrowser.open("%s/streams/%s" % (user.server_url, stream.id), new=2):
|
||||
|
||||
client = speckle_clients[int(speckle.active_user)]
|
||||
if client.account.serverInfo.frontend2:
|
||||
stream_url = f"{user.server_url}/projects/{stream.id}"
|
||||
else:
|
||||
stream_url= f"{user.server_url}/streams/{stream.id}"
|
||||
|
||||
if not webbrowser.open(stream_url, new=2):
|
||||
raise Exception("Failed to open stream in browser")
|
||||
|
||||
metrics.track(
|
||||
@@ -428,7 +432,7 @@ class AddStreamFromURL(bpy.types.Operator):
|
||||
bl_description = "Add an existing stream by providing its URL"
|
||||
stream_url: StringProperty(
|
||||
name="Stream URL", default="https://speckle.xyz/streams/3073b96e86"
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
@@ -444,13 +448,26 @@ class AddStreamFromURL(bpy.types.Operator):
|
||||
return {"CANCELLED"}
|
||||
|
||||
def execute(self, context):
|
||||
try:
|
||||
self.add_stream_from_url(context)
|
||||
return {"FINISHED"}
|
||||
except Exception as ex:
|
||||
_report(f"{self.bl_idname} failed: {ex}")
|
||||
return {"CANCELLED"}
|
||||
self.add_stream_from_url(context)
|
||||
return {"FINISHED"}
|
||||
|
||||
@staticmethod
|
||||
def _get_or_add_stream(user : SpeckleUserObject, stream : Stream) -> Tuple[int, SpeckleStreamObject]:
|
||||
index, b_stream = next(
|
||||
((i, cast(SpeckleStreamObject, s)) for i, s in enumerate(user.streams) if s.id == stream.id),
|
||||
(None, None),
|
||||
)
|
||||
|
||||
if index is not None:
|
||||
assert(b_stream)
|
||||
return (index, b_stream)
|
||||
|
||||
add_user_stream(user, stream)
|
||||
return next(
|
||||
(i, cast(SpeckleStreamObject, s)) for i, s in enumerate(user.streams) if s.id == stream.id
|
||||
)
|
||||
|
||||
|
||||
def add_stream_from_url(self, context: Context) -> None:
|
||||
speckle = get_speckle(context)
|
||||
|
||||
@@ -460,28 +477,20 @@ class AddStreamFromURL(bpy.types.Operator):
|
||||
None,
|
||||
)
|
||||
if user_index is None:
|
||||
raise Exception("Unable to find user stream server")
|
||||
raise Exception(f"No user account credentials for {wrapper.host}, have you added your account in Manager?")
|
||||
|
||||
speckle.active_user = str(user_index)
|
||||
user = cast(SpeckleUserObject, speckle.users[user_index])
|
||||
|
||||
client = speckle_clients[user_index]
|
||||
stream = client.stream.get(wrapper.stream_id, branch_limit=20)
|
||||
stream = client.stream.get(wrapper.stream_id, branch_limit=LoadUserStreams.branch_limit, commit_limit=LoadUserStreams.commits_limit)
|
||||
if not isinstance(stream, Stream):
|
||||
raise SpeckleException("Could not get the requested stream")
|
||||
raise SpeckleException(f"Could not get the requested stream {wrapper.stream_id}")
|
||||
|
||||
index, b_stream = next(
|
||||
((i, s) for i, s in enumerate(user.streams) if s.id == stream.id),
|
||||
(None, None),
|
||||
)
|
||||
(index, b_stream) = self._get_or_add_stream(user, stream)
|
||||
user.active_stream = index
|
||||
|
||||
if index is None:
|
||||
add_user_stream(user, stream)
|
||||
user.active_stream, b_stream = next(
|
||||
(i, s) for i, s in enumerate(user.streams) if s.id == stream.id
|
||||
)
|
||||
else:
|
||||
user.active_stream = index
|
||||
_report(f"Selecting stream at index {index} ({b_stream.id} - {b_stream.name})")
|
||||
|
||||
if wrapper.branch_name:
|
||||
b_index = b_stream.branches.find(wrapper.branch_name)
|
||||
@@ -521,10 +530,10 @@ class CreateStream(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Create new stream"
|
||||
|
||||
stream_name: StringProperty(name="Stream name")
|
||||
stream_name: StringProperty(name="Stream name") # type: ignore
|
||||
stream_description: StringProperty(
|
||||
name="Stream description", default="This is a Blender stream."
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
@@ -541,12 +550,8 @@ class CreateStream(bpy.types.Operator):
|
||||
return {"CANCELLED"}
|
||||
|
||||
def execute(self, context):
|
||||
try:
|
||||
self.create_stream(context)
|
||||
return {"FINISHED"}
|
||||
except Exception as ex:
|
||||
_report(f"{self.bl_idname} failed: {ex}")
|
||||
return {"CANCELLED"}
|
||||
self.create_stream(context)
|
||||
return {"FINISHED"}
|
||||
|
||||
def create_stream(self, context: Context) -> None:
|
||||
speckle = get_speckle(context)
|
||||
@@ -579,6 +584,7 @@ class CreateStream(bpy.types.Operator):
|
||||
)
|
||||
|
||||
|
||||
@deprecated
|
||||
class DeleteStream(bpy.types.Operator):
|
||||
"""
|
||||
Delete stream
|
||||
@@ -591,10 +597,11 @@ class DeleteStream(bpy.types.Operator):
|
||||
|
||||
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)
|
||||
delete_collection: BoolProperty(name="Delete collection", default=False) # type: ignore
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
@@ -611,19 +618,16 @@ class DeleteStream(bpy.types.Operator):
|
||||
return {"CANCELLED"}
|
||||
|
||||
def execute(self, context):
|
||||
try:
|
||||
self.delete_stream(context)
|
||||
return {"FINISHED"}
|
||||
except Exception as ex:
|
||||
_report(f"{self.bl_idname} failed: {ex}")
|
||||
return {"CANCELLED"}
|
||||
|
||||
def delete_stream(self, context: Context) -> None:
|
||||
if not self.are_you_sure:
|
||||
raise Exception("Cancelled by user")
|
||||
|
||||
_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()
|
||||
|
||||
@@ -631,7 +635,8 @@ class DeleteStream(bpy.types.Operator):
|
||||
|
||||
client.stream.delete(id=stream.id)
|
||||
|
||||
if self.delete_collection:
|
||||
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]
|
||||
@@ -651,7 +656,7 @@ class DeleteStream(bpy.types.Operator):
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@deprecated
|
||||
class SelectOrphanObjects(bpy.types.Operator):
|
||||
"""
|
||||
Select Speckle objects that don't belong to any stream
|
||||
@@ -696,12 +701,8 @@ class CopyStreamId(bpy.types.Operator):
|
||||
bl_description = "Copy stream ID to clipboard"
|
||||
|
||||
def execute(self, context):
|
||||
try:
|
||||
self.copy_stream_id(context)
|
||||
return {"FINISHED"}
|
||||
except Exception as ex:
|
||||
_report(f"{self.bl_idname} failed: {ex}")
|
||||
return {"CANCELLED"}
|
||||
self.copy_stream_id(context)
|
||||
return {"FINISHED"}
|
||||
|
||||
def copy_stream_id(self, context) -> None:
|
||||
speckle = get_speckle(context)
|
||||
@@ -727,12 +728,9 @@ class CopyCommitId(bpy.types.Operator):
|
||||
bl_description = "Copy commit ID to clipboard"
|
||||
|
||||
def execute(self, context):
|
||||
try:
|
||||
self.copy_commit_id(context)
|
||||
return {"FINISHED"}
|
||||
except Exception as ex:
|
||||
_report(f"{self.bl_idname} failed: {ex}")
|
||||
return {"CANCELLED"}
|
||||
self.copy_commit_id(context)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
def copy_commit_id(self, context) -> None:
|
||||
speckle = get_speckle(context)
|
||||
@@ -760,12 +758,9 @@ class CopyBranchName(bpy.types.Operator):
|
||||
bl_description = "Copy branch name to clipboard"
|
||||
|
||||
def execute(self, context):
|
||||
try:
|
||||
self.copy_branch_id(context)
|
||||
return {"FINISHED"}
|
||||
except Exception as ex:
|
||||
_report(f"{self.bl_idname} failed: {ex}")
|
||||
return {"CANCELLED"}
|
||||
self.copy_branch_id(context)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
def copy_branch_id(self, context) -> None:
|
||||
speckle = get_speckle(context)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
"""
|
||||
User account operators
|
||||
"""
|
||||
from typing import cast
|
||||
from typing import List, cast
|
||||
import bpy
|
||||
from bpy.types import Context
|
||||
from bpy_speckle.functions import _report
|
||||
from bpy_speckle.clients import speckle_clients
|
||||
from bpy_speckle.properties.scene import SpeckleCommitObject, SpeckleSceneSettings, SpeckleStreamObject, SpeckleUserObject, get_speckle
|
||||
from bpy_speckle.properties.scene import SpeckleBranchObject, SpeckleCommitObject, SpeckleSceneSettings, SpeckleStreamObject, SpeckleUserObject, get_speckle
|
||||
from specklepy.core.api.client import SpeckleClient
|
||||
from specklepy.core.api.models import Stream
|
||||
from specklepy.core.api.credentials import get_local_accounts, Account
|
||||
@@ -57,7 +57,7 @@ class LoadUsers(bpy.types.Operator):
|
||||
|
||||
_report("Loading users...")
|
||||
|
||||
speckle = cast(SpeckleSceneSettings, context.scene.speckle) #type: ignore
|
||||
speckle = get_speckle(context)
|
||||
users_list = speckle.users
|
||||
|
||||
ResetUsers.reset_ui(context)
|
||||
@@ -133,13 +133,17 @@ def add_user_stream(user: SpeckleUserObject, stream: Stream):
|
||||
s.id = stream.id
|
||||
s.description = stream.description
|
||||
|
||||
_report(f"Adding stream {s.id} - {s.name}")
|
||||
|
||||
if not stream.branches:
|
||||
return
|
||||
|
||||
# branches = [branch for branch in stream.branches.items if branch.name != "globals"]
|
||||
for b in stream.branches.items:
|
||||
branch = s.branches.add()
|
||||
branch = cast(SpeckleBranchObject, s.branches.add())
|
||||
branch.name = b.name
|
||||
branch.id = b.id
|
||||
branch.description = b.description or ""
|
||||
|
||||
if not b.commits:
|
||||
continue
|
||||
@@ -171,15 +175,13 @@ class LoadUserStreams(bpy.types.Operator):
|
||||
bl_description = "(Re)load all available user streams"
|
||||
|
||||
stream_limit: int = 20
|
||||
branch_limit: int = 20
|
||||
branch_limit: int = 100
|
||||
commits_limit: int = 10
|
||||
|
||||
def execute(self, context):
|
||||
try:
|
||||
self.load_user_stream(context)
|
||||
return {"FINISHED"}
|
||||
except Exception as ex:
|
||||
_report(f"{self.bl_idname} failed: {ex}")
|
||||
return {"CANCELLED"}
|
||||
self.load_user_stream(context)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
def load_user_stream(self, context: Context) -> None:
|
||||
speckle = get_speckle(context)
|
||||
@@ -193,14 +195,14 @@ class LoadUserStreams(bpy.types.Operator):
|
||||
raise Exception(f"Failed to retrieve streams") from ex
|
||||
|
||||
if not streams:
|
||||
raise Exception("Zero streams found")
|
||||
_report("Zero streams found")
|
||||
return
|
||||
|
||||
user.streams.clear()
|
||||
|
||||
for s in streams:
|
||||
assert(s.id)
|
||||
sstream = client.stream.get(id=s.id, branch_limit=self.branch_limit)
|
||||
sstream = client.stream.get(id=s.id, branch_limit=self.branch_limit, commit_limit=10)
|
||||
add_user_stream(user, sstream)
|
||||
|
||||
bpy.context.view_layer.update()
|
||||
|
||||
@@ -37,6 +37,8 @@ class SpeckleBranchObject(bpy.types.PropertyGroup):
|
||||
return [("0", "<none>", "<none>", 0)]
|
||||
|
||||
name: StringProperty(default="main")
|
||||
id: StringProperty(default="")
|
||||
description: StringProperty(default="")
|
||||
commits: CollectionProperty(type=SpeckleCommitObject)
|
||||
commit: EnumProperty(
|
||||
name="Commit",
|
||||
|
||||
@@ -10,8 +10,9 @@ from bpy.props import (
|
||||
CollectionProperty,
|
||||
EnumProperty,
|
||||
)
|
||||
from deprecated import deprecated
|
||||
|
||||
|
||||
@deprecated
|
||||
class OBJECT_PT_speckle(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
# bl_idname = 'OBJECT_PT_speckle'
|
||||
|
||||
@@ -149,7 +149,6 @@ class VIEW3D_PT_SpeckleStreams(bpy.types.Panel):
|
||||
row = col.row(align=True)
|
||||
row.operator("speckle.add_stream_from_url", text="", icon="URL")
|
||||
row.operator("speckle.create_stream", text="", icon="ADD")
|
||||
row.operator("speckle.delete_stream", text="", icon="REMOVE")
|
||||
row.operator("speckle.load_user_streams", text="", icon="FILE_REFRESH")
|
||||
|
||||
|
||||
@@ -225,7 +224,6 @@ class VIEW3D_PT_SpeckleActiveStream(bpy.types.Panel):
|
||||
subcol = row.column()
|
||||
subcol.operator("speckle.send_stream_objects", text="Send")
|
||||
subcol.prop(speckle, "send_script", text="")
|
||||
area.prop(stream, "query", text="Filter")
|
||||
|
||||
col.separator()
|
||||
|
||||
|
||||
Generated
+555
-539
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -7,15 +7,15 @@ license = "Apache-2.0"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.8, <4.0.0"
|
||||
specklepy = "^2.17.14"
|
||||
specklepy = "^2.18.2"
|
||||
attrs = "^23.1.0"
|
||||
|
||||
# [tool.poetry.group.local_specklepy.dependencies]
|
||||
# specklepy = {path = "../specklepy", develop = true}
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
fake-bpy-module-latest = "^20230117"
|
||||
black = "^22.10.0"
|
||||
fake-bpy-module-latest = "^20240212"
|
||||
black = "23.11.0"
|
||||
pylint = "^2.15.7"
|
||||
ruff = "^0.0.187"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user