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:
Carlos Villagrasa
2022-09-30 18:01:26 +02:00
committed by GitHub
parent 40e463a55f
commit 0a05197d3e
6 changed files with 101 additions and 37 deletions
@@ -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