Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't rename/delete My Storey when running Python script from CLI #4140

Closed
pederlh opened this issue Dec 21, 2023 · 3 comments
Closed

Can't rename/delete My Storey when running Python script from CLI #4140

pederlh opened this issue Dec 21, 2023 · 3 comments

Comments

@pederlh
Copy link

pederlh commented Dec 21, 2023

Cross-posting from this OSArch forum thread.

I am trying to rename (or delete) the default storey that is created when initializing an IFC project in Blender, and I wish to do this through running a Python script. However, whether this succeeds or not seems to depend on if the script is run from within the Blender UI or if the script is provided when running Blender from the command line.

I have an example file which should reproduce the problem. If the following snippet is run from the Scripting tab in Blender, the storey seems to be renamed properly. What I try to do is to do the renaming, set the storey as the (current) parent colelction, assign ifc classes to some objects, and link the ifc components to the storey:

import bpy

bpy.ops.bim.create_project()
bpy.ops.mesh.primitive_cube_add(
    size=2, enter_editmode=False, align="WORLD", location=(0, 0, 0), scale=(1, 1, 1)
)

storey_name = "Floor 1"

# Rename collection / Storey object
old_collection_name = "IfcBuildingStorey/My Storey"
if old_collection_name in bpy.data.collections and bpy.data.objects:
    collection = bpy.data.collections.get(old_collection_name)
    collection.name = f"IfcBuildingStorey/{storey_name}"

    storey = bpy.data.objects[old_collection_name]
    storey.name = f"IfcBuildingStorey/{storey_name}"
else:
    raise ValueError("Couldn't edit storey name, default storey not found.")

parent_collection_name = f"IfcBuildingStorey/{storey_name}"
parent_collection = bpy.data.collections.get(parent_collection_name)
if not parent_collection:
    raise ValueError(f"Parent collection '{parent_collection_name}' not found.")

# Construct Ifc components
bpy.ops.object.select_all(action="DESELECT")
for obj in bpy.data.objects:
    if obj.type == "MESH":
        obj.select_set(True)

        bpy.ops.bim.assign_class(ifc_class="IfcBuildingElementProxy")

        # Unlink from any other collection (like 'IfcProject/My Project') before adding to storey
        for collection in obj.users_collection:
            collection.objects.unlink(obj)
        parent_collection.objects.link(obj)

        obj.select_set(False)  # Deselect the current object

This seems to work fine, but if I add the following lines to the Python file (saving the code as debug_renaming.py)

# Export the loaded model to IFC format
import sys

argv = sys.argv[sys.argv.index("--") + 1 :]
output_path = argv[0]
bpy.ops.export_ifc.bim(filepath=str(output_path), should_save_as=True, save_as_invoked=True)

and run from the command line

$ Blender -b -P debug_renaming.py -- cube_CLI.ifc

the cube will be linked to IfcBuildingStorey/My Storey instead of IfcBuildingStorey/Floor 1

I don't have any clue what is going on here, so help is much appreciated. If the renaming itself is done incorrectly I would love to hear the better way.

Using Blender 4.0.0, BlenderBIM 0.0.23110

@Andrej730
Copy link
Contributor

I think it's the same problem as with my suggestion to rename storey with snippet below - it works when called from console but doesn't work when called from python script.

obj = bpy.data.objects["IfcBuildingStorey/My Storey"]
obj.name = "test"
obj.name # 'IfcBuildingStorey/test'

So the issue in your case is that IFC name is not updated when cube_CLI.ifc is saved. We change IFC name based on object name (in name_callback in handler.py).

def name_callback(obj, data):
try:
obj.name
except:

To detect blender objects name change we subscribe to it using blender msgbus: bpy.msgbus.subscribe_rna(...). And the problem with that method that it seems that to trigger callbacks only after python code is executed.

Therefore, if you rename object and export in one python script then object's IFC name updated only after file is exported. You can test it by adding print("file is exported") at the end of your script and print("name_callback") at the beginning of name_callback() and "name_callback" will never get printed if script is executed from background mode.

So the possible solutions are:

  1. Find some way to trigger callbacks when it's need (e.g. before the saving .ifc). I've searched and asked on blender forums and couple blender chats but haven't found a way to do so. Perhaps, @Gorgious56 have you met this one before?

  2. Call name_callback directly.

from blenderbim.bim.handler import name_callback
storey = bpy.data.objects[old_collection_name]
storey.name = storey_name
name_callback(storey, "name")

We could have also call all callbacks on bpy.ops.export_ifc.bim (and other places) but I would look for other solution as it would solve only part of the problems with postponed handler calls.

@Moult
Copy link
Contributor

Moult commented Jan 10, 2024

I'd say this is just a fact of life of how Blender and Python work, not really a bug or anything we can fix. There are limitations such that if you call it headlessly, you need to be aware of these shortcomings. For this reason it's best to:

  1. Where possible, write pure ifcopenshell Python to manipulate data, and not depend on Blender.
  2. Where possible use tool and core classes instead of bpy.ops.
  3. Where possible use bpy.ops that don't rely on special event handlers, and if they do, be aware of it and manually edit the data.

The less you depend on the UI, the more stable things will become. If you're trying to get a bunch of autogenerated shapes/meshes, this is completely possible without bpy.ops and I'd recommend not using bpy.ops at all.

@Moult Moult closed this as completed Jan 10, 2024
@pederlh
Copy link
Author

pederlh commented Jan 10, 2024

Thank you for the feedback @Andrej730 and @Moult, both your answers are incredibly helpful 💯

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants