mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-08 05:16:25 +00:00
887 lines
27 KiB
Python
887 lines
27 KiB
Python
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
|
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
|
#
|
|
# This file is part of BlenderBIM Add-on.
|
|
#
|
|
# BlenderBIM Add-on is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# BlenderBIM Add-on is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import os
|
|
import bpy
|
|
import addon_utils
|
|
from pathlib import Path
|
|
from bpy.types import Panel
|
|
from bpy.props import StringProperty, IntProperty, BoolProperty
|
|
from ifcopenshell.util.doc import (
|
|
get_entity_doc,
|
|
get_property_set_doc,
|
|
get_type_doc,
|
|
get_attribute_doc,
|
|
)
|
|
from . import ifc
|
|
import blenderbim.tool as tool
|
|
import blenderbim.bim
|
|
from blenderbim.bim.helper import IfcHeaderExtractor
|
|
from blenderbim.bim.prop import Attribute
|
|
|
|
|
|
class IFCFileSelector:
|
|
def is_existing_ifc_file(self, filepath=None):
|
|
if filepath is None:
|
|
filepath = self.filepath
|
|
return os.path.exists(filepath) and "ifc" in os.path.splitext(filepath)[1].lower()
|
|
|
|
def get_filepath(self):
|
|
"""get filepath taking into account relative paths"""
|
|
if self.use_relative_path:
|
|
filepath = os.path.relpath(self.filepath, bpy.path.abspath("//"))
|
|
else:
|
|
filepath = self.filepath
|
|
return filepath
|
|
|
|
def draw(self, context):
|
|
# Access filepath & Directory https://blender.stackexchange.com/a/207665
|
|
params = context.space_data.params
|
|
# Decode byte string https://stackoverflow.com/a/47737082/
|
|
directory = Path(params.directory.decode("utf-8"))
|
|
filepath = os.path.join(directory, params.filename)
|
|
layout = self.layout
|
|
if self.is_existing_ifc_file(filepath):
|
|
box = layout.box()
|
|
box.label(text="IFC Header Specifications", icon="INFO")
|
|
header_data = IfcHeaderExtractor(filepath).extract()
|
|
for key, value in header_data.items():
|
|
if value != "":
|
|
split = box.split()
|
|
split.label(text=key.title().replace("_", " "))
|
|
split.label(text=str(value))
|
|
if (
|
|
key.lower() == "schema_name"
|
|
and str(value).lower() == "ifc2x3"
|
|
and filepath[-4:].lower() == ".ifc"
|
|
):
|
|
row = box.row()
|
|
op = row.operator("bim.run_migrate_patch", text="Upgrade to IFC4")
|
|
op.infile = filepath
|
|
op.outfile = filepath[0:-4] + "-IFC4.ifc"
|
|
op.schema = "IFC4"
|
|
|
|
if bpy.data.is_saved:
|
|
layout.prop(self, "use_relative_path")
|
|
else:
|
|
self.use_relative_path = False
|
|
layout.label(text="Save the .blend file first ")
|
|
layout.label(text="to use relative paths for .ifc.")
|
|
|
|
|
|
class BIM_PT_section_plane(Panel):
|
|
bl_label = "Temporary Section Cutaways"
|
|
bl_idname = "BIM_PT_section_plane"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "output"
|
|
bl_options = {"DEFAULT_CLOSED"}
|
|
bl_parent_id = "BIM_PT_tab_sandbox"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
props = context.scene.BIMProperties
|
|
|
|
layout.prop(props, "should_section_selected_objects")
|
|
layout.prop(props, "section_plane_colour")
|
|
layout.prop(props, "section_line_decorator_width")
|
|
|
|
row = layout.row(align=True)
|
|
row.operator("bim.add_section_plane")
|
|
row.operator("bim.remove_section_plane")
|
|
|
|
|
|
class BIM_UL_generic(bpy.types.UIList):
|
|
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
|
if item:
|
|
layout.prop(item, "name", text="", emboss=False)
|
|
else:
|
|
layout.label(text="", translate=False)
|
|
|
|
|
|
class BIM_UL_topics(bpy.types.UIList):
|
|
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
|
ob = data
|
|
if item:
|
|
layout.prop(item, "title", text="", emboss=False)
|
|
else:
|
|
layout.label(text="", translate=False)
|
|
|
|
|
|
class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
|
bl_idname = "blenderbim"
|
|
svg2pdf_command: StringProperty(name="SVG to PDF Command", description='E.g. [["inkscape", "svg", "-o", pdf]]')
|
|
svg2dxf_command: StringProperty(
|
|
name="SVG to DXF Command",
|
|
description='E.g. [["inkscape", "svg", "-o", "eps"], ["pstoedit", "-dt", "-f", "dxf:-polyaslines -mm", "eps", "dxf", "-psarg", "-dNOSAFER"]]',
|
|
)
|
|
svg_command: StringProperty(name="SVG Command", description='E.g. [["firefox", "path"]]')
|
|
layout_svg_command: StringProperty(name="Layout SVG Command", description='E.g. [["firefox", "path"]]')
|
|
pdf_command: StringProperty(name="PDF Command", description='E.g. [["firefox", "path"]]')
|
|
spreadsheet_command: StringProperty(name="Spreadsheet Command", description='E.g. [["libreoffice", "path"]]')
|
|
openlca_port: IntProperty(name="OpenLCA IPC Port", default=8080)
|
|
should_hide_empty_props: BoolProperty(name="Should Hide Empty Properties", default=True)
|
|
should_setup_workspace: BoolProperty(name="Should Setup Workspace Layout for BIM", default=True)
|
|
should_setup_toolbar: BoolProperty(
|
|
name="Always Show Toolbar In 3D Viewport",
|
|
default=True,
|
|
description="If disabled, the toolbar will only load when an IFC model is active",
|
|
)
|
|
should_play_chaching_sound: BoolProperty(
|
|
name="Should Make A Cha-Ching Sound When Project Costs Updates", default=False
|
|
)
|
|
lock_grids_on_import: BoolProperty(name="Should Lock Grids By Default", default=True)
|
|
decorations_colour: bpy.props.FloatVectorProperty(
|
|
name="Decorations Colour", subtype="COLOR", default=(1, 1, 1, 1), min=0.0, max=1.0, size=4
|
|
)
|
|
decorator_color_selected: bpy.props.FloatVectorProperty(
|
|
name="Selected Elements Color",
|
|
subtype="COLOR",
|
|
default=(0.545, 0.863, 0, 1), # green
|
|
min=0.0,
|
|
max=1.0,
|
|
size=4,
|
|
description="Color of selected verts/edges (used in profile editing mode)",
|
|
)
|
|
decorator_color_unselected: bpy.props.FloatVectorProperty(
|
|
name="Not Selected Elements Color",
|
|
subtype="COLOR",
|
|
default=(1, 1, 1, 1), # white
|
|
min=0.0,
|
|
max=1.0,
|
|
size=4,
|
|
description="Color of not selected verts/edges (used in profile editing mode)",
|
|
)
|
|
decorator_color_special: bpy.props.FloatVectorProperty(
|
|
name="Special Elements Color",
|
|
subtype="COLOR",
|
|
default=(0.157, 0.565, 1, 1), # blue
|
|
min=0.0,
|
|
max=1.0,
|
|
size=4,
|
|
description="Color of special selected verts/edges (openings, preview verts/edges in roof editing, verts with arcs/circles in profile editing)",
|
|
)
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
box = layout.box()
|
|
row = box.row()
|
|
row.label(
|
|
text="To uninstall: 1) Disable the add-on 2) Restart Blender 3) Press the 'Remove' button.",
|
|
icon="ERROR",
|
|
)
|
|
row = box.row()
|
|
row.label(
|
|
text="To upgrade, first uninstall your current BlenderBIM Add-on, then install the new version.",
|
|
icon="ERROR",
|
|
)
|
|
|
|
row = layout.row()
|
|
row.operator("bim.open_upstream", text="Help Donate to Fund Development!", icon="FUND").page = "fund"
|
|
row = layout.row()
|
|
row.operator("bim.open_upstream", text="Visit Homepage").page = "home"
|
|
row.operator("bim.open_upstream", text="Visit Documentation").page = "docs"
|
|
row = layout.row()
|
|
row.operator("bim.open_upstream", text="Visit Wiki").page = "wiki"
|
|
row.operator("bim.open_upstream", text="Visit Community").page = "community"
|
|
row = layout.row()
|
|
row.operator("bim.file_associate", icon="LOCKVIEW_ON")
|
|
row.operator("bim.file_unassociate", icon="LOCKVIEW_OFF")
|
|
row = layout.row()
|
|
row.prop(self, "svg2pdf_command")
|
|
row = layout.row()
|
|
row.prop(self, "svg2dxf_command")
|
|
row = layout.row()
|
|
row.prop(self, "svg_command")
|
|
row = layout.row()
|
|
row.prop(self, "layout_svg_command")
|
|
row = layout.row()
|
|
row.prop(self, "pdf_command")
|
|
row = layout.row()
|
|
row.prop(self, "spreadsheet_command")
|
|
row = layout.row()
|
|
row.prop(self, "openlca_port")
|
|
row = layout.row()
|
|
row.prop(self, "should_hide_empty_props")
|
|
row = layout.row()
|
|
row.prop(self, "should_setup_workspace")
|
|
row = layout.row()
|
|
row.prop(self, "should_setup_toolbar")
|
|
row = layout.row()
|
|
row.prop(self, "should_play_chaching_sound")
|
|
row = layout.row()
|
|
row.prop(self, "lock_grids_on_import")
|
|
|
|
row = layout.row()
|
|
row.prop(context.scene.BIMProjectProperties, "should_disable_undo_on_save")
|
|
row = layout.row()
|
|
row.prop(context.scene.BIMProjectProperties, "should_stream")
|
|
|
|
row = layout.row()
|
|
row.prop(context.scene.BIMModelProperties, "occurrence_name_style")
|
|
row = layout.row()
|
|
row.prop(context.scene.BIMModelProperties, "occurrence_name_function")
|
|
|
|
row = self.layout.row()
|
|
row.prop(self, "decorations_colour")
|
|
row = self.layout.row()
|
|
row.prop(self, "decorator_color_selected")
|
|
row = self.layout.row()
|
|
row.prop(self, "decorator_color_unselected")
|
|
row = self.layout.row()
|
|
row.prop(self, "decorator_color_special")
|
|
|
|
row = self.layout.row(align=True)
|
|
row.prop(context.scene.BIMProperties, "schema_dir")
|
|
row.operator("bim.select_schema_dir", icon="FILE_FOLDER", text="")
|
|
|
|
row = self.layout.row(align=True)
|
|
row.prop(context.scene.BIMProperties, "data_dir")
|
|
row.operator("bim.select_data_dir", icon="FILE_FOLDER", text="")
|
|
|
|
row = self.layout.row(align=True)
|
|
row.prop(context.scene.BIMProperties, "pset_dir")
|
|
row = self.layout.row(align=True)
|
|
row.prop(context.scene.DocProperties, "sheets_dir")
|
|
row = self.layout.row(align=True)
|
|
row.prop(context.scene.DocProperties, "layouts_dir")
|
|
row = self.layout.row(align=True)
|
|
row.prop(context.scene.DocProperties, "titleblocks_dir")
|
|
row = self.layout.row(align=True)
|
|
row.prop(context.scene.DocProperties, "drawings_dir")
|
|
row = self.layout.row(align=True)
|
|
row.prop(context.scene.DocProperties, "stylesheet_path")
|
|
row = self.layout.row(align=True)
|
|
row.prop(context.scene.DocProperties, "markers_path")
|
|
row = self.layout.row(align=True)
|
|
row.prop(context.scene.DocProperties, "symbols_path")
|
|
row = self.layout.row(align=True)
|
|
row.prop(context.scene.DocProperties, "patterns_path")
|
|
row = self.layout.row(align=True)
|
|
row.prop(context.scene.DocProperties, "shadingstyles_path")
|
|
row = self.layout.row(align=True)
|
|
row.prop(context.scene.DocProperties, "shadingstyle_default")
|
|
|
|
|
|
# Scene panel groups
|
|
class BIM_PT_tabs(Panel):
|
|
bl_label = "BlenderBIM Add-on"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
bl_order = 0
|
|
bl_options = {"HIDE_HEADER"}
|
|
|
|
def draw(self, context):
|
|
try:
|
|
is_ifc_project = bool(tool.Ifc.get())
|
|
aprops = tool.Blender.get_area_props(context)
|
|
if not aprops:
|
|
# Fallback in case areas aren't setup yet.
|
|
aprops = context.screen.BIMTabProperties
|
|
|
|
row = self.layout.row()
|
|
row.operator(
|
|
"bim.set_tab",
|
|
text="",
|
|
emboss=aprops.tab == "PROJECT",
|
|
depress=True,
|
|
icon_value=blenderbim.bim.icons["IFC"].icon_id,
|
|
).tab = "PROJECT"
|
|
self.draw_tab_entry(row, "FILE_3D", "OBJECT", is_ifc_project, aprops.tab == "OBJECT")
|
|
self.draw_tab_entry(row, "MATERIAL", "GEOMETRY", is_ifc_project, aprops.tab == "GEOMETRY")
|
|
self.draw_tab_entry(row, "DOCUMENTS", "DRAWINGS", is_ifc_project, aprops.tab == "DRAWINGS")
|
|
self.draw_tab_entry(row, "NETWORK_DRIVE", "SERVICES", is_ifc_project, aprops.tab == "SERVICES")
|
|
self.draw_tab_entry(row, "EDITMODE_HLT", "STRUCTURE", is_ifc_project, aprops.tab == "STRUCTURE")
|
|
self.draw_tab_entry(row, "NLA", "SCHEDULING", is_ifc_project, aprops.tab == "SCHEDULING")
|
|
self.draw_tab_entry(row, "PACKAGE", "FM", True, aprops.tab == "FM")
|
|
self.draw_tab_entry(row, "COMMUNITY", "QUALITY", True, aprops.tab == "QUALITY")
|
|
self.draw_tab_entry(row, "BLENDER", "BLENDER", True, aprops.tab == "BLENDER")
|
|
row.operator("bim.switch_tab", text="", emboss=False, icon="UV_SYNC_SELECT")
|
|
|
|
# Yes, that's right.
|
|
row = self.layout.row()
|
|
row.scale_y = 0.2
|
|
for tab in [
|
|
"PROJECT",
|
|
"OBJECT",
|
|
"GEOMETRY",
|
|
"DRAWINGS",
|
|
"SERVICES",
|
|
"STRUCTURE",
|
|
"SCHEDULING",
|
|
"FM",
|
|
"QUALITY",
|
|
"BLENDER",
|
|
"SWITCH",
|
|
]:
|
|
if aprops.tab == tab:
|
|
row.prop(aprops, "active_tab", text="", icon="BLANK1")
|
|
else:
|
|
row.prop(aprops, "inactive_tab", text="", icon="BLANK1", emboss=False)
|
|
|
|
row = self.layout.row(align=True)
|
|
row.prop(aprops, "tab", text="")
|
|
except:
|
|
pass # Prior to load_post, we may not have any area properties setup
|
|
|
|
def draw_tab_entry(self, row, icon, tab_name, enabled=True, highlight=False):
|
|
tab_entry = row.row(align=True)
|
|
tab_entry.operator("bim.set_tab", text="", emboss=highlight, icon=icon, depress=True).tab = tab_name
|
|
tab_entry.enabled = enabled
|
|
|
|
|
|
class BIM_PT_tab_project_info(Panel):
|
|
bl_label = "Project Info"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "PROJECT")
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_project_setup(Panel):
|
|
bl_label = "Project Setup"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "PROJECT")
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_stakeholders(Panel):
|
|
bl_label = "Stakeholders"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
bl_options = {"DEFAULT_CLOSED"}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "PROJECT") and tool.Ifc.get()
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_collaboration(Panel):
|
|
bl_label = "Collaboration"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "QUALITY")
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_grouping_and_filtering(Panel):
|
|
bl_label = "Grouping and Filtering"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
bl_options = {"DEFAULT_CLOSED"}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "PROJECT") and tool.Ifc.get()
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_geometry(Panel):
|
|
bl_label = "Geometry"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "PROJECT") and tool.Ifc.get()
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_status(Panel):
|
|
bl_label = "Status"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "SCHEDULING") and tool.Ifc.get()
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_resources(Panel):
|
|
bl_label = "Resources"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "SCHEDULING") and tool.Ifc.get()
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_cost(Panel):
|
|
bl_label = "Cost"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "SCHEDULING") and tool.Ifc.get()
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_sequence(Panel):
|
|
bl_label = "Construction Scheduling"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "SCHEDULING") and tool.Ifc.get()
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_structural(Panel):
|
|
bl_label = "Structural"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "STRUCTURE") and tool.Ifc.get()
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_services(Panel):
|
|
bl_label = "Services"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "SERVICES") and tool.Ifc.get()
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_zones(Panel):
|
|
bl_label = "Zones"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "SERVICES") and tool.Ifc.get()
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_quality_control(Panel):
|
|
bl_label = "Quality Control"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "QUALITY")
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_clash_detection(Panel):
|
|
bl_label = "Clash Detection"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "QUALITY")
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_sandbox(Panel):
|
|
bl_label = "Sandbox"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
bl_options = {"DEFAULT_CLOSED"}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "QUALITY")
|
|
|
|
def draw(self, context):
|
|
row = self.layout.row()
|
|
row.label(text="More Experimental Than Usual", icon="ERROR")
|
|
|
|
|
|
# Object panel groups
|
|
class BIM_PT_tab_object_metadata(Panel):
|
|
bl_label = "Object Metadata"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
bl_order = 1
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "OBJECT") and tool.Ifc.get()
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_placement(Panel):
|
|
bl_label = "Placement"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
bl_order = 1
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "GEOMETRY") and tool.Ifc.get()
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_representations(Panel):
|
|
bl_label = "Representations"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
bl_order = 1
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "GEOMETRY") and tool.Ifc.get()
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_geometric_relationships(Panel):
|
|
bl_label = "Geometric Relationships"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
bl_order = 1
|
|
bl_options = {"DEFAULT_CLOSED"}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "GEOMETRY") and tool.Ifc.get()
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_parametric_geometry(Panel):
|
|
bl_label = "Parametric Geometry"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
bl_order = 1
|
|
bl_options = {"DEFAULT_CLOSED"}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "GEOMETRY") and tool.Ifc.get()
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_materials(Panel):
|
|
bl_label = "Materials"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
bl_order = 1
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "GEOMETRY") and tool.Ifc.get()
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_styles(Panel):
|
|
bl_label = "Styles"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
bl_order = 1
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "GEOMETRY") and tool.Ifc.get()
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_profiles(Panel):
|
|
bl_label = "Profiles"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
bl_order = 1
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "GEOMETRY") and tool.Ifc.get()
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_sheets(Panel):
|
|
bl_label = "Sheets"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
bl_order = 1
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "DRAWINGS") and tool.Ifc.get()
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_drawings(Panel):
|
|
bl_label = "Drawings"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
bl_order = 1
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "DRAWINGS") and tool.Ifc.get()
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_schedules(Panel):
|
|
bl_label = "Schedules"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
bl_order = 1
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "DRAWINGS") and tool.Ifc.get()
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_references(Panel):
|
|
bl_label = "References"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
bl_order = 1
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "DRAWINGS") and tool.Ifc.get()
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_misc(Panel):
|
|
bl_label = "Misc."
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
bl_order = 1
|
|
bl_options = {"DEFAULT_CLOSED"}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "OBJECT") and tool.Ifc.get()
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_handover(Panel):
|
|
bl_label = "Commissioning and Handover"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
bl_order = 1
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "FM")
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class BIM_PT_tab_operations(Panel):
|
|
bl_label = "Operations and Maintenance"
|
|
bl_space_type = "PROPERTIES"
|
|
bl_region_type = "WINDOW"
|
|
bl_context = "scene"
|
|
bl_order = 2
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
return tool.Blender.is_tab(context, "FM")
|
|
|
|
def draw(self, context):
|
|
pass
|
|
|
|
|
|
class UIData:
|
|
data = {}
|
|
is_loaded = False
|
|
|
|
@classmethod
|
|
def load(cls):
|
|
cls.data = {"version": cls.version()}
|
|
cls.is_loaded = True
|
|
|
|
@classmethod
|
|
def version(cls):
|
|
return tool.Blender.get_blenderbim_version()
|
|
|
|
|
|
def draw_statusbar(self, context):
|
|
if not UIData.is_loaded:
|
|
UIData.load()
|
|
text = f"BlenderBIM Add-on v{UIData.data['version']}"
|
|
self.layout.label(text=text)
|
|
|
|
|
|
def draw_custom_context_menu(self, context):
|
|
# https://blender.stackexchange.com/a/275555/86891
|
|
if (
|
|
not hasattr(context, "button_pointer")
|
|
or not hasattr(context, "button_prop")
|
|
or not hasattr(context.button_prop, "identifier")
|
|
):
|
|
return
|
|
prop = context.button_pointer
|
|
prop_name = context.button_prop.identifier
|
|
prop_value = getattr(prop, prop_name, None)
|
|
if prop_value is None:
|
|
return
|
|
version = tool.Ifc.get_schema()
|
|
layout = self.layout
|
|
|
|
if isinstance(context.button_pointer, Attribute):
|
|
description = getattr(context.button_pointer, "description", None)
|
|
ifc_class = getattr(context.button_pointer, "ifc_class", "")
|
|
if ifc_class:
|
|
try:
|
|
url = get_entity_doc(version, context.button_pointer.ifc_class).get("spec_url", "")
|
|
except RuntimeError: # It's not an Entity Attribute. Let's try a Property Set attribute.
|
|
doc = get_property_set_doc(version, context.button_pointer.ifc_class)
|
|
if doc:
|
|
url = doc.get("spec_url", "")
|
|
else: # It's a custom property set. No URL available
|
|
url = ""
|
|
if description:
|
|
layout.separator()
|
|
op_description = layout.operator("bim.show_description", text="IFC Description", icon="INFO")
|
|
op_description.attr_name = getattr(context.button_pointer, "name", "")
|
|
op_description.description = description
|
|
op_description.url = url
|
|
else:
|
|
# Ugly but we can't know which type of data is under the cursor so we test everything until it clicks
|
|
try:
|
|
docs = get_entity_doc(version, prop_value)
|
|
if docs is None:
|
|
raise RuntimeError
|
|
except (RuntimeError, AttributeError):
|
|
try:
|
|
docs = get_type_doc(version, prop_value)
|
|
if docs is None:
|
|
raise RuntimeError
|
|
except (RuntimeError, AttributeError):
|
|
try:
|
|
docs = get_property_set_doc(version, prop_value)
|
|
if docs is None:
|
|
raise RuntimeError
|
|
except (RuntimeError, AttributeError):
|
|
pass
|
|
if docs:
|
|
url = docs.get("spec_url", "")
|
|
if url:
|
|
layout.separator()
|
|
url_op = layout.operator("bim.open_webbrowser", icon="URL", text="Online IFC Documentation")
|
|
url_op.url = url
|