From 3f4656600e5b205ae5a9265c950384486452a805 Mon Sep 17 00:00:00 2001 From: Andrej Date: Mon, 9 Jun 2025 09:55:46 +0500 Subject: [PATCH] typing --- .../bonsai/bim/module/group/operator.py | 18 ++++---- src/bonsai/bonsai/bim/module/group/prop.py | 25 +++++++++-- src/bonsai/bonsai/bim/module/group/ui.py | 22 ++++++++-- src/bonsai/bonsai/bim/module/ifcgit/data.py | 4 +- .../bonsai/bim/module/ifcgit/operator.py | 22 +++++----- src/bonsai/bonsai/bim/module/ifcgit/prop.py | 41 +++++++++++++++---- src/bonsai/bonsai/bim/module/ifcgit/ui.py | 25 ++++++++--- .../bonsai/bim/module/light/operator.py | 1 + src/bonsai/bonsai/bim/module/pset/data.py | 10 +++-- src/bonsai/bonsai/bim/module/pset/ui.py | 4 +- src/bonsai/bonsai/core/aggregate.py | 28 ++++++------- src/bonsai/bonsai/core/ifcgit.py | 36 ++++++++-------- src/bonsai/bonsai/tool/blender.py | 11 ++++- src/bonsai/bonsai/tool/ifcgit.py | 33 +++++++++------ .../api/cost/assign_cost_item_quantity.py | 4 -- 15 files changed, 184 insertions(+), 100 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/group/operator.py b/src/bonsai/bonsai/bim/module/group/operator.py index 6932ec6f78..c82eb0d055 100644 --- a/src/bonsai/bonsai/bim/module/group/operator.py +++ b/src/bonsai/bonsai/bim/module/group/operator.py @@ -31,7 +31,7 @@ class LoadGroups(bpy.types.Operator, tool.Ifc.Operator): bl_options = {"REGISTER", "UNDO"} def _execute(self, context): - self.props = context.scene.BIMGroupProperties + self.props = tool.Blender.get_group_props() self.expanded_groups = json.loads(context.scene.ExpandedGroups.json_string) self.props.groups.clear() @@ -95,8 +95,9 @@ class DisableGroupEditingUI(bpy.types.Operator, tool.Ifc.Operator): bl_options = {"REGISTER", "UNDO"} def _execute(self, context): - context.scene.BIMGroupProperties.is_editing = False - context.scene.BIMGroupProperties.active_group_id = 0 + props = tool.Blender.get_group_props() + props.is_editing = False + props.active_group_id = 0 return {"FINISHED"} @@ -122,7 +123,7 @@ class EditGroup(bpy.types.Operator, tool.Ifc.Operator): bl_options = {"REGISTER", "UNDO"} def _execute(self, context): - props = context.scene.BIMGroupProperties + props = tool.Blender.get_group_props() attributes = bonsai.bim.helper.export_attributes(props.group_attributes) ifc_file = tool.Ifc.get() ifcopenshell.api.group.edit_group(ifc_file, group=ifc_file.by_id(props.active_group_id), attributes=attributes) @@ -150,7 +151,7 @@ class EnableEditingGroup(bpy.types.Operator, tool.Ifc.Operator): group: bpy.props.IntProperty() def _execute(self, context): - props = context.scene.BIMGroupProperties + props = tool.Blender.get_group_props() props.group_attributes.clear() bonsai.bim.helper.import_attributes2(tool.Ifc.get().by_id(self.group), props.group_attributes) props.active_group_id = self.group @@ -163,7 +164,8 @@ class DisableEditingGroup(bpy.types.Operator, tool.Ifc.Operator): bl_options = {"REGISTER", "UNDO"} def _execute(self, context): - context.scene.BIMGroupProperties.active_group_id = 0 + props = tool.Blender.get_group_props() + props.active_group_id = 0 return {"FINISHED"} @@ -183,9 +185,9 @@ class AssignGroup(bpy.types.Operator, tool.Ifc.Operator): if not self.is_assigning: return bpy.ops.bim.unassign_group(group=self.group) products = [ - tool.Ifc.get_entity(o) + element for o in tool.Blender.get_selected_objects(include_active=False) - if tool.Ifc.get_entity(o) + if (element := tool.Ifc.get_entity(o)) ] ifcopenshell.api.group.assign_group(tool.Ifc.get(), products=products, group=tool.Ifc.get().by_id(self.group)) diff --git a/src/bonsai/bonsai/bim/module/group/prop.py b/src/bonsai/bonsai/bim/module/group/prop.py index 2a091e21ae..af6b60ddd8 100644 --- a/src/bonsai/bonsai/bim/module/group/prop.py +++ b/src/bonsai/bonsai/bim/module/group/prop.py @@ -17,10 +17,10 @@ # along with Bonsai. If not, see . import bpy +import bonsai.tool as tool from bonsai.bim.prop import StrProperty, Attribute from bonsai.bim.module.pset.data import refresh as refresh_pset from bpy.types import PropertyGroup -import json from bpy.props import ( PointerProperty, StringProperty, @@ -31,6 +31,7 @@ from bpy.props import ( FloatVectorProperty, CollectionProperty, ) +from typing import TYPE_CHECKING, Union def update_active_group_index(self, context): @@ -40,6 +41,9 @@ def update_active_group_index(self, context): class ExpandedGroups(StrProperty): json_string: StringProperty(name="JSON String", default="[]") + if TYPE_CHECKING: + json_string: str + class Group(PropertyGroup): name: StringProperty(name="Name") @@ -48,6 +52,13 @@ class Group(PropertyGroup): has_children: BoolProperty(name="Has Children", default=False) tree_depth: IntProperty(name="Tree Depth") + if TYPE_CHECKING: + name: str + ifc_definition_id: int + is_expanded: bool + has_children: bool + tree_depth: int + class BIMGroupProperties(PropertyGroup): group_attributes: CollectionProperty(name="Group Attributes", type=Attribute) @@ -56,7 +67,13 @@ class BIMGroupProperties(PropertyGroup): active_group_index: IntProperty(name="Active Group Index", update=update_active_group_index) active_group_id: IntProperty(name="Active Group Id") + if TYPE_CHECKING: + group_attributes: bpy.types.bpy_prop_collection_idprop[Attribute] + is_editing: bool + groups: bpy.types.bpy_prop_collection_idprop[Group] + active_group_index: int + active_group_id: int + @property - def active_group(self): - if self.active_group_index < len(self.groups): - return self.groups[self.active_group_index] + def active_group(self) -> Union[Group, None]: + return tool.Blender.get_active_uilist_element(self.groups, self.active_group_index) diff --git a/src/bonsai/bonsai/bim/module/group/ui.py b/src/bonsai/bonsai/bim/module/group/ui.py index 309c49d5d0..e433549f34 100644 --- a/src/bonsai/bonsai/bim/module/group/ui.py +++ b/src/bonsai/bonsai/bim/module/group/ui.py @@ -16,11 +16,16 @@ # You should have received a copy of the GNU General Public License # along with Bonsai. If not, see . +from __future__ import annotations import bpy import bonsai.tool as tool from bpy.types import Panel, UIList from bonsai.bim.helper import draw_attributes from bonsai.bim.module.group.data import GroupsData, ObjectGroupsData +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from bonsai.bim.module.group.prop import BIMGroupProperties, Group class BIM_PT_groups(Panel): @@ -39,7 +44,7 @@ class BIM_PT_groups(Panel): def draw(self, context): if not GroupsData.is_loaded: GroupsData.load() - self.props = context.scene.BIMGroupProperties + self.props = tool.Blender.get_group_props() row = self.layout.row(align=True) row.label(text=f"{GroupsData.data['total_groups']} Groups Found", icon="OUTLINER") @@ -99,7 +104,8 @@ class BIM_PT_object_groups(Panel): def draw(self, context): if not ObjectGroupsData.is_loaded: ObjectGroupsData.load() - self.props = context.scene.BIMGroupProperties + assert self.layout + self.props = tool.Blender.get_group_props() for group in ObjectGroupsData.data["groups"]: row = self.layout.row(align=True) @@ -113,7 +119,17 @@ class BIM_PT_object_groups(Panel): class BIM_UL_groups(UIList): - def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + def draw_item( + self, + context: bpy.types.Context, + layout: bpy.types.UILayout, + data: BIMGroupProperties, + item: Group, + icon, + active_data, + active_propname, + index: int, + ): if item: row = layout.row(align=True) for i in range(0, item.tree_depth): diff --git a/src/bonsai/bonsai/bim/module/ifcgit/data.py b/src/bonsai/bonsai/bim/module/ifcgit/data.py index 2df65540ab..18ffe0e55c 100644 --- a/src/bonsai/bonsai/bim/module/ifcgit/data.py +++ b/src/bonsai/bonsai/bim/module/ifcgit/data.py @@ -153,7 +153,7 @@ class IfcGitData: @classmethod def commit(cls): - props = bpy.context.scene.IfcGitProperties + props = tool.IfcGit.get_ifcgit_props() if cls.repo() and len(props.ifcgit_commits) > 0: item = props.ifcgit_commits[props.commit_index] try: @@ -163,7 +163,7 @@ class IfcGitData: @classmethod def current_revision(cls): - props = bpy.context.scene.IfcGitProperties + props = tool.IfcGit.get_ifcgit_props() if cls.repo() and cls.repo().head.is_valid() and len(props.ifcgit_commits) > 0: return tool.IfcGitRepo.repo.commit() diff --git a/src/bonsai/bonsai/bim/module/ifcgit/operator.py b/src/bonsai/bonsai/bim/module/ifcgit/operator.py index 4b71a33fa1..69cc239261 100644 --- a/src/bonsai/bonsai/bim/module/ifcgit/operator.py +++ b/src/bonsai/bonsai/bim/module/ifcgit/operator.py @@ -68,7 +68,7 @@ class CloneRepo(bpy.types.Operator): @classmethod def poll(cls, context): - props = context.scene.IfcGitProperties + props = tool.IfcGit.get_ifcgit_props() if ( props.remote_url and props.local_folder @@ -80,7 +80,7 @@ class CloneRepo(bpy.types.Operator): def execute(self, context): - props = context.scene.IfcGitProperties + props = tool.IfcGit.get_ifcgit_props() core.clone_repo(tool.IfcGit, props.remote_url, props.local_folder, self) props.remote_url = "" refresh() @@ -112,7 +112,7 @@ class CommitChanges(bpy.types.Operator): @classmethod def poll(cls, context): IfcGitData.make_sure_is_loaded() - props = context.scene.IfcGitProperties + props = tool.IfcGit.get_ifcgit_props() repo = IfcGitData.data["repo"] if props.commit_message == "": return False @@ -148,7 +148,7 @@ class AddTag(bpy.types.Operator): @classmethod def poll(cls, context): IfcGitData.make_sure_is_loaded() - props = context.scene.IfcGitProperties + props = tool.IfcGit.get_ifcgit_props() if props.new_tag_name == "": return False repo = IfcGitData.data["repo"] @@ -218,7 +218,7 @@ class DisplayRevision(bpy.types.Operator): @classmethod def poll(cls, context): - props = context.scene.IfcGitProperties + props = tool.IfcGit.get_ifcgit_props() if props.ifcgit_commits: return True @@ -253,7 +253,7 @@ class SwitchRevision(bpy.types.Operator): @classmethod def poll(cls, context): - props = context.scene.IfcGitProperties + props = tool.IfcGit.get_ifcgit_props() if props.ifcgit_commits: return True @@ -274,7 +274,7 @@ class Merge(bpy.types.Operator): @classmethod def poll(cls, context): IfcGitData.make_sure_is_loaded() - props = context.scene.IfcGitProperties + props = tool.IfcGit.get_ifcgit_props() if IfcGitData.data["ifcmerge_exe"] and props.ifcgit_commits and not IfcGitData.data["is_detached"]: return True return False @@ -296,8 +296,7 @@ class Push(bpy.types.Operator): bl_options = {"REGISTER"} def execute(self, context): - - props = context.scene.IfcGitProperties + props = tool.IfcGit.get_ifcgit_props() repo = IfcGitData.data["repo"] core.push(tool.IfcGit, repo, props.select_remote, self) return {"FINISHED"} @@ -311,8 +310,7 @@ class Fetch(bpy.types.Operator): bl_options = {"REGISTER"} def execute(self, context): - - props = context.scene.IfcGitProperties + props = tool.IfcGit.get_ifcgit_props() repo = IfcGitData.data["repo"] remote = repo.remotes[props.select_remote] remote.fetch() @@ -329,7 +327,7 @@ class AddRemote(bpy.types.Operator): @classmethod def poll(cls, context): IfcGitData.make_sure_is_loaded() - props = context.scene.IfcGitProperties + props = tool.IfcGit.get_ifcgit_props() repo = IfcGitData.data["repo"] if ( not repo diff --git a/src/bonsai/bonsai/bim/module/ifcgit/prop.py b/src/bonsai/bonsai/bim/module/ifcgit/prop.py index 128256957d..128cfcd44b 100644 --- a/src/bonsai/bonsai/bim/module/ifcgit/prop.py +++ b/src/bonsai/bonsai/bim/module/ifcgit/prop.py @@ -1,4 +1,5 @@ import bpy +import bonsai.tool as tool from bpy.types import PropertyGroup from bpy.props import ( StringProperty, @@ -8,11 +9,10 @@ from bpy.props import ( EnumProperty, ) from bonsai.bim.module.ifcgit.data import IfcGitData +from typing import TYPE_CHECKING, Literal -def git_branches(self, context): - """branches enum""" - +def git_branches(self: "IfcGitProperties", context: bpy.types.Context) -> tool.Blender.BLENDER_ENUM_ITEMS: # NOTE "Python must keep a reference to the strings returned by # the callback or Blender will misbehave or even crash" IfcGitData.data["branch_names"] = sorted([branch.name for branch in IfcGitData.data["repo"].heads]) @@ -29,9 +29,7 @@ def git_branches(self, context): return [(myname, myname, myname) for myname in IfcGitData.data["branch_names"]] -def git_remotes(self, context): - """remotes enum""" - +def git_remotes(self: "IfcGitProperties", context: bpy.types.Context) -> tool.Blender.BLENDER_ENUM_ITEMS: IfcGitData.data["remote_names"] = sorted([remote.name for remote in IfcGitData.data["remotes"]]) if "origin" in IfcGitData.data["remote_names"]: @@ -41,12 +39,11 @@ def git_remotes(self, context): return [(myname, myname, myname) for myname in IfcGitData.data["remote_names"]] -def update_revlist(self, context): +def update_revlist(self: "IfcGitProperties", context: bpy.types.Context) -> None: """wrapper to trigger update of the revision list""" bpy.ops.ifcgit.refresh() - props = context.scene.IfcGitProperties - props.commit_index = 0 + self.commit_index = 0 class IfcGitTag(PropertyGroup): @@ -61,6 +58,10 @@ class IfcGitTag(PropertyGroup): default="", ) + if TYPE_CHECKING: + name: str + message: str + class IfcGitListItem(PropertyGroup): """Group of properties representing an item in the list.""" @@ -89,6 +90,14 @@ class IfcGitListItem(PropertyGroup): ) tags: CollectionProperty(type=IfcGitTag, name="List of revision tags") + if TYPE_CHECKING: + hexsha: str + relevant: bool + author_name: str + author_email: str + message: str + tags: bpy.types.bpy_prop_collection_idprop[IfcGitTag] + class IfcGitProperties(PropertyGroup): @@ -140,3 +149,17 @@ class IfcGitProperties(PropertyGroup): ], update=update_revlist, ) + + if TYPE_CHECKING: + ifcgit_commits: bpy.types.bpy_prop_collection_idprop[IfcGitListItem] + commit_index: int + commit_message: str + new_branch_name: str + new_tag_name: str + new_tag_message: str + remote_name: str + remote_url: str + local_folder: str + display_branch: str + select_remote: str + ifcgit_filter: Literal["all", "tagged", "relevant"] diff --git a/src/bonsai/bonsai/bim/module/ifcgit/ui.py b/src/bonsai/bonsai/bim/module/ifcgit/ui.py index 760d719a60..631f33c7e1 100644 --- a/src/bonsai/bonsai/bim/module/ifcgit/ui.py +++ b/src/bonsai/bonsai/bim/module/ifcgit/ui.py @@ -1,10 +1,16 @@ +from __future__ import annotations import bpy import time import os import platform +import bonsai.tool as tool +from typing import TYPE_CHECKING from bonsai.bim.module.ifcgit.data import IfcGitData +if TYPE_CHECKING: + from bonsai.bim.module.ifcgit.prop import IfcGitProperties, IfcGitListItem + class IFCGIT_PT_panel(bpy.types.Panel): """Scene Properties panel to interact with IFC repository data""" @@ -35,7 +41,7 @@ class IFCGIT_PT_panel(bpy.types.Panel): ) return - props = context.scene.IfcGitProperties + props = tool.IfcGit.get_ifcgit_props() # TODO if file isn't saved, offer to save to disk @@ -195,9 +201,17 @@ class IFCGIT_PT_panel(bpy.types.Panel): class COMMIT_UL_List(bpy.types.UIList): """List of Git commits""" - def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): - - props = context.scene.IfcGitProperties + def draw_item( + self, + context: bpy.types.UILayout, + layout: bpy.types.UILayout, + data: IfcGitProperties, + item: IfcGitListItem, + icon, + active_data, + active_propname, + index: int, + ): current_revision = IfcGitData.data["current_revision"] @@ -212,7 +226,7 @@ class COMMIT_UL_List(bpy.types.UIList): refs = "" if item.hexsha in lookup: for branch in lookup[item.hexsha]: - if branch.name == props.display_branch: + if branch.name == data.display_branch: refs = "[" + branch.name + "] " lookup = IfcGitData.data["tags_by_hexsha"] @@ -272,6 +286,7 @@ class IFCGIT_PT_revision_inspector(bpy.types.Panel): IfcGitData.load() layout = self.layout + assert layout if not IfcGitData.data["git_exe"]: row = layout.row() diff --git a/src/bonsai/bonsai/bim/module/light/operator.py b/src/bonsai/bonsai/bim/module/light/operator.py index 2275f7bc55..bf709b0222 100644 --- a/src/bonsai/bonsai/bim/module/light/operator.py +++ b/src/bonsai/bonsai/bim/module/light/operator.py @@ -566,6 +566,7 @@ class RefreshIFCMaterials(bpy.types.Operator): def execute(self, context): props = context.scene.radiance_exporter_properties + ifc_file: ifcopenshell.file ifc_file = tool.Ifc.get() if props.should_load_from_memory else ifcopenshell.open(props.ifc_file) props.materials.clear() diff --git a/src/bonsai/bonsai/bim/module/pset/data.py b/src/bonsai/bonsai/bim/module/pset/data.py index 09d63b41a5..f139de5f9e 100644 --- a/src/bonsai/bonsai/bim/module/pset/data.py +++ b/src/bonsai/bonsai/bim/module/pset/data.py @@ -266,8 +266,9 @@ class GroupQtosData(Data): @classmethod def load(cls): - props = bpy.context.scene.BIMGroupProperties - ifc_definition_id = props.groups[props.active_group_index].ifc_definition_id + props = tool.Blender.get_group_props() + assert (active_group := props.active_group) + ifc_definition_id = active_group.ifc_definition_id cls.data = {"qtos": cls.psetqtos(tool.Ifc.get_entity_by_id(ifc_definition_id), qtos_only=True)} cls.is_loaded = True @@ -278,8 +279,9 @@ class GroupPsetData(Data): @classmethod def load(cls): - props = bpy.context.scene.BIMGroupProperties - ifc_definition_id = props.groups[props.active_group_index].ifc_definition_id + props = tool.Blender.get_group_props() + assert (active_group := props.active_group) + ifc_definition_id = active_group.ifc_definition_id cls.data = {"psets": cls.psetqtos(tool.Ifc.get_entity_by_id(ifc_definition_id), psets_only=True)} cls.is_loaded = True diff --git a/src/bonsai/bonsai/bim/module/pset/ui.py b/src/bonsai/bonsai/bim/module/pset/ui.py index 071d330b94..53e0ea45e9 100644 --- a/src/bonsai/bonsai/bim/module/pset/ui.py +++ b/src/bonsai/bonsai/bim/module/pset/ui.py @@ -662,7 +662,7 @@ class BIM_PT_group_qtos(Panel): @classmethod def poll(cls, context): - props = context.scene.BIMGroupProperties + props = tool.Blender.get_group_props() total_resources = len(props.groups) if total_resources > 0 and props.active_group_index < total_resources: return True @@ -696,7 +696,7 @@ class BIM_PT_group_psets(Panel): @classmethod def poll(cls, context): - props = context.scene.BIMGroupProperties + props = tool.Blender.get_group_props() total_resources = len(props.groups) if total_resources > 0 and props.active_group_index < total_resources: return True diff --git a/src/bonsai/bonsai/core/aggregate.py b/src/bonsai/bonsai/core/aggregate.py index b50f91fa6b..a1f2723338 100644 --- a/src/bonsai/bonsai/core/aggregate.py +++ b/src/bonsai/bonsai/core/aggregate.py @@ -25,18 +25,18 @@ if TYPE_CHECKING: import bonsai.tool as tool -def enable_editing_aggregate(aggregator: tool.Aggregate, obj: bpy.types.Object) -> None: +def enable_editing_aggregate(aggregator: type[tool.Aggregate], obj: bpy.types.Object) -> None: aggregator.enable_editing(obj) -def disable_editing_aggregate(aggregator: tool.Aggregate, obj: bpy.types.Object) -> None: +def disable_editing_aggregate(aggregator: type[tool.Aggregate], obj: bpy.types.Object) -> None: aggregator.disable_editing(obj) def assign_object( - ifc: tool.Ifc, - aggregator: tool.Aggregate, - collector: tool.Collector, + ifc: type[tool.Ifc], + aggregator: type[tool.Aggregate], + collector: type[tool.Collector], relating_obj: Optional[bpy.types.Object] = None, related_obj: Optional[bpy.types.Object] = None, ) -> Union[ifcopenshell.entity_instance, None]: @@ -53,9 +53,9 @@ def assign_object( def unassign_object( - ifc: tool.Ifc, - aggregate: tool.Aggregate, - collector: tool.Collector, + ifc: type[tool.Ifc], + aggregate: type[tool.Aggregate], + collector: type[tool.Collector], relating_obj: Optional[bpy.types.Object] = None, related_obj: Optional[bpy.types.Object] = None, ) -> None: @@ -74,10 +74,10 @@ def unassign_object( def add_part_to_object( - ifc: tool.Ifc, - aggregator: tool.Aggregate, - collector: tool.Collector, - blender: tool.Blender, + ifc: type[tool.Ifc], + aggregator: type[tool.Aggregate], + collector: type[tool.Collector], + blender: type[tool.Blender], obj: bpy.types.Object, part_class: str, part_name: Optional[str] = None, @@ -88,7 +88,7 @@ def add_part_to_object( def enter_aggregate_mode( - aggregator: tool.Aggregate, + aggregator: type[tool.Aggregate], obj: bpy.types.Object, ): aggregator.update_previous_aggregate_mode_state() @@ -98,7 +98,7 @@ def enter_aggregate_mode( aggregator.enable_aggregate_mode(obj) -def exit_aggregate_mode(aggregator: tool.Aggregate): +def exit_aggregate_mode(aggregator: type[tool.Aggregate]): aggregator.update_previous_aggregate_mode_state() if new_obj := aggregator.get_higher_aggregate(): aggregator.disable_aggregate_mode() diff --git a/src/bonsai/bonsai/core/ifcgit.py b/src/bonsai/bonsai/core/ifcgit.py index 6d37a5c5c9..bbb7efa99f 100644 --- a/src/bonsai/bonsai/core/ifcgit.py +++ b/src/bonsai/bonsai/core/ifcgit.py @@ -27,19 +27,19 @@ if TYPE_CHECKING: import git -def create_repo(ifcgit: tool.IfcGit, ifc: tool.Ifc) -> None: +def create_repo(ifcgit: type[tool.IfcGit], ifc: type[tool.Ifc]) -> None: path_ifc = ifc.get_path() path_dir = ifcgit.get_path_dir(path_ifc) ifcgit.init_repo(path_dir) -def add_file(ifcgit: tool.IfcGit, ifc: tool.Ifc) -> None: +def add_file(ifcgit: type[tool.IfcGit], ifc: type[tool.Ifc]) -> None: path_ifc = ifc.get_path() repo = ifcgit.repo_from_path(path_ifc) ifcgit.add_file_to_repo(repo, path_ifc) -def clone_repo(ifcgit: tool.IfcGit, remote_url: str, local_folder: str, operator: bpy.types.Operator) -> None: +def clone_repo(ifcgit: type[tool.IfcGit], remote_url: str, local_folder: str, operator: bpy.types.Operator) -> None: repo = ifcgit.clone_repo(remote_url, local_folder) if not repo: operator.report({"ERROR"}, "Clone failed") @@ -48,14 +48,14 @@ def clone_repo(ifcgit: tool.IfcGit, remote_url: str, local_folder: str, operator ifcgit.load_anyifc(repo) -def discard_uncommitted(ifcgit: tool.IfcGit, ifc: tool.Ifc) -> None: +def discard_uncommitted(ifcgit: type[tool.IfcGit], ifc: type[tool.Ifc]) -> None: path_ifc = ifc.get_path() # NOTE this is calling the git binary in a subprocess ifcgit.git_checkout(path_ifc) ifcgit.load_project(path_ifc) -def commit_changes(ifcgit: tool.IfcGit, ifc: tool.Ifc, repo: git.Repo) -> None: +def commit_changes(ifcgit: type[tool.IfcGit], ifc: type[tool.Ifc], repo: git.Repo) -> None: """Commit and create new branches as required""" path_ifc = ifc.get_path() @@ -67,34 +67,34 @@ def commit_changes(ifcgit: tool.IfcGit, ifc: tool.Ifc, repo: git.Repo) -> None: ifcgit.git_commit(path_ifc) -def add_tag(ifcgit: tool.IfcGit, repo: git.Repo) -> None: +def add_tag(ifcgit: type[tool.IfcGit], repo: git.Repo) -> None: ifcgit.add_tag(repo) -def delete_tag(ifcgit: tool.IfcGit, repo: git.Repo, tag_name: git.TagReference) -> None: +def delete_tag(ifcgit: type[tool.IfcGit], repo: git.Repo, tag_name: git.TagReference) -> None: ifcgit.delete_tag(repo, tag_name) -def add_remote(ifcgit: tool.IfcGit, repo: git.Repo) -> None: +def add_remote(ifcgit: type[tool.IfcGit], repo: git.Repo) -> None: ifcgit.add_remote(repo) -def delete_remote(ifcgit: tool.IfcGit, repo: git.Repo) -> None: +def delete_remote(ifcgit: type[tool.IfcGit], repo: git.Repo) -> None: ifcgit.delete_remote(repo) -def push(ifcgit: tool.IfcGit, repo: git.Repo, remote_name: str, operator: bpy.types.Operator) -> None: +def push(ifcgit: type[tool.IfcGit], repo: git.Repo, remote_name: str, operator: bpy.types.Operator) -> None: error_message = ifcgit.push(repo, remote_name, repo.active_branch.name) if error_message: operator.report({"ERROR"}, error_message) -def refresh_revision_list(ifcgit: tool.IfcGit, repo: git.Repo, ifc: tool.Ifc) -> None: +def refresh_revision_list(ifcgit: type[tool.IfcGit], repo: git.Repo, ifc: type[tool.Ifc]) -> None: if repo.heads: ifcgit.refresh_revision_list(ifc.get_path()) -def colourise_revision(ifcgit: tool.IfcGit) -> None: +def colourise_revision(ifcgit: type[tool.IfcGit]) -> None: step_ids = ifcgit.get_revisions_step_ids() if not step_ids: @@ -104,7 +104,7 @@ def colourise_revision(ifcgit: tool.IfcGit) -> None: ifcgit.colourise(final_step_ids) -def colourise_uncommitted(ifcgit: tool.IfcGit, ifc: tool.Ifc, repo: git.Repo) -> None: +def colourise_uncommitted(ifcgit: type[tool.IfcGit], ifc: type[tool.Ifc], repo: git.Repo) -> None: path_ifc = ifc.get_path() step_ids = ifcgit.ifc_diff_ids(repo, None, "HEAD", path_ifc) if not step_ids: @@ -114,7 +114,7 @@ def colourise_uncommitted(ifcgit: tool.IfcGit, ifc: tool.Ifc, repo: git.Repo) -> ifcgit.colourise(final_step_ids) -def switch_revision(ifcgit: tool.IfcGit, ifc: tool.Ifc) -> None: +def switch_revision(ifcgit: type[tool.IfcGit], ifc: type[tool.Ifc]) -> None: # FIXME bad things happen when switching to a revision that predates current project path_ifc = ifc.get_path() @@ -124,25 +124,25 @@ def switch_revision(ifcgit: tool.IfcGit, ifc: tool.Ifc) -> None: ifcgit.decolourise() -def merge_branch(ifcgit: tool.IfcGit, ifc: tool.Ifc, operator: bpy.types.Operator) -> None: +def merge_branch(ifcgit: type[tool.IfcGit], ifc: type[tool.Ifc], operator: bpy.types.Operator) -> None: path_ifc = ifc.get_path() ifcgit.config_ifcmerge() ifcgit.execute_merge(path_ifc, operator) -def entity_log(ifcgit: tool.IfcGit, ifc: tool.Ifc, step_id: int, operator: bpy.types.Operator) -> None: +def entity_log(ifcgit: type[tool.IfcGit], ifc: type[tool.Ifc], step_id: int, operator: bpy.types.Operator) -> None: path_ifc = ifc.get_path() log_text = ifcgit.entity_log(path_ifc, step_id) # ERROR is only way to display a multi-line message operator.report({"ERROR"}, log_text) -def install_git(ifcgit: tool.IfcGit, operator: bpy.types.Operator) -> None: +def install_git(ifcgit: type[tool.IfcGit], operator: bpy.types.Operator) -> None: if platform.system() == "Windows": ifcgit.install_git_windows(operator=operator) else: print("install_git() not implemented") -def run_git_diff(ifcgit: tool.IfcGit, operator: bpy.types.Operator) -> None: +def run_git_diff(ifcgit: type[tool.IfcGit], operator: bpy.types.Operator) -> None: ifcgit.run_git_diff(operator) diff --git a/src/bonsai/bonsai/tool/blender.py b/src/bonsai/bonsai/tool/blender.py index f9785c06a5..022ef95b96 100644 --- a/src/bonsai/bonsai/tool/blender.py +++ b/src/bonsai/bonsai/tool/blender.py @@ -52,6 +52,7 @@ if TYPE_CHECKING: from bonsai.bim.module.csv.prop import CsvProperties from bonsai.bim.module.constraint.prop import BIMConstraintProperties, BIMObjectConstraintProperties from bonsai.bim.module.diff.prop import DiffProperties + from bonsai.bim.module.group.prop import BIMGroupProperties T = TypeVar("T") @@ -244,8 +245,9 @@ class Blender(bonsai.core.tool.Blender): wsprops = tool.Sequence.get_work_schedule_props() return wsprops.active_work_schedule_id elif obj_type == "Group": - prop = context.scene.BIMGroupProperties - return prop.groups[prop.active_group_index].ifc_definition_id + props = tool.Blender.get_group_props() + assert (active_group := props.active_group) + return active_group.ifc_definition_id assert_never(obj_type) @classmethod @@ -1664,6 +1666,11 @@ class Blender(bonsai.core.tool.Blender): assert (scene := bpy.context.scene) return scene.DiffProperties + @classmethod + def get_group_props(cls) -> BIMGroupProperties: + assert (scene := bpy.context.scene) + return scene.BIMGroupProperties + @classmethod def get_bim_props(cls, scene: Optional[bpy.types.Scene] = None) -> BIMProperties: if scene is None: diff --git a/src/bonsai/bonsai/tool/ifcgit.py b/src/bonsai/bonsai/tool/ifcgit.py index 6cf4760c4d..30e2712a7a 100644 --- a/src/bonsai/bonsai/tool/ifcgit.py +++ b/src/bonsai/bonsai/tool/ifcgit.py @@ -25,7 +25,7 @@ import logging from bonsai.bim import import_ifc from bonsai.bim.ifc import IfcStore import bonsai.tool as tool -from typing import TYPE_CHECKING, Union +from typing import TYPE_CHECKING, Union, Literal, Any # allows git import even if git executable isn't found os.environ["GIT_PYTHON_REFRESH"] = "quiet" @@ -37,10 +37,17 @@ except ImportError: if TYPE_CHECKING: import git + from bonsai.bim.module.ifcgit.prop import IfcGitProperties + class IfcGit: STEP_IDS = dict[str, set[int]] + @classmethod + def get_ifcgit_props(cls) -> IfcGitProperties: + assert (scene := bpy.context.scene) + return scene.IfcGitProperties + @classmethod def init_repo(cls, path_dir: str) -> None: IfcGitRepo.repo = git.Repo.init(path_dir) @@ -118,7 +125,7 @@ class IfcGit: @classmethod def checkout_new_branch(cls, path_file: str) -> None: """Create a branch and move uncommitted changes to this branch""" - props = bpy.context.scene.IfcGitProperties + props = cls.get_ifcgit_props() if props.new_branch_name: IfcGitRepo.repo.git.checkout(b=props.new_branch_name) props.display_branch = props.new_branch_name @@ -127,7 +134,7 @@ class IfcGit: @classmethod def git_commit(cls, path_file: str) -> None: - props = bpy.context.scene.IfcGitProperties + props = cls.get_ifcgit_props() repo = IfcGitRepo.repo if os.name == "nt": cls.dos2unix(path_file) @@ -137,7 +144,7 @@ class IfcGit: @classmethod def add_tag(cls, repo: git.Repo) -> None: - props = bpy.context.scene.IfcGitProperties + props = cls.get_ifcgit_props() item = props.ifcgit_commits[props.commit_index] repo.create_tag(props.new_tag_name, ref=item.hexsha, message=props.new_tag_message) props.new_tag_name = "" @@ -150,14 +157,14 @@ class IfcGit: @classmethod def add_remote(cls, repo: git.Repo) -> None: - props = bpy.context.scene.IfcGitProperties + props = cls.get_ifcgit_props() repo.create_remote(name=props.remote_name, url=props.remote_url) props.remote_name = "" props.remote_url = "" @classmethod def delete_remote(cls, repo: git.Repo) -> None: - props = bpy.context.scene.IfcGitProperties + props = cls.get_ifcgit_props() remote_name = props.select_remote if remote_name in repo.remotes: repo.delete_remote(remote_name) @@ -176,7 +183,7 @@ class IfcGit: @classmethod def create_new_branch(cls) -> None: """Convert a detached HEAD into a branch""" - props = bpy.context.scene.IfcGitProperties + props = cls.get_ifcgit_props() repo = IfcGitRepo.repo new_branch = repo.create_head(props.new_branch_name) new_branch.checkout() @@ -187,7 +194,7 @@ class IfcGit: @classmethod def clear_commits_list(cls) -> None: - props = bpy.context.scene.IfcGitProperties + props = cls.get_ifcgit_props() # ifcgit_commits is registered list widget props.ifcgit_commits.clear() @@ -195,7 +202,7 @@ class IfcGit: @classmethod def get_commits_list(cls, path_ifc: str, lookup: dict[str, Any]) -> None: - props = bpy.context.scene.IfcGitProperties + props = props = cls.get_ifcgit_props() repo = cls.repo_from_path(path_ifc) commits = list( git.objects.commit.Commit.iter_items( @@ -342,7 +349,7 @@ class IfcGit: def get_revisions_step_ids(cls) -> Union[STEP_IDS, None]: props = tool.Blender.get_bim_props() path_ifc = tool.Blender.get_bim_props().ifc_file - props = bpy.context.scene.IfcGitProperties + props = cls.get_ifcgit_props() repo = IfcGitRepo.repo item = props.ifcgit_commits[props.commit_index] @@ -431,7 +438,7 @@ class IfcGit: @classmethod def switch_to_revision_item(cls) -> None: - props = bpy.context.scene.IfcGitProperties + props = cls.get_ifcgit_props() repo = IfcGitRepo.repo item = props.ifcgit_commits[props.commit_index] @@ -500,8 +507,8 @@ class IfcGit: output.write(line + b"\n") @classmethod - def execute_merge(cls, path_ifc: str, operator: bpy.types.Operator) -> Union[None, False]: - props = bpy.context.scene.IfcGitProperties + def execute_merge(cls, path_ifc: str, operator: bpy.types.Operator) -> Union[None, Literal[False]]: + props = cls.get_ifcgit_props() repo = IfcGitRepo.repo item = props.ifcgit_commits[props.commit_index] lookup = cls.branches_by_hexsha(repo) diff --git a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py index 5ace7af9d4..5970c08d6d 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py +++ b/src/ifcopenshell-python/ifcopenshell/api/cost/assign_cost_item_quantity.py @@ -52,15 +52,11 @@ def assign_cost_item_quantity( count towards the cost item (they only provide value). :param cost_item: The IfcCostItem to assign parametric quantities to - :type cost_item: ifcopenshell.entity_instance :param products: The IfcObjects to assign parametric quantities to - :type products: list[ifcopenshell.entity_instance] :param prop_name: The name of the quantity. If this is not specified, then it is assumed that there is no calculated quantity, and the number of objects are counted instead. - :type prop_name: str, optional :return: None - :rtype: None Example: