it is common to, instead of defining the properties in the UI, define properties as a separate PropertyGroup. This property group is, when installing the add-on, registered on a certain namespace (for example the 'scene'), so you can access it globally. So for you that could be:
from bpy.props import BoolProperty, StringProperty, IntProperty, EnumProperty
from bpy.types import PropertyGroup
class ExcelProperties(PropertyGroup):
ifc_product: BoolProperty(name="IfcProduct", default=False)
ifc_building_story: BoolProperty(name="IfcBuildingStory", default=False)
name: BoolProperty(name="Name", default=False)
excel_path: stringProperty(name="Excel path", subtype='FILE_PATH')
Afterwards you register the property in some namespace:
def execute(self, context):
excel_properties = context.scene.excel_properties
if excel_properties.ifc_product:
do_something()
It's quite nice because you can separate the properties from the ui and from the functions. BlenderBIM does this nicely (I use this too for my own add-ons, it might also be the standard structure, I don't know), by making the following structure for each package:
package_name:
ui.py
prop.py
operator.py
__init__.py (for registering all the classes from the three python files.
then in the WritetoXLSX class I can call the checkbox.
if context.scene.my_ifcproduct == True:
ifc_dictionary['IfcProduct'] = ifc_product_type_list
I think I need to group some of them, start to get a bit crowdy. And I hardcoded the sum formulas in the header, they don't work anymore. Because the colums move when the user decides to check and uncheck some of the exports needed. Need to find a method for this.
Hmm I've actually never done that. Theoretically it should be as easy as copy / pasting the third party module and all its dependencies in a folder in the zip, and then access it like you would any other module you created in the addon folder. Blenderbim does that in libs/site/packages
You should be able to use from BlenderBIMOpenOfficeXML.lib.site.packages import openpyxl.
Note python modules should be named in lower case, and I think it's not mandatory but you can use snake_case like you would regular python modules.
I used the method @LaurensJN described.
I added this to my script
import site
site.addsitedir(os.path.join(os.path.dirname(os.path.realpath(__file__)), "libs", "site", "packages"))
Then copy pasted the modules from the python site-packages to my own folder called with the following folder struture libs>sites>packages ( I have a vague memory of reading this somewhere in Blender documentation too)
I placed the openpyxl, pandas and xlsxwriter modules in there
did a clean install of Blender 3.0 and BlenderBIM on my old brick laptop.
validated where the imports were coming from with [module_name_here].__file__ to see where they are located.
And it works! :-D
They are now placed in
Nope there is AFAIK no convention on how to structure an add-on in Blender, apart from the mandatory dictionary with bl_info somewhere in the file if it is a single script or in __init__.py if it's a multi-file addon.
That being said as a general rule in programming if a class or a module is doing more than one thing, it's a good idea to split it up, and keep dependencies between modules to a minimum. Most beginner classes / tutorials will tell you about the SOLID principle which is an interesting mantra to try to follow, at least until you know when it makes sense to break the rules. When you'll know, you'll know. :)
The ui / prop / operator / tool separation makes sense because usually data storage, data manipulation, and data display can be done by independent systems so there is no reason they should be scripted in the same place. The great benefit is that it's easier to test, easier to debug, easier to extend and easier to swap when a new technology for either module emerges.
Nope there is AFAIK no convention on how to structure an add-on in Blender, apart from the mandatory dictionary with bl_info somewhere in the file if it is a single script or in init.py if it's a multi-file addon.
How do I run a live development environment when I have three init.py, prop.py, operator.py and ui.py files from VS Code?
I only need to register the __init.py in Blender somewhere?
Hey @Coen do you have it over on github or such ? I can try giving you hints. I think there are fundamentally 3 types of addon strucutres.
If your addon does a single thing, you can write all your code in a single file and install that file as an addon.
If your addon does a few thing, you want to have a __init__.py file with the mandatory addon dictionary so Blender knows how to set it up. Then you can have in the same folder your other features in different python files. You can import them with a simple from . import my_other_file.
If your addon is expansive, you can structure it with folders, like you would files in your computer. You can then navigate through them with import my_addon_directory.my_secondary_directory.my_other_file. You still need to have a __init__.py file with the mandatory dictionary.
A lot of blender addons are available over on github, you can study how they're structure and see what you want to go for : https://github.com/topics/blender-addon
Yes, I think i've added you to my private repo, it's a work in progress of the blenderbim spreadsheet add-on completely rewritten with the ifcopenshell.api
What I want to do is to just change the code of the add-on from vs code and see instant changes in the ui of blender.
Thanks, this was suprisingly easy, however I always get lost in the seas of documentation with Blender
What I did was this:
I edited these settings:
each time I save in VS code the add-on is reloaded
result
Copy / paste the file in your addon root folder, boom. Everything is automatically registered. One thing you do absolutely need to do, if you're using subfolders, is add a file called __init__.py in every subfolder otherwise the utility won't be able to scan it and the classes won't register. It doesn't matter if the file is blank. It also automatically runs any function in your files named register at startup and unregister when you disable the addon, so you still can define custom behaviour on register / unregister.
I think I am doing something wrong, I have the following in prop.py
import bpy
from bpy.types import Scene
from bpy.props import BoolProperty
def register():
Scene.property_ifcproduct = BoolProperty(name="IfcProduct",description="Export IfcProduct",default=True)
def unregister():
del Scene.property_ifcproduct
The python files are all in the same folder.
When I try from . prop import property_ifcproduct the add-on fails to load from VS code, it says:
RuntimeError: Error: Traceback (most recent call last):
File "c:\Program Files\Blender Foundation\Blender 3.4\3.4\scripts\modules\addon_utils.py", line 333, in enable
mod = __import__(module_name)
File "C:\Users\cclaus\AppData\Roaming\Blender Foundation\Blender\3.4\scripts\addons\blenderbim_spreadsheet\__init__.py", line 20, in <module>
from . import ui, prop, operator
File "C:\Users\cclaus\AppData\Roaming\Blender Foundation\Blender\3.4\scripts\addons\blenderbim_spreadsheet\operator.py", line 4, in <module>
print (bpy.props.property_ifcproduct)
AttributeError: module 'bpy.props' has no attribute 'property_ifcproduct'
Well you can't do that in Python, could you elaborate on what you're trying to do ? You can access the property in the operator from the context passed in the execute method. context.scene.property_ifcproduct
Thanks for answering:
This I what I want to do:
I want to create a checkbox in the ui, but I want to use the name of the checkbox in operator.py rather then the true or false value. My aim is to create only the 'hardcoded' variables in the ui.py.
I made this littel UI:
in ui.py:
Oh, okay, I understand. You may be going a little too fast here, but it's possible. FWIW I don't think it's that bad to hardcode values, as long as you can replace them easily with a "Search & Replace" across all your files.
You need to access a barely documented construct through bl_rna.
I didn't test this but you should be able to access it with my_bool_name = context.scene.bl_rna.properties["my_ifcproduct"].name. You can explore dir(context.scene.bl_rna.properties["my_ifcproduct"]), it lets you fetch some information about the property definition.
Thanks, I think I've exhaused every option, read the documentation thoroughfully but to no avail. I eventually came up with this workaround.
The idea now is just to hardcode the variables only in prop.py
import bpy
from bpy.types import Scene
from bpy.props import BoolProperty, StringProperty
prop_ifcproduct = 'IfcProduct'
class IFCProperties(bpy.types.PropertyGroup):
my_ifcproduct: bpy.props.BoolProperty(name=prop_ifcproduct,description="Export IfcProduct",default=True)
my_ifcbuildingstorey: bpy.props.BoolProperty(name="IfcBuildingStorey",description="Export IfcBuildingStorey",default = True)
def register():
bpy.utils.register_class(IFCProperties)
bpy.types.Scene.ifc_properties = bpy.props.PointerProperty(type=IFCProperties)
def unregister():
bpy.utils.unregister_class(IFCProperties)
del bpy.types.Scene.ifc_properties
in operator.py
import bpy
from . import prop
class ExportToSpreadSheet(bpy.types.Operator):
bl_idname = "export.tospreadsheet"
bl_label = "Export to Spreadsheet"
def execute(self, context):
print (prop.prop_ifcproduct)
return {'FINISHED'}
def register():
bpy.utils.register_class(ExportToSpreadSheet)
def unregister():
bpy.utils.unregister_class(ExportToSpreadSheet)
Might not be good practice and a bit risky maybe, but it works and I only have one place where I define variables
What would be best practice for filling the UI in blender with data coming from an IFC file?
For example, I want to create a dropdown classification menu, so end users can select which classification they would like to export. Because there can be more than 1 classificatoin system:
Comments
@Coen you can use the update function to run once a change has been made. See https://docs.blender.org/api/current/bpy.props.html#update-example
it is common to, instead of defining the properties in the UI, define properties as a separate PropertyGroup. This property group is, when installing the add-on, registered on a certain namespace (for example the 'scene'), so you can access it globally. So for you that could be:
Afterwards you register the property in some namespace:
Now, in the UI or in the Operator, you can access the properties in the execute/draw functions:
UI:
Operator:
It's quite nice because you can separate the properties from the ui and from the functions. BlenderBIM does this nicely (I use this too for my own add-ons, it might also be the standard structure, I don't know), by making the following structure for each package:
package_name:
You can for example look at the GIS package in the BlenderBIM add-on: https://github.com/IfcOpenShell/IfcOpenShell/tree/v0.7.0/src/blenderbim/blenderbim/bim/module/gis (the other packages are built the same way but more extensive so this is a simple example)
@LaurensJN
@vpajic
Thanks for the help.
I use this method now, in the BlenderBIMXLSXPanel in the draw method put a row and colums for the ui
then in register
then in the WritetoXLSX class I can call the checkbox.
I think I need to group some of them, start to get a bit crowdy. And I hardcoded the sum formulas in the header, they don't work anymore. Because the colums move when the user decides to check and uncheck some of the exports needed. Need to find a method for this.

I like this layout, any suggestions for a good intuive UI are very welcome. GUI is not my strong suit.

My two cents, @Coen i think last ui is nice, with the possibility of hiding the checkboxes of the same group
You can create sub-panels :
See the last part of that answer : https://blender.stackexchange.com/a/155517/86891 or the very minimal amount of code there : https://wiki.blender.org/wiki/Reference/Release_Notes/2.80/Python_API/UI_API#Sub_Panels
note you can add a class attribute
bl_order = 0/1/2/3 etc
if you want to force the ordering of the sub-panels. see https://docs.blender.org/api/current/bpy.types.Panel.html#bpy.types.Panel.bl_orderAnd use
bl_options = {'DEFAULT_CLOSED'}
for the sub panels to be folded by defaultYou can try to add
layout.prop(scene, "my_prop", toggle=True)
if it's a boolean to display as a toggle rather than a checkbox. Matter of taste https://blender.stackexchange.com/a/117791/86891How do you bundle a Blender BIM add-on in a zip if the add-on has third party python dependencies?
Hmm I've actually never done that. Theoretically it should be as easy as copy / pasting the third party module and all its dependencies in a folder in the zip, and then access it like you would any other module you created in the addon folder. Blenderbim does that in libs/site/packages
These are the dependencies as where they are stored:
I found this SO post that there are numerous ways of importing dependencies.
I copied pasted the modules openpyxl, pandas and xlsxwriter to a new relative folder
BlenderBIMOpenOfficeXML\lib\site\packages
Now I started reading that thread on Stack Overflow.
and I got completely lost...
You should be able to use
from BlenderBIMOpenOfficeXML.lib.site.packages import openpyxl
.Note python modules should be named in lower case, and I think it's not mandatory but you can use
snake_case
like you would regular python modules.I have never done that either, but the BlenderBIM add-on does that with a great deal of packages:
See https://github.com/IfcOpenShell/IfcOpenShell/blob/v0.7.0/src/blenderbim/blenderbim/init.py
If you open any BlenderBIM add-on folder, you see all packages can be found under /libs/site/packages. Therefore this line probably does the trick:
If I am wrong, probably @Moult can explain this better :)
I used the method @LaurensJN described.
I added this to my script
Then copy pasted the modules from the python site-packages to my own folder called with the following folder struture libs>sites>packages ( I have a vague memory of reading this somewhere in Blender documentation too)
I placed the openpyxl, pandas and xlsxwriter modules in there
did a clean install of Blender 3.0 and BlenderBIM on my old brick laptop.
validated where the imports were coming from with
[module_name_here].__file__
to see where they are located.And it works! :-D
They are now placed in
When I try to install the add-on Linux Ubuntu I get a specific pandas error. Going to look at that another time.
Has this been documented somewhere? This thread is gold.
EDIT: Never mind, I should read the thread more carefully, found it here:
https://wiki.osarch.org/index.php?title=Create_your_first_Blender_add-on
Is this the 'default' stander add-on convention for Blender? all the BIM modules seem to be structured in this way
@Coen oh wow I forgot I wrote that :)
Nope there is AFAIK no convention on how to structure an add-on in Blender, apart from the mandatory dictionary with
bl_info
somewhere in the file if it is a single script or in__init__.py
if it's a multi-file addon.That being said as a general rule in programming if a class or a module is doing more than one thing, it's a good idea to split it up, and keep dependencies between modules to a minimum. Most beginner classes / tutorials will tell you about the SOLID principle which is an interesting mantra to try to follow, at least until you know when it makes sense to break the rules. When you'll know, you'll know. :)
The ui / prop / operator / tool separation makes sense because usually data storage, data manipulation, and data display can be done by independent systems so there is no reason they should be scripted in the same place. The great benefit is that it's easier to test, easier to debug, easier to extend and easier to swap when a new technology for either module emerges.
@Gorgious
How do I run a live development environment when I have three init.py, prop.py, operator.py and ui.py files from VS Code?
I only need to register the __init.py in Blender somewhere?
In which file should I put this update code? or just place it a sepertae python file with the add-on?
Hey @Coen do you have it over on github or such ? I can try giving you hints. I think there are fundamentally 3 types of addon strucutres.
If your addon does a single thing, you can write all your code in a single file and install that file as an addon.
If your addon does a few thing, you want to have a
__init__.py
file with the mandatory addon dictionary so Blender knows how to set it up. Then you can have in the same folder your other features in different python files. You can import them with a simplefrom . import my_other_file
.If your addon is expansive, you can structure it with folders, like you would files in your computer. You can then navigate through them with
import my_addon_directory.my_secondary_directory.my_other_file
. You still need to have a__init__.py
file with the mandatory dictionary.A lot of blender addons are available over on github, you can study how they're structure and see what you want to go for : https://github.com/topics/blender-addon
or even the official blender addons https://github.com/blender/blender-addons
@Gorgious
Yes, I think i've added you to my private repo, it's a work in progress of the blenderbim spreadsheet add-on completely rewritten with the ifcopenshell.api
What I want to do is to just change the code of the add-on from vs code and see instant changes in the ui of blender.
AFAIK there are several methods to achieve that, I've only ever used one and never had any problem with it. It's not that hard to setup, it uses the Vscode development addon by Jacques Lucke one of the devs of Geometry Nodes. Here's a good explanation how to set it up https://b3d.interplanety.org/en/using-microsoft-visual-studio-code-as-external-ide-for-writing-blender-scripts-add-ons/
Thanks, this was suprisingly easy, however I always get lost in the seas of documentation with Blender



What I did was this:
I edited these settings:
each time I save in VS code the add-on is reloaded
result
Awesome !! I really hate registering / unregistering classes. That's why I pretty much always use this cool module by Jacques Lucke to auto load / unload blender classes. https://gist.github.com/JacquesLucke/11fecc6ea86ef36ea72f76ca547e795b
Copy / paste the file in your addon root folder, boom. Everything is automatically registered. One thing you do absolutely need to do, if you're using subfolders, is add a file called
__init__.py
in every subfolder otherwise the utility won't be able to scan it and the classes won't register. It doesn't matter if the file is blank. It also automatically runs any function in your files namedregister
at startup andunregister
when you disable the addon, so you still can define custom behaviour on register / unregister.Take a look at one of my addons if you want to see how it works. https://github.com/Gorgious56/asset_browser_utilities
Here for instance I don't register my menus, I only append them to the asset browser header. The rest is taken care of by the utility. https://github.com/Gorgious56/asset_browser_utilities/blob/master/core/ui/menu/main.py#L64-L69
@Gorgious
How do you acces the values in operator.py if you have defined them in prop.py?
If they're in the same directory, you can do
from . prop import my_prop
I think I am doing something wrong, I have the following in
prop.py
then in
ui.py
I have:Now I want to acces this in
operator.py
The python files are all in the same folder.
When I try
from . prop import property_ifcproduct
the add-on fails to load from VS code, it says:Well you can't do that in Python, could you elaborate on what you're trying to do ? You can access the property in the operator from the context passed in the
execute
method.context.scene.property_ifcproduct
Thanks for answering:

