Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a2f7ab422f | |||
| 8c58d9d14c | |||
| 90e61b6dc1 | |||
| 5c479e4c0e | |||
| 2800b84747 | |||
| 24e7f02213 | |||
| c1d7947085 |
@@ -49,8 +49,7 @@ Head to the [**📚 documentation**](https://speckle.guide/user/blender.html) fo
|
||||
|
||||
## Installation
|
||||
|
||||
Currently, we are supporting all Blender 3.X versions on Windows and Mac.
|
||||
We have experimental support for Blender 4.0 and greater.
|
||||
We officially support Blender 3.3 and newer, on Windows and Mac.
|
||||
|
||||
Please follow our installation instructions on our [connector docs](https://speckle.guide/user/blender.html#installation)
|
||||
|
||||
@@ -59,10 +58,10 @@ Once enabled in `Preferences -> Addons`,
|
||||
The Speckle connector UI can be found in the 3d viewport toolbar (N), under the `Speckle` tab.
|
||||
|
||||
- Available user accounts are automatically detected and made available. To add user accounts use **Speckle Manager**.
|
||||
- Select the user from the dropdown list in the `Users` panel. This will populate the `Streams` list with available streams for the selected user.
|
||||
- Select a branch and commit from the dropdown menus.
|
||||
- Click on `Receive` to download the objects from the selected stream, branch, and commit. The stream objects will be loaded into a Blender Collection, named `<STREAM_NAME> [ <STREAM_BRANCH> @ <BRANCH_COMMIT> ]`. <!-- You can filter the stream by entering a query into the `Filter` field (i.e. `properties.weight>10` or `type="Mesh"`). -->
|
||||
- Click on `Open Stream in Web` to view the stream in your web browser.
|
||||
- Select the user from the dropdown list in the `Users` panel. This will populate the `Projects` list with available projects for the selected user account.
|
||||
- Select a model and version from the dropdown menus.
|
||||
- Click on `Receive` to download and convert the objects from the selected model version. The objects will be linked into a Blender Collection, named `<PROJECT_NAME> [ <MODEL_NAME> @ <VERSION_ID> ]`.
|
||||
- Click on `Open Model in Web` to view the model in your web browser.
|
||||
|
||||
## Supported Elements
|
||||
|
||||
@@ -77,7 +76,7 @@ The full matrix of supported Blender and Speckle types [can be found here](https
|
||||
|
||||
- If a `renderMaterial` property is found, **SpeckleBlender** will create a material named using the sub-property `renderMaterial.name`. If a material with that name already exists in Blender, **SpeckleBlender** will just assign that existing material to the object. This allows geometry to be updated without having to re-assign and re-create materials.
|
||||
|
||||
- Receiving vertex colors are supported. The `colors` list from Speckle meshes is translated to a vertex color layer.
|
||||
- Receiving vertex colors is supported. The `colors` list from Speckle meshes is translated to a vertex color layer.
|
||||
|
||||
- Receive/Send scripts. Allow injecting a custom python function to the receive/send process to automate any blender operations
|
||||
|
||||
|
||||
@@ -774,15 +774,21 @@ def _make_unique_name( desired_name: str, taken_names: Collection[str], counter:
|
||||
def _get_friendly_object_name(speckle_object: Base) -> Optional[str]:
|
||||
return (getattr(speckle_object, "name", None)
|
||||
or getattr(speckle_object, "Name", None)
|
||||
or getattr(speckle_object, "family", None)
|
||||
or _get_revit_family_name(speckle_object)
|
||||
)
|
||||
|
||||
def _get_revit_family_name(speckle_object: Base) -> Optional[str]:
|
||||
family = getattr(speckle_object, "family", None)
|
||||
family_type = getattr(speckle_object, "type", None)
|
||||
|
||||
if family and family_type:
|
||||
return f"{family_type}-{family}"
|
||||
else:
|
||||
return None
|
||||
|
||||
# Blender object names must not exceed 62 characters
|
||||
# We need to ensure the complete ID is included in the name (to prevent identity collisions)
|
||||
# So we if the name is too long, we need to truncate
|
||||
|
||||
|
||||
def _truncate_object_name(name: str) -> str:
|
||||
|
||||
MAX_NAME_LENGTH = OBJECT_NAME_MAX_LENGTH - SPECKLE_ID_LENGTH - len(OBJECT_NAME_SPECKLE_SEPARATOR)
|
||||
|
||||
@@ -20,6 +20,7 @@ from .streams import (
|
||||
CopyStreamId,
|
||||
CopyCommitId,
|
||||
CopyBranchName,
|
||||
CopyModelId,
|
||||
)
|
||||
from .commit import DeleteCommit
|
||||
from .misc import OpenSpeckleGuide, OpenSpeckleTutorials, OpenSpeckleForum
|
||||
@@ -33,6 +34,7 @@ operator_classes = [
|
||||
CopyStreamId,
|
||||
CopyCommitId,
|
||||
CopyBranchName,
|
||||
CopyModelId,
|
||||
]
|
||||
|
||||
operator_classes.extend([DeleteCommit])
|
||||
|
||||
@@ -11,19 +11,19 @@ from specklepy.logging import metrics
|
||||
|
||||
class DeleteCommit(bpy.types.Operator):
|
||||
"""
|
||||
Deletes the selected commit from the selected stream.
|
||||
Permanently deletes the selected version from the selected model.
|
||||
To execute from code, call: `bpy.ops.speckle.delete_commit(are_you_sure=True)`
|
||||
"""
|
||||
|
||||
bl_idname = "speckle.delete_commit"
|
||||
bl_label = "Delete commit"
|
||||
bl_label = "Delete Version"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Delete active commit permanently"
|
||||
bl_description = "Permanently Deletes the selected version from the selected model"
|
||||
|
||||
are_you_sure: BoolProperty(
|
||||
name="Confirm",
|
||||
default=False,
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
@@ -51,7 +51,7 @@ class DeleteCommit(bpy.types.Operator):
|
||||
def delete_commit(context: bpy.types.Context) -> None:
|
||||
speckle = get_speckle(context)
|
||||
|
||||
(_, stream, _, commit) = speckle.validate_commit_selection()
|
||||
(_, stream, branch, commit) = speckle.validate_commit_selection()
|
||||
|
||||
client = speckle_clients[int(speckle.active_user)]
|
||||
|
||||
@@ -68,5 +68,5 @@ class DeleteCommit(bpy.types.Operator):
|
||||
if not deleted:
|
||||
raise Exception("Delete operation failed")
|
||||
|
||||
print(f"Commit {commit.id} ({commit.message}) has been deleted from stream {stream.id}")
|
||||
print(f"Version {commit.id} ({commit.message}) of model {branch.id} ({branch.name}) has been deleted from project {stream.id} ({stream.name})")
|
||||
|
||||
|
||||
@@ -6,13 +6,15 @@ from specklepy.logging import metrics
|
||||
|
||||
|
||||
class OpenSpeckleGuide(bpy.types.Operator):
|
||||
bl_idname = "speckle.open_speckle_guide"
|
||||
bl_label = "Speckle Guide"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Browse the documentation on the Speckle Guide"
|
||||
_guide_url = "https://speckle.guide/user/blender.html"
|
||||
|
||||
bl_idname = "speckle.open_speckle_guide"
|
||||
bl_label = "Speckle Docs"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = f"Browse the documentation on the Speckle Guide ({_guide_url})"
|
||||
|
||||
def execute(self, context):
|
||||
webbrowser.open("https://speckle.guide/user/blender.html")
|
||||
webbrowser.open(self._guide_url)
|
||||
metrics.track(
|
||||
"Connector Action",
|
||||
None,
|
||||
@@ -24,13 +26,15 @@ class OpenSpeckleGuide(bpy.types.Operator):
|
||||
|
||||
|
||||
class OpenSpeckleTutorials(bpy.types.Operator):
|
||||
_tutorials_url = "https://speckle.systems/tutorials/"
|
||||
|
||||
bl_idname = "speckle.open_speckle_tutorials"
|
||||
bl_label = "Tutorials Portal"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Visit our tutorials portal for learning resources"
|
||||
bl_description = f"Visit our tutorials portal for learning resources ({_tutorials_url})"
|
||||
|
||||
def execute(self, context):
|
||||
webbrowser.open("https://speckle.systems/tutorials/")
|
||||
webbrowser.open(self._tutorials_url)
|
||||
metrics.track(
|
||||
"Connector Action",
|
||||
None,
|
||||
@@ -42,13 +46,15 @@ class OpenSpeckleTutorials(bpy.types.Operator):
|
||||
|
||||
|
||||
class OpenSpeckleForum(bpy.types.Operator):
|
||||
_forum_url = "https://speckle.community/"
|
||||
|
||||
bl_idname = "speckle.open_speckle_forum"
|
||||
bl_label = "Community Forum"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Ask questions and join the discussion on our community forum"
|
||||
bl_description = f"Ask questions and join the discussion on our community forum ({_forum_url})"
|
||||
|
||||
def execute(self, context):
|
||||
webbrowser.open("https://speckle.community/")
|
||||
webbrowser.open(self._forum_url)
|
||||
metrics.track(
|
||||
"Connector Action",
|
||||
None,
|
||||
|
||||
@@ -22,7 +22,7 @@ class UpdateObject(bpy.types.Operator):
|
||||
"""
|
||||
|
||||
bl_idname = "speckle.update_object"
|
||||
bl_label = "Update Object"
|
||||
bl_label = "Update Object (DEPRECATED)"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
client = None
|
||||
@@ -76,7 +76,7 @@ class ResetObject(bpy.types.Operator):
|
||||
"""
|
||||
|
||||
bl_idname = "speckle.reset_object"
|
||||
bl_label = "Reset Object"
|
||||
bl_label = "Reset Object (DEPRECATED)"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
@@ -104,7 +104,7 @@ class DeleteObject(bpy.types.Operator):
|
||||
"""
|
||||
|
||||
bl_idname = "speckle.delete_object"
|
||||
bl_label = "Delete Object"
|
||||
bl_label = "Delete Object (DEPRECATED)"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
@@ -159,7 +159,7 @@ class UploadNgonsAsPolylines(bpy.types.Operator):
|
||||
"""
|
||||
|
||||
bl_idname = "speckle.upload_ngons_as_polylines"
|
||||
bl_label = "Upload Ngons As Polylines"
|
||||
bl_label = "Upload Ngons As Polylines (DEPRECATED)"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
clear_stream: BoolProperty(
|
||||
@@ -256,7 +256,7 @@ class SelectIfSameCustomProperty(bpy.types.Operator):
|
||||
"""
|
||||
|
||||
bl_idname = "speckle.select_if_same_custom_props"
|
||||
bl_label = "Select Identical Custom Props"
|
||||
bl_label = "Select Identical Custom Props (DEPRECATED)"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
custom_prop: EnumProperty(
|
||||
@@ -316,7 +316,7 @@ class SelectIfHasCustomProperty(bpy.types.Operator):
|
||||
"""
|
||||
|
||||
bl_idname = "speckle.select_if_has_custom_props"
|
||||
bl_label = "Select Same Custom Prop"
|
||||
bl_label = "Select Same Custom Prop (DEPRECATED)"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
custom_prop: EnumProperty(
|
||||
|
||||
@@ -81,13 +81,13 @@ INSTANCES_SETTINGS = [
|
||||
|
||||
class ReceiveStreamObjects(bpy.types.Operator):
|
||||
"""
|
||||
Receive stream objects
|
||||
Receive objects from selected model version
|
||||
"""
|
||||
|
||||
bl_idname = "speckle.receive_stream_objects"
|
||||
bl_label = "Download Stream Objects"
|
||||
bl_label = "Receive"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Receive objects from active stream"
|
||||
bl_description = "Receive objects from selected model version"
|
||||
|
||||
clean_meshes: BoolProperty(name="Clean Meshes", default=False) # type: ignore
|
||||
|
||||
@@ -153,7 +153,7 @@ class ReceiveStreamObjects(bpy.types.Operator):
|
||||
stream.id,
|
||||
commit.id,
|
||||
source_application="blender",
|
||||
message="received commit from Speckle Blender",
|
||||
message="Received model version from Speckle Blender",
|
||||
)
|
||||
|
||||
metrics.track(
|
||||
@@ -189,7 +189,7 @@ class ReceiveStreamObjects(bpy.types.Operator):
|
||||
|
||||
# ensure commit object has a name if not already
|
||||
if not commit_object.name:
|
||||
commit_object.name = "{} [ {} @ {} ]".format(stream.name, branch.name, commit.id) # Matches Rhino "Create" naming
|
||||
commit_object.name = f"{stream.name} [ {branch.name} @ {commit.id} ]" # Matches Rhino "Create" naming
|
||||
|
||||
for item in traversalFunc.traverse(commit_object):
|
||||
|
||||
@@ -198,7 +198,7 @@ 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")
|
||||
raise Exception(f"{current} was an invalid Speckle object")
|
||||
|
||||
#Convert the object!
|
||||
converted_data_type: str
|
||||
@@ -245,18 +245,18 @@ class ReceiveStreamObjects(bpy.types.Operator):
|
||||
|
||||
class SendStreamObjects(bpy.types.Operator):
|
||||
"""
|
||||
Send stream objects
|
||||
Send selected objects to selected model
|
||||
"""
|
||||
|
||||
bl_idname = "speckle.send_stream_objects"
|
||||
bl_label = "Send stream objects"
|
||||
bl_label = "Send"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Send selected objects to active stream"
|
||||
bl_description = "Send selected objects to selected model"
|
||||
|
||||
apply_modifiers: BoolProperty(name="Apply modifiers", default=True) # type: ignore
|
||||
commit_message: StringProperty(
|
||||
name="Message",
|
||||
default="Pushed elements from Blender.",
|
||||
default="Sent elements from Blender.",
|
||||
) # type: ignore
|
||||
|
||||
def draw(self, context):
|
||||
@@ -274,9 +274,9 @@ class SendStreamObjects(bpy.types.Operator):
|
||||
|
||||
N = len(context.selected_objects)
|
||||
if N == 1:
|
||||
self.commit_message = f"Pushed {N} element from Blender."
|
||||
self.commit_message = f"Sent {N} element from Blender."
|
||||
else:
|
||||
self.commit_message = f"Pushed {N} elements from Blender."
|
||||
self.commit_message = f"Sent {N} elements from Blender."
|
||||
return wm.invoke_props_dialog(self)
|
||||
|
||||
|
||||
@@ -390,9 +390,9 @@ class SendStreamObjects(bpy.types.Operator):
|
||||
|
||||
class ViewStreamDataApi(bpy.types.Operator):
|
||||
bl_idname = "speckle.view_stream_data_api"
|
||||
bl_label = "Open Stream in Web"
|
||||
bl_label = "Open Model in Web"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "View the stream in the web browser"
|
||||
bl_description = "View the selected model in the web browser"
|
||||
|
||||
def execute(self, context):
|
||||
self.view_stream_data_api(context)
|
||||
@@ -401,16 +401,12 @@ class ViewStreamDataApi(bpy.types.Operator):
|
||||
def view_stream_data_api(self, context: Context) -> None:
|
||||
speckle = get_speckle(context)
|
||||
|
||||
(user, stream) = speckle.validate_stream_selection()
|
||||
url = self._get_url_from_selection(speckle)
|
||||
|
||||
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")
|
||||
_report(f"Opening {url} in web browser")
|
||||
|
||||
if not webbrowser.open(url, new=2):
|
||||
raise Exception(f"Failed to open model in browser ({url})")
|
||||
|
||||
metrics.track(
|
||||
"Connector Action",
|
||||
@@ -420,18 +416,40 @@ class ViewStreamDataApi(bpy.types.Operator):
|
||||
},
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _get_url_from_selection(speckleScene : SpeckleSceneSettings) -> str:
|
||||
|
||||
client = speckle_clients[int(speckleScene.active_user)]
|
||||
(user, stream) = speckleScene.validate_stream_selection()
|
||||
branch = stream.get_active_branch()
|
||||
commit = branch.get_active_commit() if branch else None
|
||||
|
||||
if client.account.serverInfo.frontend2:
|
||||
server_url = f"{user.server_url}/projects/{stream.id}/"
|
||||
if branch:
|
||||
server_url += f"models/{branch.id}"
|
||||
if commit:
|
||||
server_url += f"@{commit.id}"
|
||||
else:
|
||||
server_url = f"{user.server_url}/streams/{stream.id}/"
|
||||
if commit:
|
||||
server_url += f"commits/{commit.id}"
|
||||
elif branch:
|
||||
server_url += f"branches/{branch.name}"
|
||||
|
||||
return server_url
|
||||
|
||||
class AddStreamFromURL(bpy.types.Operator):
|
||||
"""
|
||||
Add / select a stream using its url
|
||||
Add / select an existing project by providing its URL
|
||||
"""
|
||||
|
||||
bl_idname = "speckle.add_stream_from_url"
|
||||
bl_label = "Add stream from URL"
|
||||
bl_label = "Add Project From URL"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Add an existing stream by providing its URL"
|
||||
bl_description = "Add / select an existing project by providing its URL"
|
||||
stream_url: StringProperty(
|
||||
name="Stream URL", default="https://speckle.xyz/streams/3073b96e86"
|
||||
name="Project URL", default=""
|
||||
) # type: ignore
|
||||
|
||||
def draw(self, context):
|
||||
@@ -485,12 +503,12 @@ class AddStreamFromURL(bpy.types.Operator):
|
||||
client = speckle_clients[user_index]
|
||||
stream = client.stream.get(wrapper.stream_id, branch_limit=LoadUserStreams.branch_limit, commit_limit=LoadUserStreams.commits_limit)
|
||||
if not isinstance(stream, Stream):
|
||||
raise SpeckleException(f"Could not get the requested stream {wrapper.stream_id}")
|
||||
raise SpeckleException(f"Could not get the requested project {wrapper.stream_id}")
|
||||
|
||||
(index, b_stream) = self._get_or_add_stream(user, stream)
|
||||
user.active_stream = index
|
||||
|
||||
_report(f"Selecting stream at index {index} ({b_stream.id} - {b_stream.name})")
|
||||
_report(f"Selecting project at index {index} ({b_stream.id} - {b_stream.name})")
|
||||
|
||||
if wrapper.branch_name:
|
||||
b_index = b_stream.branches.find(wrapper.branch_name)
|
||||
@@ -522,17 +540,17 @@ class AddStreamFromURL(bpy.types.Operator):
|
||||
|
||||
class CreateStream(bpy.types.Operator):
|
||||
"""
|
||||
Create new stream
|
||||
Create a new Speckle project using the selected user account
|
||||
"""
|
||||
|
||||
bl_idname = "speckle.create_stream"
|
||||
bl_label = "Create stream"
|
||||
bl_label = "Create Project"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Create new stream"
|
||||
bl_description = "Create a new Speckle project using the selected user account"
|
||||
|
||||
stream_name: StringProperty(name="Stream name") # type: ignore
|
||||
stream_name: StringProperty(name="Project name") # type: ignore
|
||||
stream_description: StringProperty(
|
||||
name="Stream description", default="This is a Blender stream."
|
||||
name="Project description", default="My new project"
|
||||
) # type: ignore
|
||||
|
||||
def draw(self, context):
|
||||
@@ -587,13 +605,13 @@ class CreateStream(bpy.types.Operator):
|
||||
@deprecated
|
||||
class DeleteStream(bpy.types.Operator):
|
||||
"""
|
||||
Delete stream
|
||||
Permanently delete the selected project
|
||||
"""
|
||||
|
||||
bl_idname = "speckle.delete_stream"
|
||||
bl_label = "Delete stream"
|
||||
bl_label = "Delete Project"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Delete selected stream permanently"
|
||||
bl_description = "Permanently delete the selected project"
|
||||
|
||||
are_you_sure: BoolProperty(
|
||||
name="Confirm",
|
||||
@@ -663,7 +681,7 @@ class SelectOrphanObjects(bpy.types.Operator):
|
||||
"""
|
||||
|
||||
bl_idname = "speckle.select_orphans"
|
||||
bl_label = "Select orphaned objects"
|
||||
bl_label = "Select Orphaned Objects (DEPRECATED)"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Select Speckle objects that don't belong to any stream"
|
||||
|
||||
@@ -692,13 +710,13 @@ class SelectOrphanObjects(bpy.types.Operator):
|
||||
|
||||
class CopyStreamId(bpy.types.Operator):
|
||||
"""
|
||||
Copy stream ID to clipboard
|
||||
Copy the selected project id to clipboard
|
||||
"""
|
||||
|
||||
bl_idname = "speckle.stream_copy_id"
|
||||
bl_label = "Copy stream ID"
|
||||
bl_label = "Copy Project Id"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Copy stream ID to clipboard"
|
||||
bl_description = "Copy the selected project id to clipboard"
|
||||
|
||||
def execute(self, context):
|
||||
self.copy_stream_id(context)
|
||||
@@ -719,13 +737,13 @@ class CopyStreamId(bpy.types.Operator):
|
||||
|
||||
class CopyCommitId(bpy.types.Operator):
|
||||
"""
|
||||
Copy commit ID to clipboard
|
||||
Copy the selected version id to clipboard
|
||||
"""
|
||||
|
||||
bl_idname = "speckle.commit_copy_id"
|
||||
bl_label = "Copy commit ID"
|
||||
bl_label = "Copy Version Id"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Copy commit ID to clipboard"
|
||||
bl_description = "Copy the selected version id to clipboard"
|
||||
|
||||
def execute(self, context):
|
||||
self.copy_commit_id(context)
|
||||
@@ -747,6 +765,36 @@ class CopyCommitId(bpy.types.Operator):
|
||||
|
||||
|
||||
|
||||
class CopyModelId(bpy.types.Operator):
|
||||
"""
|
||||
Copy model id to clipboard
|
||||
"""
|
||||
|
||||
bl_idname = "speckle.model_copy_id"
|
||||
bl_label = "Copy model id"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Copy model id to clipboard"
|
||||
|
||||
def execute(self, context):
|
||||
self.copy_model_id(context)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
def copy_model_id(self, context) -> None:
|
||||
speckle = get_speckle(context)
|
||||
|
||||
(_, _, branch) = speckle.validate_branch_selection()
|
||||
|
||||
bpy.context.window_manager.clipboard = branch.id
|
||||
|
||||
metrics.track(
|
||||
"Connector Action",
|
||||
custom_props={
|
||||
"name": "copy_branch_id"
|
||||
},
|
||||
)
|
||||
|
||||
@deprecated
|
||||
class CopyBranchName(bpy.types.Operator):
|
||||
"""
|
||||
Copy branch name to clipboard
|
||||
@@ -775,3 +823,37 @@ class CopyBranchName(bpy.types.Operator):
|
||||
"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):
|
||||
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"}
|
||||
@@ -18,7 +18,7 @@ class ResetUsers(bpy.types.Operator):
|
||||
"""
|
||||
|
||||
bl_idname = "speckle.users_reset"
|
||||
bl_label = "Reset users"
|
||||
bl_label = "Reset Users"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
@@ -46,12 +46,14 @@ class ResetUsers(bpy.types.Operator):
|
||||
|
||||
class LoadUsers(bpy.types.Operator):
|
||||
"""
|
||||
Load all users from local user database
|
||||
Loads all user accounts from the credentials in the local database.
|
||||
See docs to add accounts via Manager
|
||||
"""
|
||||
|
||||
bl_idname = "speckle.users_load"
|
||||
bl_label = "Load users"
|
||||
bl_label = "Load Users"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Loads all user accounts from the credentials in the local database.\nSee docs to add accounts via Manager"
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
@@ -158,21 +160,16 @@ def add_user_stream(user: SpeckleUserObject, stream: Stream):
|
||||
commit.source_application = str(c.sourceApplication)
|
||||
commit.referenced_object = c.referencedObject
|
||||
|
||||
if hasattr(s, "baseProperties"):
|
||||
s.units = stream.baseProperties.units # type: ignore
|
||||
else:
|
||||
s.units = "Meters"
|
||||
|
||||
|
||||
class LoadUserStreams(bpy.types.Operator):
|
||||
"""
|
||||
Load all available streams for active user
|
||||
(Re)Load all available projects for active user
|
||||
"""
|
||||
|
||||
bl_idname = "speckle.load_user_streams"
|
||||
bl_label = "Load user streams"
|
||||
bl_label = "Load User's Projects"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "(Re)load all available user streams"
|
||||
bl_description = "(Re)Load all available projects for active user"
|
||||
|
||||
stream_limit: int = 20
|
||||
branch_limit: int = 100
|
||||
@@ -192,10 +189,10 @@ class LoadUserStreams(bpy.types.Operator):
|
||||
try:
|
||||
streams = client.stream.list(stream_limit=self.stream_limit)
|
||||
except Exception as ex:
|
||||
raise Exception(f"Failed to retrieve streams") from ex
|
||||
raise Exception(f"Failed to retrieve projects") from ex
|
||||
|
||||
if not streams:
|
||||
_report("Zero streams found")
|
||||
_report("Zero projects found")
|
||||
return
|
||||
|
||||
user.streams.clear()
|
||||
|
||||
@@ -5,7 +5,7 @@ import bpy
|
||||
|
||||
|
||||
class SpeckleCollectionSettings(bpy.types.PropertyGroup):
|
||||
enabled: bpy.props.BoolProperty(default=False, name="Enabled")
|
||||
enabled: bpy.props.BoolProperty(default=False, name="Enabled") # type: ignore
|
||||
|
||||
send_or_receive: bpy.props.EnumProperty(
|
||||
name="Mode",
|
||||
@@ -13,7 +13,6 @@ class SpeckleCollectionSettings(bpy.types.PropertyGroup):
|
||||
("send", "Send", "Send data to Speckle server."),
|
||||
("receive", "Receive", "Receive data from Speckle server."),
|
||||
),
|
||||
)
|
||||
stream_id: bpy.props.StringProperty(default="")
|
||||
name: bpy.props.StringProperty(default="")
|
||||
units: bpy.props.StringProperty(default="")
|
||||
) # type: ignore
|
||||
stream_id: bpy.props.StringProperty(default="") # type: ignore
|
||||
name: bpy.props.StringProperty(default="") # type: ignore
|
||||
|
||||
@@ -13,6 +13,6 @@ class SpeckleObjectSettings(bpy.types.PropertyGroup):
|
||||
("send", "Send", "Send data to Speckle server."),
|
||||
("receive", "Receive", "Receive data from Speckle server."),
|
||||
),
|
||||
)
|
||||
stream_id: bpy.props.StringProperty(default="")
|
||||
object_id: bpy.props.StringProperty(default="")
|
||||
) # type: ignore
|
||||
stream_id: bpy.props.StringProperty(default="") # type: ignore
|
||||
object_id: bpy.props.StringProperty(default="") # type: ignore
|
||||
|
||||
@@ -1,50 +1,49 @@
|
||||
"""
|
||||
Scene properties
|
||||
"""
|
||||
from typing import Optional, Tuple
|
||||
from typing import Iterable, Optional, Tuple, cast
|
||||
import bpy
|
||||
from bpy.props import (
|
||||
StringProperty,
|
||||
BoolProperty,
|
||||
FloatProperty,
|
||||
CollectionProperty,
|
||||
EnumProperty,
|
||||
IntProperty,
|
||||
PointerProperty,
|
||||
)
|
||||
|
||||
class SpeckleSceneObject(bpy.types.PropertyGroup):
|
||||
name: bpy.props.StringProperty(default="")
|
||||
name: bpy.props.StringProperty(default="") # type: ignore
|
||||
|
||||
|
||||
class SpeckleCommitObject(bpy.types.PropertyGroup):
|
||||
id: StringProperty(default="")
|
||||
message: StringProperty(default="")
|
||||
author_name: StringProperty(default="")
|
||||
author_id: StringProperty(default="")
|
||||
created_at: StringProperty(default="")
|
||||
source_application: StringProperty(default="")
|
||||
referenced_object: StringProperty(default="")
|
||||
id: StringProperty(default="") # type: ignore
|
||||
message: StringProperty(default="") # type: ignore
|
||||
author_name: StringProperty(default="") # type: ignore
|
||||
author_id: StringProperty(default="") # type: ignore
|
||||
created_at: StringProperty(default="") # type: ignore
|
||||
source_application: StringProperty(default="") # type: ignore
|
||||
referenced_object: StringProperty(default="") # type: ignore
|
||||
|
||||
|
||||
class SpeckleBranchObject(bpy.types.PropertyGroup):
|
||||
def get_commits(self, context):
|
||||
if self.commits != None and len(self.commits) > 0:
|
||||
COMMITS = cast(Iterable[SpeckleCommitObject], self.commits)
|
||||
return [
|
||||
(str(i), commit.id, commit.message, i)
|
||||
for i, commit in enumerate(self.commits)
|
||||
for i, commit in enumerate(COMMITS)
|
||||
]
|
||||
return [("0", "<none>", "<none>", 0)]
|
||||
|
||||
name: StringProperty(default="main")
|
||||
id: StringProperty(default="")
|
||||
description: StringProperty(default="")
|
||||
commits: CollectionProperty(type=SpeckleCommitObject)
|
||||
|
||||
name: StringProperty(default="main") # type: ignore
|
||||
id: StringProperty(default="") # type: ignore
|
||||
description: StringProperty(default="") # type: ignore
|
||||
commits: CollectionProperty(type=SpeckleCommitObject) # type: ignore
|
||||
commit: EnumProperty(
|
||||
name="Commit",
|
||||
description="Active commit",
|
||||
name="Version",
|
||||
description="Selected model version",
|
||||
items=get_commits,
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
def get_active_commit(self) -> Optional[SpeckleCommitObject]:
|
||||
selected_index = int(self.commit)
|
||||
@@ -56,24 +55,23 @@ class SpeckleBranchObject(bpy.types.PropertyGroup):
|
||||
class SpeckleStreamObject(bpy.types.PropertyGroup):
|
||||
def get_branches(self, context):
|
||||
if self.branches:
|
||||
BRANCHES = cast(Iterable[SpeckleBranchObject], self.branches)
|
||||
return [
|
||||
(str(i), branch.name, branch.name, i)
|
||||
for i, branch in enumerate(self.branches)
|
||||
(str(i), branch.name, branch.description, i)
|
||||
for i, branch in enumerate(BRANCHES)
|
||||
if branch.name != "globals"
|
||||
]
|
||||
return [("0", "<none>", "<none>", 0)]
|
||||
|
||||
name: StringProperty(default="SpeckleStream")
|
||||
description: StringProperty(default="No description provided.")
|
||||
id: StringProperty(default="")
|
||||
units: StringProperty(default="Meters")
|
||||
query: StringProperty(default="")
|
||||
branches: CollectionProperty(type=SpeckleBranchObject)
|
||||
name: StringProperty(default="") # type: ignore
|
||||
description: StringProperty(default="") # type: ignore
|
||||
id: StringProperty(default="") # type: ignore
|
||||
branches: CollectionProperty(type=SpeckleBranchObject) # type: ignore
|
||||
branch: EnumProperty(
|
||||
name="Branch",
|
||||
description="Active branch",
|
||||
name="Model",
|
||||
description="Selected Model",
|
||||
items=get_branches,
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
def get_active_branch(self) -> Optional[SpeckleBranchObject]:
|
||||
selected_index = int(self.branch)
|
||||
@@ -83,14 +81,14 @@ class SpeckleStreamObject(bpy.types.PropertyGroup):
|
||||
|
||||
|
||||
class SpeckleUserObject(bpy.types.PropertyGroup):
|
||||
server_name: StringProperty(default="SpeckleXYZ")
|
||||
server_url: StringProperty(default="https://speckle.xyz")
|
||||
id: StringProperty(default="")
|
||||
name: StringProperty(default="Speckle User")
|
||||
email: StringProperty(default="user@speckle.xyz")
|
||||
company: StringProperty(default="SpeckleSystems")
|
||||
streams: CollectionProperty(type=SpeckleStreamObject)
|
||||
active_stream: IntProperty(default=0)
|
||||
server_name: StringProperty(default="SpeckleXYZ") # type: ignore
|
||||
server_url: StringProperty(default="https://speckle.xyz") # type: ignore
|
||||
id: StringProperty(default="") # type: ignore
|
||||
name: StringProperty(default="Speckle User") # type: ignore
|
||||
email: StringProperty(default="user@speckle.xyz") # type: ignore
|
||||
company: StringProperty(default="SpeckleSystems") # type: ignore
|
||||
streams: CollectionProperty(type=SpeckleStreamObject) # type: ignore
|
||||
active_stream: IntProperty(default=0) # type: ignore
|
||||
|
||||
def get_active_stream(self) -> Optional[SpeckleStreamObject]:
|
||||
selected_index = int(self.active_stream)
|
||||
@@ -109,18 +107,19 @@ class SpeckleSceneSettings(bpy.types.PropertyGroup):
|
||||
name="Available streams",
|
||||
description="Available streams associated with user.",
|
||||
items=[],
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
users: CollectionProperty(type=SpeckleUserObject)
|
||||
users: CollectionProperty(type=SpeckleUserObject) # type: ignore
|
||||
|
||||
def get_users(self, context):
|
||||
USERS = cast(Iterable[SpeckleUserObject], self.users)
|
||||
return [
|
||||
(str(i), "{} ({})".format(user.email, user.server_name), user.server_url, i)
|
||||
for i, user in enumerate(self.users)
|
||||
(str(i), f"{user.email} ({user.server_name})", user.server_url, i)
|
||||
for i, user in enumerate(USERS)
|
||||
]
|
||||
|
||||
def set_user(self, context):
|
||||
bpy.ops.speckle.load_user_streams()
|
||||
bpy.ops.speckle.load_user_streams() # type: ignore
|
||||
|
||||
active_user: EnumProperty(
|
||||
items=get_users,
|
||||
@@ -129,29 +128,29 @@ class SpeckleSceneSettings(bpy.types.PropertyGroup):
|
||||
update=set_user,
|
||||
get=None,
|
||||
set=None,
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
objects: CollectionProperty(type=SpeckleSceneObject)
|
||||
objects: CollectionProperty(type=SpeckleSceneObject) # type: ignore
|
||||
|
||||
scale: FloatProperty(default=0.001)
|
||||
scale: FloatProperty(default=0.001) # type: ignore
|
||||
|
||||
user: StringProperty(
|
||||
name="User",
|
||||
description="Current user.",
|
||||
description="Current user",
|
||||
default="Speckle User",
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
receive_script: EnumProperty(
|
||||
name="Receive script",
|
||||
description="Script to run when receiving stream objects.",
|
||||
description="Custom py script to execute when receiving objects. See docs for function signature.",
|
||||
items=get_scripts,
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
send_script: EnumProperty(
|
||||
name="Send script",
|
||||
description="Script to run when sending stream objects.",
|
||||
description="Custom py script to execute when sending objects. See docs for function signature",
|
||||
items=get_scripts,
|
||||
)
|
||||
) # type: ignore
|
||||
|
||||
def get_active_user(self) -> Optional[SpeckleUserObject]:
|
||||
selected_index = int(self.active_user)
|
||||
@@ -163,7 +162,7 @@ class SpeckleSceneSettings(bpy.types.PropertyGroup):
|
||||
def validate_user_selection(self) -> SpeckleUserObject:
|
||||
user = self.get_active_user()
|
||||
if not user:
|
||||
raise SelectionException("No user selected/found")
|
||||
raise SelectionException("No user account selected/found")
|
||||
return user
|
||||
|
||||
def validate_stream_selection(self) -> Tuple[SpeckleUserObject, SpeckleStreamObject]:
|
||||
@@ -171,7 +170,7 @@ class SpeckleSceneSettings(bpy.types.PropertyGroup):
|
||||
|
||||
stream = user.get_active_stream()
|
||||
if not stream:
|
||||
raise SelectionException("No stream selected/found")
|
||||
raise SelectionException("No project selected/found")
|
||||
|
||||
return (user, stream)
|
||||
|
||||
@@ -180,14 +179,14 @@ class SpeckleSceneSettings(bpy.types.PropertyGroup):
|
||||
|
||||
branch = stream.get_active_branch()
|
||||
if not branch:
|
||||
raise SelectionException("No branch selected/found")
|
||||
raise SelectionException("No model selected/found")
|
||||
return (user, stream, branch)
|
||||
|
||||
def validate_commit_selection(self) ->Tuple[SpeckleUserObject, SpeckleStreamObject, SpeckleBranchObject, SpeckleCommitObject]:
|
||||
(user, stream, branch) = self.validate_branch_selection()
|
||||
commit = branch.get_active_commit()
|
||||
if commit is None:
|
||||
raise SelectionException("No commit selected/found")
|
||||
raise SelectionException("No model version selected/found")
|
||||
|
||||
return (user, stream, branch, commit)
|
||||
|
||||
@@ -195,4 +194,7 @@ class SelectionException(Exception):
|
||||
pass
|
||||
|
||||
def get_speckle(context: bpy.types.Context) -> SpeckleSceneSettings:
|
||||
"""
|
||||
Gets the speckle scene object
|
||||
"""
|
||||
return context.scene.speckle #type: ignore
|
||||
@@ -29,7 +29,7 @@ class OBJECT_PT_speckle(bpy.types.Panel):
|
||||
layout.active = ob.speckle.enabled
|
||||
col = layout.column()
|
||||
col.prop(ob.speckle, "send_or_receive", expand=True)
|
||||
col.prop(ob.speckle, "stream_id", text="Stream ID")
|
||||
col.prop(ob.speckle, "stream_id", text="Project ID")
|
||||
col.prop(ob.speckle, "object_id", text="Object ID")
|
||||
col.operator("speckle.update_object", text="Update")
|
||||
col.operator("speckle.reset_object", text="Reset")
|
||||
|
||||
+24
-33
@@ -4,20 +4,10 @@ Speckle UI elements for the 3d viewport
|
||||
|
||||
|
||||
import bpy
|
||||
from bpy.props import (
|
||||
StringProperty,
|
||||
BoolProperty,
|
||||
FloatProperty,
|
||||
CollectionProperty,
|
||||
EnumProperty,
|
||||
)
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
"""
|
||||
Compatibility
|
||||
TODO: evaluate if we should still support Blender <2.80
|
||||
"""
|
||||
from bpy_speckle.properties.scene import get_speckle
|
||||
|
||||
Region = "TOOLS" if bpy.app.version < (2, 80, 0) else "UI"
|
||||
|
||||
@@ -78,7 +68,7 @@ class VIEW3D_UL_SpeckleUsers(bpy.types.UIList):
|
||||
|
||||
class VIEW3D_UL_SpeckleStreams(bpy.types.UIList):
|
||||
"""
|
||||
Speckle stream list
|
||||
Speckle projects list
|
||||
"""
|
||||
|
||||
def draw_item(self, context, layout, data, stream, active_data, active_propname):
|
||||
@@ -94,7 +84,7 @@ class VIEW3D_UL_SpeckleStreams(bpy.types.UIList):
|
||||
|
||||
elif self.layout_type in {"GRID"}:
|
||||
layout.alignment = "CENTER"
|
||||
layout.label(text="Streams", icon_value=0)
|
||||
layout.label(text="Projects", icon_value=0)
|
||||
|
||||
|
||||
class VIEW3D_PT_SpeckleUser(bpy.types.Panel):
|
||||
@@ -106,10 +96,10 @@ class VIEW3D_PT_SpeckleUser(bpy.types.Panel):
|
||||
bl_region_type = Region
|
||||
bl_category = "Speckle"
|
||||
bl_context = "objectmode"
|
||||
bl_label = "User"
|
||||
bl_label = "User Account"
|
||||
|
||||
def draw(self, context):
|
||||
speckle = context.scene.speckle
|
||||
speckle = get_speckle(context)
|
||||
|
||||
layout = self.layout
|
||||
col = layout.column()
|
||||
@@ -119,28 +109,28 @@ class VIEW3D_PT_SpeckleUser(bpy.types.Panel):
|
||||
else:
|
||||
col.prop(speckle, "active_user", text="")
|
||||
user = speckle.users[int(speckle.active_user)]
|
||||
col.label(text="{} ({})".format(user.server_name, user.server_url))
|
||||
col.label(text="{} ({})".format(user.name, user.email))
|
||||
|
||||
col.label(text=f"{user.server_name} ({user.server_url})")
|
||||
col.label(text=f"{user.name} ({user.email})")
|
||||
|
||||
col.operator("speckle.users_load", text="", icon="FILE_REFRESH")
|
||||
|
||||
class VIEW3D_PT_SpeckleStreams(bpy.types.Panel):
|
||||
"""
|
||||
Speckle Streams UI panel in the 3d viewport
|
||||
Speckle projects UI panel in the 3d viewport
|
||||
"""
|
||||
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = Region
|
||||
bl_category = "Speckle"
|
||||
bl_context = "objectmode"
|
||||
bl_label = "Streams"
|
||||
bl_label = "Projects"
|
||||
|
||||
def draw(self, context):
|
||||
speckle = context.scene.speckle
|
||||
speckle = get_speckle(context)
|
||||
col = self.layout.column()
|
||||
|
||||
if len(speckle.users) < 1:
|
||||
col.label(text="No stream data.")
|
||||
col.label(text="No Projects")
|
||||
else:
|
||||
user = speckle.users[int(speckle.active_user)]
|
||||
col.template_list(
|
||||
@@ -154,25 +144,26 @@ class VIEW3D_PT_SpeckleStreams(bpy.types.Panel):
|
||||
|
||||
class VIEW3D_PT_SpeckleActiveStream(bpy.types.Panel):
|
||||
"""
|
||||
Speckle Active Streams UI panel in the 3d viewport
|
||||
Speckle Active Projects UI panel in the 3d viewport
|
||||
"""
|
||||
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = Region
|
||||
bl_category = "Speckle"
|
||||
bl_context = "objectmode"
|
||||
bl_label = "Active stream"
|
||||
bl_label = "Active Project"
|
||||
|
||||
def draw(self, context):
|
||||
speckle = context.scene.speckle
|
||||
speckle = get_speckle(context)
|
||||
col = self.layout.column()
|
||||
|
||||
if len(speckle.users) < 1:
|
||||
col.label(text="No stream data.")
|
||||
col.label(text="No projects")
|
||||
else:
|
||||
user = speckle.users[int(speckle.active_user)]
|
||||
user = speckle.validate_user_selection()
|
||||
#user = speckle.users[int(speckle.active_user)]
|
||||
if len(user.streams) < 1:
|
||||
col.label(text="No active stream.")
|
||||
col.label(text="No active project")
|
||||
else:
|
||||
stream = user.streams[user.active_stream]
|
||||
# user.active_stream = min(user.active_stream, len(user.streams) - 1)
|
||||
@@ -182,14 +173,14 @@ class VIEW3D_PT_SpeckleActiveStream(bpy.types.Panel):
|
||||
col.separator()
|
||||
|
||||
row = col.row()
|
||||
row.prop(stream, "branch", text="")
|
||||
row.operator("speckle.branch_copy_name", text="", icon="COPY_ID")
|
||||
row.prop(stream, "branch", text="Model")
|
||||
row.operator("speckle.model_copy_id", text="", icon="COPY_ID")
|
||||
|
||||
if len(stream.branches) > 0:
|
||||
branch = stream.branches[int(stream.branch)]
|
||||
|
||||
row = col.row()
|
||||
row.prop(branch, "commit", text="")
|
||||
row.prop(branch, "commit", text="Version")
|
||||
row.operator("speckle.commit_copy_id", text="", icon="COPY_ID")
|
||||
|
||||
if len(branch.commits) > 0:
|
||||
@@ -212,7 +203,7 @@ class VIEW3D_PT_SpeckleActiveStream(bpy.types.Panel):
|
||||
col.label(text=f"{commit.author_name} ({commit.author_id})")
|
||||
col.label(text=commit.source_application)
|
||||
else:
|
||||
col.label(text="No branches found!")
|
||||
col.label(text="No models found!")
|
||||
|
||||
col.separator()
|
||||
|
||||
@@ -244,7 +235,7 @@ class VIEW3D_PT_SpeckleActiveStream(bpy.types.Panel):
|
||||
|
||||
area.separator()
|
||||
col.separator()
|
||||
col.operator("speckle.view_stream_data_api", text="Open Stream in Web")
|
||||
col.operator("speckle.view_stream_data_api", text="Open Model in Web")
|
||||
|
||||
|
||||
class VIEW3D_PT_SpeckleHelp(bpy.types.Panel):
|
||||
|
||||
Reference in New Issue
Block a user