mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 17:58:20 +00:00
Construction Type Browser, 2nd iteration (#2334)
* Polish a bit the type browser UI * Remove SELECT from type browser * model and geometry module tests fixed * Polishing of type authoring layout redesign
This commit is contained in:
committed by
GitHub
parent
f9ef00fd45
commit
bbb3b62b8a
@@ -42,6 +42,7 @@ class AuthoringData:
|
||||
cls.props = bpy.context.scene.BIMModelProperties
|
||||
cls.load_ifc_classes()
|
||||
cls.load_relating_types()
|
||||
cls.load_relating_types_browser()
|
||||
cls.load_preview_constr_types()
|
||||
|
||||
@classmethod
|
||||
@@ -52,6 +53,10 @@ class AuthoringData:
|
||||
def load_relating_types(cls):
|
||||
cls.data["relating_types_ids"] = cls.relating_types()
|
||||
|
||||
@classmethod
|
||||
def load_relating_types_browser(cls):
|
||||
cls.data["relating_types_browser_ids"] = cls.relating_types_browser()
|
||||
|
||||
@classmethod
|
||||
def load_preview_constr_types(cls):
|
||||
cls.data["preview_constr_types"] = preview_icon_ids
|
||||
@@ -88,6 +93,10 @@ class AuthoringData:
|
||||
def relating_types(cls, ifc_class=None):
|
||||
return [(str(e.id()), e.Name, e.Description or "") for e in cls.constr_class_entities(ifc_class=ifc_class)]
|
||||
|
||||
@classmethod
|
||||
def relating_types_browser(cls):
|
||||
return cls.relating_types(ifc_class=cls.props.ifc_class_browser)
|
||||
|
||||
@staticmethod
|
||||
def new_relating_type_info(ifc_class):
|
||||
relating_type_info = bpy.context.scene.ConstrTypeInfo.add()
|
||||
@@ -137,9 +146,9 @@ class AuthoringData:
|
||||
col.objects.unlink(obj)
|
||||
|
||||
@classmethod
|
||||
def assetize_relating_type_from_selection(cls):
|
||||
ifc_class= cls.props.ifc_class
|
||||
relating_type_id = cls.props.relating_type_id
|
||||
def assetize_relating_type_from_selection(cls, browser=False):
|
||||
ifc_class = cls.props.ifc_class_browser if browser else cls.props.ifc_class
|
||||
relating_type_id = cls.props.relating_type_id_browser if browser else cls.props.relating_type_id
|
||||
constr_class_occurrences = cls.constr_class_entities(ifc_class=ifc_class)
|
||||
constr_class_occurrences = [
|
||||
entity for entity in constr_class_occurrences if entity.id() == int(relating_type_id)
|
||||
|
||||
@@ -193,13 +193,10 @@ class DisplayConstrTypes(bpy.types.Operator):
|
||||
if not AuthoringData.is_loaded:
|
||||
AuthoringData.load()
|
||||
props = context.scene.BIMModelProperties
|
||||
if props.unfold_relating_types:
|
||||
ifc_class = props.ifc_class
|
||||
relating_type_info = AuthoringData.relating_type_info(ifc_class)
|
||||
if relating_type_info is None or not relating_type_info.fully_loaded:
|
||||
AuthoringData.assetize_constr_class(ifc_class)
|
||||
else:
|
||||
prop.update_relating_type(props, context)
|
||||
ifc_class = props.ifc_class
|
||||
relating_type_info = AuthoringData.relating_type_info(ifc_class)
|
||||
if relating_type_info is None or not relating_type_info.fully_loaded:
|
||||
AuthoringData.assetize_constr_class(ifc_class)
|
||||
bpy.ops.bim.display_constr_types_ui("INVOKE_DEFAULT")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -33,16 +33,24 @@ def get_relating_type(self, context):
|
||||
return AuthoringData.data["relating_types_ids"]
|
||||
|
||||
|
||||
def update_icon_id(self, context):
|
||||
ifc_class = self.ifc_class
|
||||
relating_type_id = self.relating_type_id
|
||||
def get_relating_type_browser(self, context):
|
||||
if not AuthoringData.is_loaded:
|
||||
AuthoringData.load()
|
||||
return AuthoringData.data["relating_types_browser_ids"]
|
||||
|
||||
|
||||
def update_icon_id(self, context, browser=False):
|
||||
ifc_class = self.ifc_class_browser if browser else self.ifc_class
|
||||
relating_type_id = self.relating_type_id_browser if browser else self.relating_type_id
|
||||
relating_type = AuthoringData.relating_type_name_by_id(ifc_class, relating_type_id)
|
||||
if (
|
||||
ifc_class not in AuthoringData.data["preview_constr_types"]
|
||||
or relating_type_id not in AuthoringData.data["preview_constr_types"][ifc_class]
|
||||
) and relating_type is not None:
|
||||
if not AuthoringData.assetize_relating_type_from_selection():
|
||||
if not AuthoringData.assetize_relating_type_from_selection(browser=browser):
|
||||
return
|
||||
if ifc_class not in AuthoringData.data["preview_constr_types"]:
|
||||
pass
|
||||
self.icon_id = AuthoringData.data["preview_constr_types"][ifc_class][relating_type_id]["icon_id"]
|
||||
|
||||
|
||||
@@ -51,13 +59,17 @@ def update_ifc_class(self, context):
|
||||
AuthoringData.load_relating_types()
|
||||
if self.updating:
|
||||
return
|
||||
if self.unfold_relating_types:
|
||||
ifc_class = self.ifc_class
|
||||
relating_type_info = AuthoringData.relating_type_info(ifc_class)
|
||||
if relating_type_info is None or not relating_type_info.fully_loaded:
|
||||
AuthoringData.assetize_constr_class(ifc_class)
|
||||
else:
|
||||
self.relating_type_id = AuthoringData.data["relating_types_ids"][0][0]
|
||||
|
||||
|
||||
def update_ifc_class_browser(self, context):
|
||||
AuthoringData.load_ifc_classes()
|
||||
AuthoringData.load_relating_types_browser()
|
||||
if self.updating:
|
||||
return
|
||||
ifc_class = self.ifc_class_browser
|
||||
relating_type_info = AuthoringData.relating_type_info(ifc_class)
|
||||
if relating_type_info is None or not relating_type_info.fully_loaded:
|
||||
AuthoringData.assetize_constr_class(ifc_class)
|
||||
|
||||
|
||||
def update_relating_type(self, context):
|
||||
@@ -66,6 +78,12 @@ def update_relating_type(self, context):
|
||||
update_icon_id(self, context)
|
||||
|
||||
|
||||
def update_relating_type_browser(self, context):
|
||||
AuthoringData.load_relating_types_browser()
|
||||
if not self.updating:
|
||||
update_icon_id(self, context, browser=True)
|
||||
|
||||
|
||||
def update_relating_type_by_name(self, context):
|
||||
AuthoringData.load_relating_types()
|
||||
relating_type_id = AuthoringData.relating_type_id_by_name(self.ifc_class, self.relating_type)
|
||||
@@ -73,8 +91,8 @@ def update_relating_type_by_name(self, context):
|
||||
self.relating_type_id = relating_type_id
|
||||
|
||||
|
||||
def update_unfold_relating_type(self, context):
|
||||
if self.unfold_relating_types:
|
||||
def update_preview_multiple(self, context):
|
||||
if self.preview_multiple_constr_types:
|
||||
ifc_class = self.ifc_class
|
||||
relating_type_info = AuthoringData.relating_type_info(ifc_class)
|
||||
if relating_type_info is None or not relating_type_info.fully_loaded:
|
||||
@@ -85,12 +103,17 @@ def update_unfold_relating_type(self, context):
|
||||
|
||||
class BIMModelProperties(PropertyGroup):
|
||||
ifc_class: bpy.props.EnumProperty(items=get_ifc_class, name="Construction Class", update=update_ifc_class)
|
||||
ifc_class_browser: bpy.props.EnumProperty(items=get_ifc_class, name="Construction Class",
|
||||
update=update_ifc_class_browser)
|
||||
relating_type: bpy.props.StringProperty(update=update_relating_type_by_name)
|
||||
relating_type_id: bpy.props.EnumProperty(
|
||||
items=get_relating_type, name="Construction Type", update=update_relating_type
|
||||
)
|
||||
relating_type_id_browser: bpy.props.EnumProperty(
|
||||
items=get_relating_type_browser, name="Construction Type", update=update_relating_type_browser
|
||||
)
|
||||
icon_id: bpy.props.IntProperty()
|
||||
unfold_relating_types: bpy.props.BoolProperty(update=update_unfold_relating_type)
|
||||
preview_multiple_constr_types: bpy.props.BoolProperty(default=False, update=update_preview_multiple)
|
||||
updating: bpy.props.BoolProperty(default=False)
|
||||
occurrence_name_style: bpy.props.EnumProperty(
|
||||
items=[("CLASS", "By Class", ""), ("TYPE", "By Type", ""), ("CUSTOM", "Custom", "")],
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
# 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 math
|
||||
from bpy.types import Panel, Operator
|
||||
from blenderbim.bim.module.model.data import AuthoringData
|
||||
from blenderbim.bim.helper import prop_with_search, close_operator_panel
|
||||
@@ -54,34 +53,19 @@ class DisplayConstrTypesUI(Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_popup(self)
|
||||
return context.window_manager.invoke_popup(self, width=550)
|
||||
|
||||
def draw(self, context):
|
||||
props = context.scene.BIMModelProperties
|
||||
self.draw_header(props)
|
||||
if props.unfold_relating_types:
|
||||
self.draw_by_ifc_class(props)
|
||||
else:
|
||||
self.draw_by_ifc_class_and_type(props)
|
||||
self.draw_footer(props)
|
||||
|
||||
def draw_header(self, props):
|
||||
if AuthoringData.data["ifc_classes"]:
|
||||
row = self.layout.row()
|
||||
row.label(text="", icon="FILE_VOLUME")
|
||||
prop_with_search(row, props, "ifc_class", text="")
|
||||
|
||||
def draw_footer(self, props):
|
||||
row = self.layout.row()
|
||||
row.prop(data=props, property="unfold_relating_types", text="Show more")
|
||||
row.operator("bim.help_relating_types", text="", icon="QUESTION")
|
||||
|
||||
def draw_by_ifc_class(self, props):
|
||||
ifc_class = props.ifc_class
|
||||
prop_with_search(row, props, "ifc_class_browser", text="")
|
||||
ifc_class = props.ifc_class_browser
|
||||
num_cols = 3
|
||||
self.layout.row().separator(factor=0.25)
|
||||
flow = self.layout.grid_flow(row_major=True, columns=num_cols, even_columns=True, even_rows=True, align=True)
|
||||
relating_types = AuthoringData.relating_types()
|
||||
relating_types = AuthoringData.relating_types_browser()
|
||||
num_types = len(relating_types)
|
||||
for idx, (relating_type_id, name, desc) in enumerate(relating_types):
|
||||
outer_col = flow.column()
|
||||
@@ -102,27 +86,13 @@ class DisplayConstrTypesUI(Operator):
|
||||
op.ifc_class = ifc_class
|
||||
if relating_type_id.isnumeric():
|
||||
op.relating_type_id = int(relating_type_id)
|
||||
factor = 2 if idx + 1 < math.ceil(num_types / num_cols) else 1.5
|
||||
last_row_cols = num_types % num_cols
|
||||
if last_row_cols != 0:
|
||||
for _ in range(num_cols - last_row_cols):
|
||||
flow.column()
|
||||
|
||||
def draw_by_ifc_class_and_type(self, props):
|
||||
ifc_class = props.ifc_class
|
||||
relating_type_id = props.relating_type_id
|
||||
if AuthoringData.data["relating_types_ids"]:
|
||||
row = self.layout.row()
|
||||
row.label(text="", icon="FILE_3D")
|
||||
prop_with_search(row, props, "relating_type_id", text="")
|
||||
box = self.layout.box()
|
||||
box.template_icon(icon_value=props.icon_id, scale=7)
|
||||
row = self.layout.row()
|
||||
op = row.operator("bim.add_constr_type_instance", icon="ADD")
|
||||
op.from_invoke = True
|
||||
op.ifc_class = ifc_class
|
||||
if relating_type_id.isnumeric():
|
||||
op.relating_type_id = int(relating_type_id)
|
||||
row.alignment = "RIGHT"
|
||||
row.operator("bim.help_relating_types", text="", icon="QUESTION")
|
||||
|
||||
|
||||
class HelpConstrTypes(Operator):
|
||||
|
||||
@@ -23,6 +23,7 @@ from blenderbim.bim.helper import prop_with_search, close_operator_panel
|
||||
from bpy.types import WorkSpaceTool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.model.data import AuthoringData
|
||||
from blenderbim.bim.module.model import prop
|
||||
|
||||
|
||||
class BimTool(WorkSpaceTool):
|
||||
@@ -53,70 +54,80 @@ class BimTool(WorkSpaceTool):
|
||||
)
|
||||
|
||||
def draw_settings(context, layout, tool):
|
||||
if not AuthoringData.is_loaded and IfcStore.get_file():
|
||||
AuthoringData.load()
|
||||
|
||||
props = context.scene.BIMModelProperties
|
||||
is_tool_header = context.region.type == "TOOL_HEADER"
|
||||
is_sidebar = context.region.type == "UI"
|
||||
|
||||
row = layout.row(align=True)
|
||||
if not IfcStore.get_file():
|
||||
row.label(text="No IFC Project", icon="ERROR")
|
||||
return
|
||||
|
||||
ifc_classes = AuthoringData.data["ifc_classes"]
|
||||
relating_types_ids = AuthoringData.data["relating_types_ids"]
|
||||
if not AuthoringData.is_loaded:
|
||||
AuthoringData.load()
|
||||
|
||||
ifc_classes = AuthoringData.data[
|
||||
"ifc_classes"] if "ifc_classes" in AuthoringData.data else False
|
||||
relating_types_ids = AuthoringData.data[
|
||||
"relating_types_ids"] if "relating_types_ids" in AuthoringData.data else False
|
||||
|
||||
if ifc_classes and relating_types_ids and not props.icon_id:
|
||||
# hack Dion won't like to show a preview also on the first time the sidebar is shown
|
||||
bpy.app.timers.register(lambda: prop.update_relating_type(props, context))
|
||||
|
||||
ifc_class = props.ifc_class
|
||||
relating_type_id = props.relating_type_id
|
||||
relating_type = AuthoringData.relating_type_name_by_id(ifc_class, relating_type_id)
|
||||
|
||||
if is_tool_header:
|
||||
row = layout.row(align=True)
|
||||
if ifc_classes:
|
||||
row.label(text="", icon="FILE_VOLUME")
|
||||
row.label(text=ifc_class)
|
||||
row.prop(data=props, property="ifc_class", text="")
|
||||
else:
|
||||
row.label(text="No Construction Class", icon="FILE_VOLUME")
|
||||
row = layout.row(align=True)
|
||||
if relating_types_ids:
|
||||
row.label(text="", icon="FILE_3D")
|
||||
row.label(text=f"{relating_type} ")
|
||||
row.prop(data=props, property="relating_type_id", text="")
|
||||
else:
|
||||
row.label(text="No Construction Type", icon="FILE_3D")
|
||||
if ifc_classes:
|
||||
row = layout.row()
|
||||
row.operator("bim.display_constr_types", icon="TRIA_DOWN", text="")
|
||||
row.operator("bim.display_constr_types", icon="VIEW_ORTHO", text="")
|
||||
row = layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_A")
|
||||
row.label(text=f" Add")
|
||||
else:
|
||||
txt_ifc_class = ifc_class if ifc_classes else "No Construction Class"
|
||||
txt_relating_type = relating_type if relating_types_ids else "No Construction Type"
|
||||
row = layout.row(align=True)
|
||||
row.label(text=txt_ifc_class, icon="FILE_VOLUME")
|
||||
row = layout.row(align=True)
|
||||
row.label(text=txt_relating_type, icon="FILE_3D")
|
||||
|
||||
if AuthoringData.data["ifc_classes"]:
|
||||
row = layout.row()
|
||||
if ifc_classes:
|
||||
row.label(text="", icon="FILE_VOLUME")
|
||||
prop_with_search(row, props, "ifc_class", text="")
|
||||
else:
|
||||
row.label(text="No Construction Class", icon="FILE_VOLUME")
|
||||
row = layout.row(align=True)
|
||||
if relating_types_ids:
|
||||
row.label(text="", icon="FILE_3D")
|
||||
prop_with_search(row, props, "relating_type_id", text="")
|
||||
else:
|
||||
row.label(text="No Construction Type", icon="FILE_3D")
|
||||
if is_sidebar and ifc_classes and relating_types_ids:
|
||||
box = layout.box()
|
||||
box.template_icon(icon_value=props.icon_id, scale=8)
|
||||
row = layout.row()
|
||||
op = row.operator("bim.add_constr_type_instance", icon="ADD")
|
||||
op.from_invoke = True
|
||||
op.ifc_class = ifc_class
|
||||
if relating_type_id.isnumeric():
|
||||
op.relating_type_id = int(relating_type_id)
|
||||
|
||||
if ifc_classes:
|
||||
row = layout.row(align=True)
|
||||
row.label(text="", icon="EVENT_SHIFT")
|
||||
row.label(text="", icon="EVENT_A")
|
||||
row.label(text=f" Add Type Instance")
|
||||
|
||||
ifc_class = props.ifc_class
|
||||
relating_type_id = props.relating_type_id
|
||||
if AuthoringData.data["relating_types_ids"]:
|
||||
row = layout.row()
|
||||
row.label(text="", icon="FILE_3D")
|
||||
prop_with_search(row, props, "relating_type_id", text="")
|
||||
|
||||
box = layout.box()
|
||||
box.template_icon(icon_value=props.icon_id, scale=7)
|
||||
row = layout.row()
|
||||
op = row.operator("bim.add_constr_type_instance", icon="ADD")
|
||||
op.from_invoke = True
|
||||
op.ifc_class = ifc_class
|
||||
if relating_type_id.isnumeric():
|
||||
op.relating_type_id = int(relating_type_id)
|
||||
|
||||
if AuthoringData.data["ifc_classes"]:
|
||||
if ifc_classes:
|
||||
if ifc_class == "IfcWallType":
|
||||
row = layout.row()
|
||||
row.label(text="Join")
|
||||
|
||||
Reference in New Issue
Block a user