feat: better drag and drop
This commit is contained in:
@@ -29,7 +29,8 @@ SUPPORT_EXTS = ['', '.dcm', '.DCM', '.DICOM', '.ima', '.IMA',
|
|||||||
'.png', '.PNG',
|
'.png', '.PNG',
|
||||||
'.vtk',
|
'.vtk',
|
||||||
'.ome.tiff', '.ome.tif',
|
'.ome.tiff', '.ome.tif',
|
||||||
'.mrc', '.mrc.gz', '.map', '.map.gz']
|
'.mrc', '.mrc.gz', '.map', '.map.gz',
|
||||||
|
'.gz']
|
||||||
|
|
||||||
OME_EXTS = ['.ome.tiff', '.ome.tif',
|
OME_EXTS = ['.ome.tiff', '.ome.tif',
|
||||||
'.tif', '.TIF', '.tiff', '.TIFF']
|
'.tif', '.TIF', '.tiff', '.TIFF']
|
||||||
|
|||||||
@@ -24,19 +24,6 @@ from .utils import (change_render_setting, get_cache_dir, get_preferences,
|
|||||||
import SimpleITK as sitk
|
import SimpleITK as sitk
|
||||||
import transforms3d
|
import transforms3d
|
||||||
|
|
||||||
FH_EXTS = ['', '.dcm', '.DCM', '.DICOM', '.ima', '.IMA',
|
|
||||||
'.gipl', '.gipl.gz',
|
|
||||||
'.mnc', '.MNC',
|
|
||||||
'.mrc', '.rec',
|
|
||||||
'.mha', '.mhd',
|
|
||||||
'.nia', '.nii', '.nii.gz', '.hdr', '.img', '.img.gz',
|
|
||||||
'.hdf', '.h4', '.hdf4', '.he2', '.h5', '.hdf5', '.he5',
|
|
||||||
'.nrrd', '.nhdr',
|
|
||||||
'.vtk',
|
|
||||||
'.gz'
|
|
||||||
'.ome.tiff', '.ome.tif',
|
|
||||||
'.mrc', '.mrc.gz', '.map', '.map.gz']
|
|
||||||
|
|
||||||
|
|
||||||
def get_layer_shape(bioxel_size: float, orig_shape: tuple, orig_spacing: tuple):
|
def get_layer_shape(bioxel_size: float, orig_shape: tuple, orig_spacing: tuple):
|
||||||
shape = (int(orig_shape[0] / bioxel_size * orig_spacing[0]),
|
shape = (int(orig_shape[0] / bioxel_size * orig_spacing[0]),
|
||||||
@@ -69,23 +56,19 @@ class ImportVolumetricData():
|
|||||||
bl_options = {'UNDO'}
|
bl_options = {'UNDO'}
|
||||||
|
|
||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH") # type: ignore
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH") # type: ignore
|
||||||
directory: bpy.props.StringProperty(subtype='DIR_PATH') # type: ignore
|
|
||||||
|
|
||||||
read_as = "scalar"
|
read_as = "scalar"
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
container_objs = get_container_objs_from_selection()
|
data_path = Path(self.filepath).resolve()
|
||||||
|
ext = get_ext(data_path)
|
||||||
|
if ext not in SUPPORT_EXTS:
|
||||||
|
self.report({"WARNING"}, "Not supported format.")
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
if len(container_objs) > 0:
|
|
||||||
bpy.ops.bioxelnodes.parse_volumetric_data('INVOKE_DEFAULT',
|
bpy.ops.bioxelnodes.parse_volumetric_data('INVOKE_DEFAULT',
|
||||||
filepath=self.filepath,
|
filepath=self.filepath,
|
||||||
directory=self.directory,
|
skip_read_as=True,
|
||||||
container_obj_name=container_objs[0].name,
|
|
||||||
read_as=self.read_as)
|
|
||||||
else:
|
|
||||||
bpy.ops.bioxelnodes.parse_volumetric_data('INVOKE_DEFAULT',
|
|
||||||
filepath=self.filepath,
|
|
||||||
directory=self.directory,
|
|
||||||
read_as=self.read_as)
|
read_as=self.read_as)
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
@@ -113,7 +96,7 @@ class BIOXELNODES_FH_ImportVolumetricData(bpy.types.FileHandler):
|
|||||||
bl_idname = "BIOXELNODES_FH_ImportVolumetricData"
|
bl_idname = "BIOXELNODES_FH_ImportVolumetricData"
|
||||||
bl_label = "File handler for dicom import"
|
bl_label = "File handler for dicom import"
|
||||||
bl_import_operator = "bioxelnodes.parse_volumetric_data"
|
bl_import_operator = "bioxelnodes.parse_volumetric_data"
|
||||||
bl_file_extensions = ";".join(FH_EXTS)
|
bl_file_extensions = ";".join(SUPPORT_EXTS)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll_drop(cls, context):
|
def poll_drop(cls, context):
|
||||||
@@ -135,21 +118,26 @@ def get_series_ids(self, context):
|
|||||||
|
|
||||||
class ParseVolumetricData(bpy.types.Operator):
|
class ParseVolumetricData(bpy.types.Operator):
|
||||||
bl_idname = "bioxelnodes.parse_volumetric_data"
|
bl_idname = "bioxelnodes.parse_volumetric_data"
|
||||||
bl_label = "Import Volumetric Data"
|
bl_label = "Import Volumetric Data (BioxelNodes)"
|
||||||
bl_description = "Import Volumetric Data as Layer"
|
bl_description = "Import Volumetric Data as Layer"
|
||||||
bl_options = {'UNDO'}
|
bl_options = {'UNDO'}
|
||||||
|
|
||||||
meta = None
|
meta = None
|
||||||
thread = None
|
thread = None
|
||||||
_timer = None
|
_timer = None
|
||||||
|
container_obj_name = ""
|
||||||
|
|
||||||
progress: bpy.props.FloatProperty(name="Progress",
|
progress: bpy.props.FloatProperty(name="Progress",
|
||||||
options={"SKIP_SAVE"},
|
options={"SKIP_SAVE"},
|
||||||
default=1) # type: ignore
|
default=1) # type: ignore
|
||||||
|
|
||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH") # type: ignore
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH") # type: ignore
|
||||||
directory: bpy.props.StringProperty(subtype='DIR_PATH') # type: ignore
|
|
||||||
container_obj_name: bpy.props.StringProperty() # type: ignore
|
skip_read_as: bpy.props.BoolProperty(name="Skip Read As",
|
||||||
|
default=False) # type: ignore
|
||||||
|
|
||||||
|
skip_series_select: bpy.props.BoolProperty(name="Skip Sries Select",
|
||||||
|
default=True) # type: ignore
|
||||||
|
|
||||||
read_as: bpy.props.EnumProperty(name="Read as",
|
read_as: bpy.props.EnumProperty(name="Read as",
|
||||||
default="scalar",
|
default="scalar",
|
||||||
@@ -166,7 +154,7 @@ class ParseVolumetricData(bpy.types.Operator):
|
|||||||
data_path = Path(self.filepath).resolve()
|
data_path = Path(self.filepath).resolve()
|
||||||
ext = get_ext(data_path)
|
ext = get_ext(data_path)
|
||||||
if ext not in SUPPORT_EXTS:
|
if ext not in SUPPORT_EXTS:
|
||||||
self.report({"WARNING"}, "Not supported extension.")
|
self.report({"WARNING"}, "Not supported format.")
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
print("Collecting Meta Data...")
|
print("Collecting Meta Data...")
|
||||||
@@ -297,12 +285,19 @@ class ParseVolumetricData(bpy.types.Operator):
|
|||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
if not self.filepath and not self.directory:
|
if not self.filepath:
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
data_path = Path(self.filepath).resolve()
|
data_path = Path(self.filepath).resolve()
|
||||||
ext = get_ext(data_path)
|
ext = get_ext(data_path)
|
||||||
|
|
||||||
|
container_objs = get_container_objs_from_selection()
|
||||||
|
if len(container_objs) > 0:
|
||||||
|
self.container_obj_name = container_objs[0].name
|
||||||
|
|
||||||
|
title = f"Add to **{self.container_obj_name}**" \
|
||||||
|
if self.container_obj_name != "" else f"Init a Container"
|
||||||
|
|
||||||
# Series Selection
|
# Series Selection
|
||||||
if ext in DICOM_EXTS:
|
if ext in DICOM_EXTS:
|
||||||
data_dirpath = data_path.parent
|
data_dirpath = data_path.parent
|
||||||
@@ -358,9 +353,10 @@ class ParseVolumetricData(bpy.types.Operator):
|
|||||||
series_item.label = value
|
series_item.label = value
|
||||||
|
|
||||||
if len(series_items.keys()) > 1:
|
if len(series_items.keys()) > 1:
|
||||||
|
self.skip_series_select = False
|
||||||
context.window_manager.invoke_props_dialog(self,
|
context.window_manager.invoke_props_dialog(self,
|
||||||
width=400,
|
width=400,
|
||||||
title="Which series to import?")
|
title=title)
|
||||||
return {'RUNNING_MODAL'}
|
return {'RUNNING_MODAL'}
|
||||||
elif len(series_items.keys()) == 1:
|
elif len(series_items.keys()) == 1:
|
||||||
self.series_id = list(series_items.keys())[0]
|
self.series_id = list(series_items.keys())[0]
|
||||||
@@ -368,27 +364,37 @@ class ParseVolumetricData(bpy.types.Operator):
|
|||||||
self.report({"ERROR"}, "Get no vaild series.")
|
self.report({"ERROR"}, "Get no vaild series.")
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
if self.skip_read_as:
|
||||||
self.execute(context)
|
self.execute(context)
|
||||||
return {'RUNNING_MODAL'}
|
return {'RUNNING_MODAL'}
|
||||||
|
else:
|
||||||
|
context.window_manager.invoke_props_dialog(self,
|
||||||
|
width=400,
|
||||||
|
title=title)
|
||||||
|
return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
|
if not self.skip_read_as:
|
||||||
layout.label(
|
layout.label(
|
||||||
text='Detect multi-series in DICOM, pick one')
|
text='What kind is the data read as?')
|
||||||
|
layout.prop(self, "read_as")
|
||||||
|
if not self.skip_series_select:
|
||||||
|
layout.label(text='Which series to import?')
|
||||||
layout.prop(self, "series_id")
|
layout.prop(self, "series_id")
|
||||||
|
|
||||||
|
|
||||||
def get_sequence_sources(self, context):
|
def get_sequence_sources(self, context):
|
||||||
items = [("-1", "None (Static)", "")]
|
items = [("-1", "None (1 frame)", "")]
|
||||||
orig_shape = tuple(self.orig_shape)
|
orig_shape = tuple(self.orig_shape)
|
||||||
if self.frame_count > 1:
|
if self.frame_count > 1:
|
||||||
items.append(("0", f"Frame (Get {self.frame_count} frames)", ""))
|
items.append(("0", f"Frame ({self.frame_count} frames)", ""))
|
||||||
elif self.frame_count == 1 and self.channel_count > 1:
|
elif self.frame_count == 1 and self.channel_count > 1:
|
||||||
items.append(("4", f"Channel (Get {self.channel_count} frames)", ""))
|
items.append(("4", f"Channel ({self.channel_count} frames)", ""))
|
||||||
elif self.frame_count == 1 and self.channel_count == 1:
|
elif self.frame_count == 1 and self.channel_count == 1:
|
||||||
items.append(("1", f"X (Get {orig_shape[0]} frames)", ""))
|
items.append(("1", f"X ({orig_shape[0]} frames)", ""))
|
||||||
items.append(("2", f"Y (Get {orig_shape[1]} frames)", ""))
|
items.append(("2", f"Y ({orig_shape[1]} frames)", ""))
|
||||||
items.append(("3", f"Z (Get {orig_shape[2]} frames)", ""))
|
items.append(("3", f"Z ({orig_shape[2]} frames)", ""))
|
||||||
|
|
||||||
return items
|
return items
|
||||||
|
|
||||||
@@ -681,12 +687,9 @@ class ImportVolumetricDataDialog(bpy.types.Operator):
|
|||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
if self.read_as == "label":
|
layer_kind = self.read_as.capitalize()
|
||||||
volume_dtype = "Label"
|
title = f"Add to **{self.container_obj_name}**, As {layer_kind}" \
|
||||||
elif self.read_as == "scalar":
|
if self.container_obj_name != "" else f"Init a Container, As {layer_kind}"
|
||||||
volume_dtype = "Scalar"
|
|
||||||
title = f"As {volume_dtype} Opitons (Add to Container: {self.container_obj_name})" \
|
|
||||||
if self.container_obj_name != "" else f"As {volume_dtype} Options (Init a Container)"
|
|
||||||
context.window_manager.invoke_props_dialog(self,
|
context.window_manager.invoke_props_dialog(self,
|
||||||
width=500,
|
width=500,
|
||||||
title=title)
|
title=title)
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class SaveStagedData(bpy.types.Operator):
|
|||||||
bl_label = "Save Staged Data"
|
bl_label = "Save Staged Data"
|
||||||
bl_description = "Save all staged data in this file for sharing"
|
bl_description = "Save all staged data in this file for sharing"
|
||||||
|
|
||||||
save_layer: bpy.props.BoolProperty(
|
save_cache: bpy.props.BoolProperty(
|
||||||
name="Save Layer Caches",
|
name="Save Layer Caches",
|
||||||
default=True,
|
default=True,
|
||||||
) # type: ignore
|
) # type: ignore
|
||||||
@@ -112,7 +112,7 @@ class SaveStagedData(bpy.types.Operator):
|
|||||||
|
|
||||||
self.report({"INFO"}, f"Successfully saved to {output_path}")
|
self.report({"INFO"}, f"Successfully saved to {output_path}")
|
||||||
|
|
||||||
if self.save_layer:
|
if self.save_cache:
|
||||||
fails = []
|
fails = []
|
||||||
for layer in get_all_layer_objs():
|
for layer in get_all_layer_objs():
|
||||||
try:
|
try:
|
||||||
@@ -140,7 +140,7 @@ class SaveStagedData(bpy.types.Operator):
|
|||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
panel = layout.box()
|
panel = layout.box()
|
||||||
panel.prop(self, "save_layer")
|
panel.prop(self, "save_cache")
|
||||||
panel.prop(self, "cache_dir")
|
panel.prop(self, "cache_dir")
|
||||||
panel = layout.box()
|
panel = layout.box()
|
||||||
panel.prop(self, "save_lib")
|
panel.prop(self, "save_lib")
|
||||||
@@ -148,7 +148,7 @@ class SaveStagedData(bpy.types.Operator):
|
|||||||
|
|
||||||
|
|
||||||
class SaveCaches(bpy.types.Operator):
|
class SaveCaches(bpy.types.Operator):
|
||||||
bl_idname = "bioxelnodes.save_layers"
|
bl_idname = "bioxelnodes.save_caches"
|
||||||
bl_label = "Save Caches"
|
bl_label = "Save Caches"
|
||||||
bl_description = "Save Container's caches to directory."
|
bl_description = "Save Container's caches to directory."
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user