refactor: Create property group MultipleFileSelect

To easier create and display properties that allow one or more files as input, a `MultipleFileSelect` property group has been extracted, together with an operator for selecting the files and the code for displaying the property.
This commit is contained in:
Blender Defender
2024-07-29 12:42:31 +02:00
committed by Dion Moult
parent 98edb5a34c
commit 95a7ea8dce
12 changed files with 71 additions and 93 deletions
@@ -101,6 +101,7 @@ classes = [
operator.BIM_OT_remove_section_plane,
operator.BIM_OT_select_object,
operator.BIM_OT_show_description,
operator.BIM_OT_multiple_file_selector,
operator.ClippingPlaneCutWithCappings,
operator.CloseError,
operator.EditBlenderCollection,
@@ -132,6 +133,7 @@ classes = [
prop.BIMMeshProperties,
prop.BIMFacet,
prop.BIMFilterGroup,
prop.MultipleFileSelect,
ui.BIM_UL_clipping_plane,
ui.BIM_UL_generic,
ui.BIM_UL_topics,
@@ -22,7 +22,6 @@ from . import ui, prop, operator
classes = (
operator.ExecuteIfcFM,
operator.ExecuteIfcFMFederate,
operator.SelectFMIfcFile,
operator.SelectFMSpreadsheetFiles,
prop.BIMFMProperties,
ui.BIM_PT_fm,
@@ -24,20 +24,6 @@ import logging
import tempfile
import ifcopenshell
import blenderbim.tool as tool
from blenderbim.bim.operator import MultipleFileSelector
class SelectFMIfcFile(MultipleFileSelector):
bl_idname = "bim.select_fm_ifc_file"
bl_label = "Select FM IFC File"
filename_ext = ".ifc"
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"})
def execute(self, context):
props = context.scene.BIMFMProperties
self.update_props(props, "ifc_file", props.ifc_files)
return {"FINISHED"}
class ExecuteIfcFM(bpy.types.Operator):
@@ -50,7 +36,7 @@ class ExecuteIfcFM(bpy.types.Operator):
@classmethod
def poll(cls, context):
props = context.scene.BIMFMProperties
return props.should_load_from_memory or props.ifc_file
return props.should_load_from_memory or props.ifc_files.single_file
def invoke(self, context, event):
props = context.scene.BIMFMProperties
@@ -63,14 +49,13 @@ class ExecuteIfcFM(bpy.types.Operator):
props = context.scene.BIMFMProperties
ifc_file = tool.Ifc.get()
filepaths = []
if not (ifc_file and props.should_load_from_memory):
if len(props.ifc_files):
ifc_files = [ifcopenshell.open(f.name) for f in props.ifc_files]
filepaths = [f.name for f in props.ifc_files]
else:
ifc_files = [ifcopenshell.open(props.ifc_file)]
else:
if ifc_file and props.should_load_from_memory:
ifc_files = [ifc_file]
else:
ifc_files = [ifcopenshell.open(f.name) for f in props.ifc_files.file_list]
if len(ifc_files) > 1:
filepaths = [f.name for f in props.ifc_files.file_list]
for i, ifc_file in enumerate(ifc_files):
if filepaths:
@@ -18,7 +18,7 @@
import bpy
from blenderbim.bim.module.fm.data import FMData
from blenderbim.bim.prop import StrProperty
from blenderbim.bim.prop import MultipleFileSelect, StrProperty
from bpy.types import PropertyGroup
from bpy.props import (
PointerProperty,
@@ -39,8 +39,7 @@ def get_engine(self, context):
class BIMFMProperties(PropertyGroup):
ifc_file: StringProperty(default="", name="IFC File")
ifc_files: CollectionProperty(name="IFC Files", type=StrProperty)
ifc_files: PointerProperty(type=MultipleFileSelect)
spreadsheet_files: CollectionProperty(name="Spreadsheets", type=StrProperty)
should_load_from_memory: BoolProperty(default=False, name="Load from Memory")
engine: EnumProperty(items=get_engine, name="Engine")
@@ -45,12 +45,8 @@ class BIM_PT_fm(Panel):
row.prop(props, "should_load_from_memory")
if not IfcStore.get_file() or not props.should_load_from_memory:
row = layout.row(align=True)
if len(props.ifc_files) > 1:
row.label(text=f"{len(props.ifc_files)} Files Selected")
else:
row.prop(props, "ifc_file")
row.operator("bim.select_fm_ifc_file", icon="FILE_FOLDER", text="")
row = layout.row()
props.ifc_files.layout_file_select(row, "*.ifc;*.ifczip;*.ifcxml", "IFC File(s)")
row = layout.row()
row.prop(props, "engine")
@@ -48,7 +48,6 @@ class BIM_PT_radiance_exporter(bpy.types.Panel):
if not tool.Ifc.get() or not props.should_load_from_memory:
row = self.layout.row(align=True)
row.prop(props, "ifc_file")
# row.operator("bim.select_ifctester_ifc_file", icon="FILE_FOLDER", text="")
row = layout.row()
row.prop(props, "output_dir")
@@ -21,8 +21,6 @@ from . import ui, prop, operator
classes = (
operator.ExecuteIfcTester,
operator.SelectSpecs,
operator.SelectIfcTesterIfcFile,
operator.SelectRequirement,
operator.SelectFailedEntities,
operator.SelectEntity,
@@ -27,7 +27,6 @@ import ifctester.reporter
import ifcopenshell
import blenderbim.tool as tool
import blenderbim.bim.handler
from blenderbim.bim.operator import MultipleFileSelector
from pathlib import Path
@@ -38,7 +37,7 @@ class ExecuteIfcTester(bpy.types.Operator, tool.Ifc.Operator):
@classmethod
def poll(cls, context):
props = context.scene.IfcTesterProperties
return (props.ifc_file or props.should_load_from_memory) and props.specs
return (props.ifc_files.single_file or props.should_load_from_memory) and props.specs
def execute(self, context):
props = context.scene.IfcTesterProperties
@@ -49,13 +48,13 @@ class ExecuteIfcTester(bpy.types.Operator, tool.Ifc.Operator):
ifc_data = tool.Ifc.get()
ifc_path = tool.Ifc.get_path()
for f in props.specs_files:
for f in props.specs.file_list:
self.execute_tester(ifc_data, ifc_path, f.name)
else:
for ifc_file in props.ifc_files:
for ifc_file in props.ifc_files.file_list:
ifc_data = ifcopenshell.open(ifc_file.name)
for f in props.specs_files:
for f in props.specs.file_list:
self.execute_tester(ifc_data, ifc_file.name, f.name)
blenderbim.bim.handler.refresh_ui_data()
@@ -103,33 +102,6 @@ class ExecuteIfcTester(bpy.types.Operator, tool.Ifc.Operator):
new_spec.status = spec["status"]
class SelectSpecs(MultipleFileSelector):
bl_idname = "bim.select_specs"
bl_label = "Select IDS"
filename_ext = ".ids"
filter_glob: bpy.props.StringProperty(default="*.ids;*.xml", options={"HIDDEN"})
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def execute(self, context):
props = context.scene.IfcTesterProperties
self.update_props(props, "specs", props.specs_files)
return {"FINISHED"}
class SelectIfcTesterIfcFile(MultipleFileSelector):
bl_idname = "bim.select_ifctester_ifc_file"
bl_label = "Select IfcTester IFC File"
filename_ext = ".ifc"
filter_glob: bpy.props.StringProperty(default="*.ifc;*.ifczip;*.ifcxml", options={"HIDDEN"})
def execute(self, context):
props = context.scene.IfcTesterProperties
self.update_props(props, "ifc_file", props.ifc_files)
return {"FINISHED"}
class SelectRequirement(bpy.types.Operator):
bl_idname = "bim.select_requirement"
bl_label = "Select Specification"
@@ -17,7 +17,7 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
from blenderbim.bim.module.tester.data import TesterData
from blenderbim.bim.prop import StrProperty
from blenderbim.bim.prop import StrProperty, MultipleFileSelect
from bpy.types import PropertyGroup
from bpy.props import (
PointerProperty,
@@ -48,10 +48,8 @@ class FailedEntities(PropertyGroup):
class IfcTesterProperties(PropertyGroup):
specs: StringProperty(default="", name="IDS File(s)")
specs_files: CollectionProperty(name="IDS Files", type=StrProperty)
ifc_file: StringProperty(default="", name="IFC File(s)")
ifc_files: CollectionProperty(name="IFC Files", type=StrProperty)
specs: PointerProperty(type=MultipleFileSelect)
ifc_files: PointerProperty(type=MultipleFileSelect)
should_load_from_memory: BoolProperty(default=False, name="Load from Memory", options=set())
generate_html_report: BoolProperty(default=False, name="Generate HTML report", options=set())
generate_ods_report: BoolProperty(default=False, name="Generate ODS report", options=set())
@@ -50,19 +50,10 @@ class BIM_PT_tester(Panel):
if not tool.Ifc.get() or not props.should_load_from_memory:
row = self.layout.row(align=True)
if len(props.ifc_files) > 1:
row.label(text=f"{len(props.ifc_files)} Files Selected")
else:
row.prop(props, "ifc_file")
row.operator("bim.select_ifctester_ifc_file", icon="FILE_FOLDER", text="")
props.ifc_files.layout_file_select(row, "*.ifc;*.ifczip;*.ifcxml", "IFC File(s)")
row = self.layout.row(align=True)
if len(props.specs_files) > 1:
row.label(text=f"{len(props.specs_files)} Files Selected")
else:
row.prop(props, "specs")
row.operator("bim.select_specs", icon="FILE_FOLDER", text="")
props.specs.layout_file_select(row, "*.ids;*.xml", "IDS File(s)")
row = self.layout.row()
row.operator("bim.execute_ifc_tester")
+21 -10
View File
@@ -143,22 +143,30 @@ class SelectURIAttribute(bpy.types.Operator):
return {"RUNNING_MODAL"}
class MultipleFileSelector(bpy.types.Operator):
class BIM_OT_multiple_file_selector(bpy.types.Operator):
"""Open Blender's file explorer to select one or multiple files."""
bl_idname = "bim.multiple_file_selector"
bl_label = "Select Multiple Files"
bl_options = {"REGISTER", "UNDO"}
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
files: bpy.props.CollectionProperty(name="File Path", type=bpy.types.OperatorFileListElement)
filter_glob: bpy.props.StringProperty(default="*", options={"HIDDEN"})
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
def update_props(self, props, single_file: str, file_list: bpy.types.CollectionProperty):
@classmethod
def poll(self, context):
return getattr(context, "file_props", None) is not None
def execute(self, context):
dirname = os.path.dirname(self.filepath)
file_list.clear()
self.file_props.single_file = self.filepath
self.file_props.set_file_list(dirname, [f.name for f in self.files])
for f in self.files:
new = file_list.add()
new.name = os.path.join(dirname, f.name)
setattr(props, single_file, self.filepath)
return {"FINISHED"}
def invoke(self, context, event):
self.file_props = context.file_props
context.window_manager.fileselect_add(self)
return {"RUNNING_MODAL"}
@@ -458,7 +466,8 @@ class BIM_OT_add_section_plane(bpy.types.Operator):
group.interface.new_socket(name="Line Decorator", in_out="OUTPUT", socket_type="NodeSocketFloat")
else:
group.inputs.new("NodeSocketFloat", "Value")
group.inputs["Value"].default_value = 1.0 # Mandatory multiplier for the last node group
# Mandatory multiplier for the last node group
group.inputs["Value"].default_value = 1.0
group.inputs.new("NodeSocketVector", "Vector")
group.inputs.new("NodeSocketFloat", "Line Decorator")
group.outputs.new("NodeSocketFloat", "Value")
@@ -526,12 +535,14 @@ class BIM_OT_add_section_plane(bpy.types.Operator):
mix_decorator = group.nodes.new(type="ShaderNodeMixShader")
mix_decorator.name = "Line Decorator Mix"
mix_decorator.inputs[0].default_value = 0 # Directly pass input shader when there is no cutaway
# Directly pass input shader when there is no cutaway
mix_decorator.inputs[0].default_value = 0
mix_decorator.location = group_output.location - Vector((200, 0))
mix_section = group.nodes.new(type="ShaderNodeMixShader")
mix_section.name = "Section Mix"
mix_section.inputs[0].default_value = 1 # Directly pass input shader when there is no cutaway
# Directly pass input shader when there is no cutaway
mix_section.inputs[0].default_value = 1
mix_section.location = mix_decorator.location - Vector((200, 200))
transparent = nodes.new(type="ShaderNodeBsdfTransparent")
+28
View File
@@ -478,3 +478,31 @@ class BIMFacet(PropertyGroup):
class BIMFilterGroup(PropertyGroup):
filters: CollectionProperty(type=BIMFacet, name="filters")
def update_single_file(self, context):
self.file_list.clear()
new = self.file_list.add()
new.name = self.single_file
class MultipleFileSelect(bpy.types.PropertyGroup):
single_file: bpy.props.StringProperty(name="Single File Path", description="", update=update_single_file)
file_list: bpy.props.CollectionProperty(type=StrProperty)
def set_file_list(self, dirname: str, files: list[str]):
self.file_list.clear()
for f in files:
new = self.file_list.add()
new.name = os.path.join(dirname, f)
def layout_file_select(self, layout, filter_glob="", text=""):
if len(self.file_list) > 1:
layout.label(text=f"{len(self.file_list)} Files Selected")
else:
layout.prop(self, "single_file", text=text)
layout.context_pointer_set("file_props", self)
op = layout.operator("bim.multiple_file_selector", icon="FILE_FOLDER", text="")
op.filter_glob = filter_glob