mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 09:48:32 +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.AddConstrTypeInstance,
|
||||
product.DisplayConstrTypes,
|
||||
product.ReinvokeOperator,
|
||||
product.AlignProduct,
|
||||
product.DynamicallyVoidProduct,
|
||||
workspace.Hotkey,
|
||||
|
||||
@@ -131,11 +131,14 @@ class AuthoringData:
|
||||
if new_obj is not None:
|
||||
to_be_deleted = True
|
||||
obj = new_obj
|
||||
obj.hide_set(True)
|
||||
obj.asset_mark()
|
||||
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):
|
||||
if bpy.app.is_job_running("RENDER_PREVIEW"):
|
||||
def wait_for_asset_previews_generation(check_interval_seconds=interval):
|
||||
if blender33_or_above and bpy.app.is_job_running("RENDER_PREVIEW"):
|
||||
return check_interval_seconds
|
||||
else:
|
||||
if ifc_class not in cls.props.constr_classes:
|
||||
@@ -157,7 +160,8 @@ class AuthoringData:
|
||||
collection.objects.unlink(obj)
|
||||
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
|
||||
def assetize_relating_type_from_selection(cls, browser=False):
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
import bpy
|
||||
import mathutils
|
||||
from functools import reduce
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.system
|
||||
@@ -211,6 +212,44 @@ class DisplayConstrTypes(bpy.types.Operator):
|
||||
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):
|
||||
bl_idname = "bim.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):
|
||||
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 context == "lost_context" or (context.region is not None and context.region.type != "TOOL_HEADER"):
|
||||
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 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:
|
||||
return update_interval_seconds
|
||||
else:
|
||||
self.icon_id = self.constr_classes[ifc_class].constr_types[relating_type_id].icon_id
|
||||
try:
|
||||
AuthoringData.assetize_relating_type_from_selection(browser=browser)
|
||||
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):
|
||||
@@ -68,15 +70,16 @@ def update_ifc_class(self, context):
|
||||
|
||||
|
||||
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
|
||||
constr_class_info = AuthoringData.constr_class_info(ifc_class)
|
||||
if context.region is not None and context.region.type != "TOOL_HEADER":
|
||||
AuthoringData.load_ifc_classes()
|
||||
AuthoringData.load_relating_types_browser()
|
||||
if self.updating:
|
||||
return
|
||||
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:
|
||||
AuthoringData.assetize_constr_class(ifc_class)
|
||||
if constr_class_info is None or not constr_class_info.fully_loaded:
|
||||
AuthoringData.assetize_constr_class(ifc_class)
|
||||
|
||||
|
||||
def update_relating_type(self, context):
|
||||
@@ -103,13 +106,14 @@ def get_constr_class_info(props, ifc_class):
|
||||
|
||||
|
||||
def update_preview_multiple(self, context):
|
||||
if self.preview_multiple_constr_types:
|
||||
ifc_class = self.ifc_class
|
||||
constr_class_info = get_constr_class_info(self, ifc_class)
|
||||
if constr_class_info is None or not constr_class_info.fully_loaded:
|
||||
AuthoringData.assetize_constr_class(ifc_class)
|
||||
else:
|
||||
update_relating_type(self, context)
|
||||
if context.region is not None and context.region.type != "TOOL_HEADER":
|
||||
if self.preview_multiple_constr_types:
|
||||
ifc_class = self.ifc_class
|
||||
constr_class_info = get_constr_class_info(self, ifc_class)
|
||||
if constr_class_info is None or not constr_class_info.fully_loaded:
|
||||
AuthoringData.assetize_constr_class(ifc_class)
|
||||
else:
|
||||
update_relating_type(self, context)
|
||||
|
||||
|
||||
class ConstrTypeInfo(PropertyGroup):
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# 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 bpy
|
||||
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
|
||||
@@ -40,14 +41,19 @@ class DisplayConstrTypesUI(Operator):
|
||||
bl_label = "Browse Construction Types"
|
||||
bl_options = {"REGISTER"}
|
||||
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):
|
||||
return {"FINISHED"}
|
||||
|
||||
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)
|
||||
|
||||
def draw(self, context):
|
||||
bpy.context.window.cursor_set("DEFAULT")
|
||||
props = context.scene.BIMModelProperties
|
||||
if AuthoringData.data["ifc_classes"]:
|
||||
row = self.layout.row()
|
||||
@@ -68,9 +74,19 @@ class DisplayConstrTypesUI(Operator):
|
||||
row = box.row()
|
||||
if ifc_class in props.constr_classes:
|
||||
constr_class_info = props.constr_classes[ifc_class]
|
||||
if relating_type_id in constr_class_info.constr_types:
|
||||
icon_id = constr_class_info.constr_types[relating_type_id].icon_id
|
||||
constr_types_info = constr_class_info.constr_types
|
||||
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)
|
||||
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()
|
||||
op = row.operator("bim.add_constr_type_instance", icon="ADD")
|
||||
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
|
||||
)
|
||||
|
||||
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
|
||||
bpy.app.timers.register(lambda: prop.update_ifc_class_browser(props, context))
|
||||
bpy.app.timers.register(lambda: prop.update_relating_type(props, context))
|
||||
bpy.app.timers.register(lambda: prop.update_ifc_class(props, "lost_context"))
|
||||
bpy.app.timers.register(lambda: prop.update_relating_type(props, "lost_context"))
|
||||
|
||||
ifc_class = props.ifc_class
|
||||
relating_type_id = props.relating_type_id
|
||||
|
||||
Reference in New Issue
Block a user