Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 74 additions & 20 deletions src/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from copick.models import CopickMesh, CopickPicks, CopickSegmentation
from copick.util.uri import serialize_copick_uri
from copick_shared_ui.core.thumbnail_cache import set_global_cache_config, set_global_cache_image_interface
from copick_shared_ui.core.types import segmentation_type_flags, segmentation_type_of
from copick_shared_ui.core.types import copick_object_type, segmentation_type_flags, segmentation_type_of
from copick_shared_ui.util.instances import instance_colors

# Qt
Expand Down Expand Up @@ -1203,34 +1203,88 @@ def new_particles(self, object_name: str, user_id: str, session_id: str):
# Set mouse mode to "mark plane" (pick on plane)
run(self.session, "ui mousemode right 'mark plane'", log=False)

def duplicate_mesh(self, index: QModelIndex, user_id: str = "", session_id: str = ""):
"""Placeholder for mesh duplication"""
# Get entity from unified table model
model = index.model()
entity = model.get_entity(index)
def _duplicate_entity(self, index: QModelIndex, user_id: str, session_id: str, object_type: str) -> Any:
"""Copy the filament set, mesh or segmentation at ``index`` to a new user / session.

if not isinstance(entity, CopickMesh):
return
Empty user_id/session_id fall back to the root user and "<session>-copy-1". Returns the copy, or None (with a
warning in the log) if the target already exists or the copy failed.
"""
if not index.isValid():
return None
entity = index.model().get_entity(index)
if entity is None or copick_object_type(entity) != object_type:
return None

# TODO: Implement mesh duplication logic
pass
self.store() # write pending edits first, so the copy includes them

if not user_id:
user_id = self.root.user_id if self.root.user_id is not None else "ArtiaX"
if not session_id:
session_id = f"{entity.session_id}-copy-1"

from copick.ops.manage import copy_copick_objects

source = serialize_copick_uri(entity)
name, rest = source.split(":", 1)
suffix = rest[rest.index("@") :] if "@" in rest else "" # voxel size and segmentation type
target = f"{name}:{user_id}/{session_id}{suffix}"
kind = {"mesh": "mesh", "segmentation": "segmentation", "filaments": "filament set"}[object_type]
try:
result = copy_copick_objects(self.root, object_type, source, target, run_name=entity.run.name)
except ValueError as e:
result = {"copied": 0, "errors": [str(e)]}
if not result["copied"]:
reason = "; ".join(result["errors"]) or "nothing was copied"
self.session.logger.warning(
f"Cannot duplicate the {kind} {source} as {target} ({reason}). Choose a different session ID.",
)
return None

run_ = entity.run
if object_type == "filaments":
copies = run_.get_filaments(object_name=name, user_id=user_id, session_id=session_id)
elif object_type == "mesh":
copies = run_.get_meshes(object_name=name, user_id=user_id, session_id=session_id)
else:
copies = [
s
for s in run_.get_segmentations(
name=name,
user_id=user_id,
session_id=session_id,
is_instance=None,
is_panoptic=None,
)
if s.voxel_size == entity.voxel_size and segmentation_type_of(s) == segmentation_type_of(entity)
]
self.session.logger.info(f"copick: duplicated the {kind} {source} as {target}")
return copies[0] if copies else None

def duplicate_filaments(self, index: QModelIndex, user_id: str = "", session_id: str = ""):
"""Duplicate a filament set to a new user / session, and show the copy."""
copy = self._duplicate_entity(index, user_id, session_id, "filaments")
self._mw.update_filaments_table()
if copy is not None:
self._show_filaments_entity(copy)

def duplicate_mesh(self, index: QModelIndex, user_id: str = "", session_id: str = ""):
"""Duplicate a mesh to a new user / session, and show the copy."""
copy = self._duplicate_entity(index, user_id, session_id, "mesh")
self._mw.update_meshes_table()
if copy is not None:
self._show_mesh_entity(copy)

def new_mesh(self, object_name: str, user_id: str, session_id: str):
"""Placeholder for new mesh creation"""
# TODO: Implement new mesh creation logic
pass

def duplicate_segmentation(self, index: QModelIndex, user_id: str = "", session_id: str = ""):
"""Placeholder for segmentation duplication"""
# Get entity from unified table model
model = index.model()
entity = model.get_entity(index)

if not isinstance(entity, CopickSegmentation):
return

# TODO: Implement segmentation duplication logic
pass
"""Duplicate a segmentation (keeping its type and voxel size) to a new user / session, and show the copy."""
copy = self._duplicate_entity(index, user_id, session_id, "segmentation")
self._mw.update_segmentations_table()
if copy is not None:
self._show_segmentation_entity(copy)

def new_segmentation(self, object_name: str, user_id: str, session_id: str):
"""Start a new (empty) instance segmentation of ``object_name`` at the active tomogram's voxel spacing and
Expand Down
7 changes: 7 additions & 0 deletions src/ui/main_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,7 @@ def _connect(self):
self._filaments_table.get_table_view().clicked.connect(
lambda i: self._copick.activate_filaments(self._map_index(self._filaments_table, i)),
)
self._filaments_table.duplicateClicked.connect(self._copick.duplicate_filaments)
self._filaments_table.newClicked.connect(self._on_new_filaments)
self._filaments_table.deleteClicked.connect(self._copick.delete_filaments)

Expand Down Expand Up @@ -629,6 +630,12 @@ def update_filaments_table(self):
if not isinstance(key, type(ctl.entries[key][0])) and ctl.is_shown(key):
self._filaments_table.set_entity_active(key, True)

def update_meshes_table(self):
self._meshes_table.update()
for mesh, surf in list(self._copick.mesh_map.items()):
if getattr(surf, "display", False):
self._meshes_table.set_entity_active(mesh, True)

def update_segmentations_table(self):
self._segmentations_table.update()
for seg in list(self._copick.seg_map) + list(self._copick.segmentations.models):
Expand Down
Loading