mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-21 14:23:53 +00:00
Construction Types Browser: Solution to most of the current issues (#2460)
* Delay asset generation * bpy.app.is_job_running only in Blender >= 3.3 * First time preview on sidebar works again * Avoid flickering * Show multiple previews on the first run * Preserve mouse position with hacky operator
This commit is contained in:
committed by
GitHub
parent
40e463a55f
commit
0a05197d3e
@@ -23,6 +23,7 @@ classes = (
|
|||||||
product.AddEmptyType,
|
product.AddEmptyType,
|
||||||
product.AddConstrTypeInstance,
|
product.AddConstrTypeInstance,
|
||||||
product.DisplayConstrTypes,
|
product.DisplayConstrTypes,
|
||||||
|
product.ReinvokeOperator,
|
||||||
product.AlignProduct,
|
product.AlignProduct,
|
||||||
product.DynamicallyVoidProduct,
|
product.DynamicallyVoidProduct,
|
||||||
workspace.Hotkey,
|
workspace.Hotkey,
|
||||||
|
|||||||
@@ -131,11 +131,14 @@ class AuthoringData:
|
|||||||
if new_obj is not None:
|
if new_obj is not None:
|
||||||
to_be_deleted = True
|
to_be_deleted = True
|
||||||
obj = new_obj
|
obj = new_obj
|
||||||
|
obj.hide_set(True)
|
||||||
obj.asset_mark()
|
obj.asset_mark()
|
||||||
obj.asset_generate_preview()
|
obj.asset_generate_preview()
|
||||||
|
blender33_or_above = bpy.app.version >= (3, 3, 0)
|
||||||
|
interval = 1e-4
|
||||||
|
|
||||||
def wait_for_asset_previews_generation(check_interval_seconds=0.0001):
|
def wait_for_asset_previews_generation(check_interval_seconds=interval):
|
||||||
if bpy.app.is_job_running("RENDER_PREVIEW"):
|
if blender33_or_above and bpy.app.is_job_running("RENDER_PREVIEW"):
|
||||||
return check_interval_seconds
|
return check_interval_seconds
|
||||||
else:
|
else:
|
||||||
if ifc_class not in cls.props.constr_classes:
|
if ifc_class not in cls.props.constr_classes:
|
||||||
@@ -157,7 +160,8 @@ class AuthoringData:
|
|||||||
collection.objects.unlink(obj)
|
collection.objects.unlink(obj)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
bpy.app.timers.register(wait_for_asset_previews_generation)
|
first_interval = 0 if blender33_or_above else interval
|
||||||
|
bpy.app.timers.register(wait_for_asset_previews_generation, first_interval=first_interval)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def assetize_relating_type_from_selection(cls, browser=False):
|
def assetize_relating_type_from_selection(cls, browser=False):
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import mathutils
|
import mathutils
|
||||||
|
from functools import reduce
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
import ifcopenshell.util.system
|
import ifcopenshell.util.system
|
||||||
@@ -211,6 +212,44 @@ class DisplayConstrTypes(bpy.types.Operator):
|
|||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class ReinvokeOperator(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.reinvoke_operator"
|
||||||
|
bl_label = "Reinvoke Popup Operator"
|
||||||
|
bl_options = {"REGISTER"}
|
||||||
|
bl_description = "Reinvoke a popup operator"
|
||||||
|
operator: bpy.props.StringProperty()
|
||||||
|
mouse_x: bpy.props.IntProperty()
|
||||||
|
mouse_y: bpy.props.IntProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
event_mouse_x, event_mouse_y = event.mouse_x, event.mouse_y
|
||||||
|
window_mouse_x, window_mouse_y = self.mouse_x, self.mouse_y
|
||||||
|
if (event_mouse_x, event_mouse_y) == (10, 10):
|
||||||
|
return {"FINISHED"}
|
||||||
|
window = context.window
|
||||||
|
window.cursor_set("WAIT")
|
||||||
|
window.cursor_warp(10, 10)
|
||||||
|
run_operator = self.run_operator
|
||||||
|
operator = self.operator
|
||||||
|
|
||||||
|
def reinvoke():
|
||||||
|
window.cursor_warp(event_mouse_x, event_mouse_y)
|
||||||
|
kwargs = {}
|
||||||
|
if (window_mouse_x, window_mouse_y) != (0, 0):
|
||||||
|
kwargs.update({"mouse_x": window_mouse_x, "mouse_y": window_mouse_y})
|
||||||
|
run_operator(operator, "INVOKE_DEFAULT", **kwargs)
|
||||||
|
|
||||||
|
bpy.app.timers.register(reinvoke)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def run_operator(operator, *args, **kwargs):
|
||||||
|
reduce(lambda x, arg: getattr(x, arg), operator.split("."), bpy.ops)(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class AlignProduct(bpy.types.Operator):
|
class AlignProduct(bpy.types.Operator):
|
||||||
bl_idname = "bim.align_product"
|
bl_idname = "bim.align_product"
|
||||||
bl_label = "Align Product"
|
bl_label = "Align Product"
|
||||||
|
|||||||
@@ -41,23 +41,25 @@ def get_relating_type_browser(self, context):
|
|||||||
|
|
||||||
|
|
||||||
def update_icon_id(self, context, browser=False):
|
def update_icon_id(self, context, browser=False):
|
||||||
ifc_class = self.ifc_class_browser if browser else self.ifc_class
|
if context == "lost_context" or (context.region is not None and context.region.type != "TOOL_HEADER"):
|
||||||
relating_type_id = self.relating_type_id_browser if browser else self.relating_type_id
|
ifc_class = self.ifc_class_browser if browser else self.ifc_class
|
||||||
# relating_type = AuthoringData.relating_type_name_by_id(ifc_class, relating_type_id)
|
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 self.constr_classes or relating_type_id not in self.constr_classes[ifc_class].constr_types:
|
|
||||||
try:
|
|
||||||
AuthoringData.assetize_relating_type_from_selection(browser=browser)
|
|
||||||
except ConstrTypeEntityNotFound:
|
|
||||||
return
|
|
||||||
|
|
||||||
def set_icon(update_interval_seconds=0.0001):
|
|
||||||
if ifc_class not in self.constr_classes or relating_type_id not in self.constr_classes[ifc_class].constr_types:
|
if ifc_class not in self.constr_classes or relating_type_id not in self.constr_classes[ifc_class].constr_types:
|
||||||
return update_interval_seconds
|
try:
|
||||||
else:
|
AuthoringData.assetize_relating_type_from_selection(browser=browser)
|
||||||
self.icon_id = self.constr_classes[ifc_class].constr_types[relating_type_id].icon_id
|
except ConstrTypeEntityNotFound:
|
||||||
|
return
|
||||||
|
|
||||||
bpy.app.timers.register(set_icon)
|
def set_icon(update_interval_seconds=1e-4):
|
||||||
|
if (ifc_class not in self.constr_classes
|
||||||
|
or relating_type_id not in self.constr_classes[ifc_class].constr_types):
|
||||||
|
return update_interval_seconds
|
||||||
|
else:
|
||||||
|
self.icon_id = self.constr_classes[ifc_class].constr_types[relating_type_id].icon_id
|
||||||
|
|
||||||
|
bpy.app.timers.register(set_icon)
|
||||||
|
|
||||||
|
|
||||||
def update_ifc_class(self, context):
|
def update_ifc_class(self, context):
|
||||||
@@ -68,15 +70,16 @@ def update_ifc_class(self, context):
|
|||||||
|
|
||||||
|
|
||||||
def update_ifc_class_browser(self, context):
|
def update_ifc_class_browser(self, context):
|
||||||
AuthoringData.load_ifc_classes()
|
if context.region is not None and context.region.type != "TOOL_HEADER":
|
||||||
AuthoringData.load_relating_types_browser()
|
AuthoringData.load_ifc_classes()
|
||||||
if self.updating:
|
AuthoringData.load_relating_types_browser()
|
||||||
return
|
if self.updating:
|
||||||
ifc_class = self.ifc_class_browser
|
return
|
||||||
constr_class_info = AuthoringData.constr_class_info(ifc_class)
|
ifc_class = self.ifc_class_browser
|
||||||
|
constr_class_info = AuthoringData.constr_class_info(ifc_class)
|
||||||
|
|
||||||
if constr_class_info is None or not constr_class_info.fully_loaded:
|
if constr_class_info is None or not constr_class_info.fully_loaded:
|
||||||
AuthoringData.assetize_constr_class(ifc_class)
|
AuthoringData.assetize_constr_class(ifc_class)
|
||||||
|
|
||||||
|
|
||||||
def update_relating_type(self, context):
|
def update_relating_type(self, context):
|
||||||
@@ -103,13 +106,14 @@ def get_constr_class_info(props, ifc_class):
|
|||||||
|
|
||||||
|
|
||||||
def update_preview_multiple(self, context):
|
def update_preview_multiple(self, context):
|
||||||
if self.preview_multiple_constr_types:
|
if context.region is not None and context.region.type != "TOOL_HEADER":
|
||||||
ifc_class = self.ifc_class
|
if self.preview_multiple_constr_types:
|
||||||
constr_class_info = get_constr_class_info(self, ifc_class)
|
ifc_class = self.ifc_class
|
||||||
if constr_class_info is None or not constr_class_info.fully_loaded:
|
constr_class_info = get_constr_class_info(self, ifc_class)
|
||||||
AuthoringData.assetize_constr_class(ifc_class)
|
if constr_class_info is None or not constr_class_info.fully_loaded:
|
||||||
else:
|
AuthoringData.assetize_constr_class(ifc_class)
|
||||||
update_relating_type(self, context)
|
else:
|
||||||
|
update_relating_type(self, context)
|
||||||
|
|
||||||
|
|
||||||
class ConstrTypeInfo(PropertyGroup):
|
class ConstrTypeInfo(PropertyGroup):
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# 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/>.
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import bpy
|
||||||
from bpy.types import Panel, Operator
|
from bpy.types import Panel, Operator
|
||||||
from blenderbim.bim.module.model.data import AuthoringData
|
from blenderbim.bim.module.model.data import AuthoringData
|
||||||
from blenderbim.bim.helper import prop_with_search, close_operator_panel
|
from blenderbim.bim.helper import prop_with_search, close_operator_panel
|
||||||
@@ -40,14 +41,19 @@ class DisplayConstrTypesUI(Operator):
|
|||||||
bl_label = "Browse Construction Types"
|
bl_label = "Browse Construction Types"
|
||||||
bl_options = {"REGISTER"}
|
bl_options = {"REGISTER"}
|
||||||
bl_description = "Display all available Construction Types to add new instances"
|
bl_description = "Display all available Construction Types to add new instances"
|
||||||
|
mouse_x: bpy.props.IntProperty(default=0)
|
||||||
|
mouse_y: bpy.props.IntProperty(default=0)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
|
if (self.mouse_x, self.mouse_y) == (0, 0):
|
||||||
|
self.mouse_x, self.mouse_y = context.window.x, context.window.y
|
||||||
return context.window_manager.invoke_popup(self, width=550)
|
return context.window_manager.invoke_popup(self, width=550)
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
|
bpy.context.window.cursor_set("DEFAULT")
|
||||||
props = context.scene.BIMModelProperties
|
props = context.scene.BIMModelProperties
|
||||||
if AuthoringData.data["ifc_classes"]:
|
if AuthoringData.data["ifc_classes"]:
|
||||||
row = self.layout.row()
|
row = self.layout.row()
|
||||||
@@ -68,9 +74,19 @@ class DisplayConstrTypesUI(Operator):
|
|||||||
row = box.row()
|
row = box.row()
|
||||||
if ifc_class in props.constr_classes:
|
if ifc_class in props.constr_classes:
|
||||||
constr_class_info = props.constr_classes[ifc_class]
|
constr_class_info = props.constr_classes[ifc_class]
|
||||||
if relating_type_id in constr_class_info.constr_types:
|
constr_types_info = constr_class_info.constr_types
|
||||||
icon_id = constr_class_info.constr_types[relating_type_id].icon_id
|
if relating_type_id in constr_types_info:
|
||||||
|
icon_id = constr_types_info[relating_type_id].icon_id
|
||||||
row.template_icon(icon_value=icon_id, scale=6.0)
|
row.template_icon(icon_value=icon_id, scale=6.0)
|
||||||
|
else:
|
||||||
|
mouse_x, mouse_y = self.mouse_x, self.mouse_y
|
||||||
|
|
||||||
|
def run_operator():
|
||||||
|
bpy.ops.bim.reinvoke_operator(
|
||||||
|
"INVOKE_DEFAULT", operator="bim.display_constr_types_ui", mouse_x=mouse_x, mouse_y=mouse_y
|
||||||
|
)
|
||||||
|
bpy.app.timers.register(run_operator)
|
||||||
|
|
||||||
row = box.row()
|
row = box.row()
|
||||||
op = row.operator("bim.add_constr_type_instance", icon="ADD")
|
op = row.operator("bim.add_constr_type_instance", icon="ADD")
|
||||||
op.from_invoke = True
|
op.from_invoke = True
|
||||||
|
|||||||
@@ -74,10 +74,10 @@ class BimTool(WorkSpaceTool):
|
|||||||
AuthoringData.data["relating_types_ids"] if "relating_types_ids" in AuthoringData.data else False
|
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:
|
if ifc_classes and relating_types_ids and not props.icon_id and not is_tool_header:
|
||||||
# hack Dion won't like to show a preview also on the first time the sidebar is shown
|
# 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_ifc_class_browser(props, context))
|
bpy.app.timers.register(lambda: prop.update_ifc_class(props, "lost_context"))
|
||||||
bpy.app.timers.register(lambda: prop.update_relating_type(props, context))
|
bpy.app.timers.register(lambda: prop.update_relating_type(props, "lost_context"))
|
||||||
|
|
||||||
ifc_class = props.ifc_class
|
ifc_class = props.ifc_class
|
||||||
relating_type_id = props.relating_type_id
|
relating_type_id = props.relating_type_id
|
||||||
|
|||||||
Reference in New Issue
Block a user