This I what I want to do:
I want to create a checkbox in the ui, but I want to use the name of the checkbox in operator.py rather then the true or false value. My aim is to create only the 'hardcoded' variables in the ui.py.
I made this littel UI:
in ui.py:
in prop.py:
in operator.py
I would like the variable
my_bool_name
just to returnIfcProduct
as a dumb string in operator.pyOh, okay, I understand. You may be going a little too fast here, but it's possible. FWIW I don't think it's that bad to hardcode values, as long as you can replace them easily with a "Search & Replace" across all your files.
You need to access a barely documented construct through
bl_rna
.I didn't test this but you should be able to access it with
my_bool_name = context.scene.bl_rna.properties["my_ifcproduct"].name
. You can exploredir(context.scene.bl_rna.properties["my_ifcproduct"])
, it lets you fetch some information about the property definition.@Gorgious
Thanks, I think I've exhaused every option, read the documentation thoroughfully but to no avail. I eventually came up with this workaround.
The idea now is just to hardcode the variables only in prop.py
in operator.py
Might not be good practice and a bit risky maybe, but it works and I only have one place where I define variables
What would be best practice for filling the UI in blender with data coming from an IFC file?

For example, I want to create a dropdown classification menu, so end users can select which classification they would like to export. Because there can be more than 1 classificatoin system: