Multiple files Addon development strategy questions (Blender 2.8)

Hi guys, i post in this section as it can help beginners to understand the process of building addon from multiple files.

here is the context:
as you may want to separate your script in various files, like properties, ui elements (panels, menus, buttons,etc…), operators and presets, there is needs in term of register and import, here is the tricky part :wink:

In this context, here is the list of files in the addon folder:

  • init.py
  • properties.py
  • ui.py
  • operators.py
  • preset.py

Lets take an exemple for our addon, a simple camera addon, that add, modify and view an orthographic camera …

So, i would like to ask the “python specialists” how to setup our files, the import in each file and the register section in init.py

hope to complete this post as sson as we have clear infos :wink:
Have a great day

Hi, you are maybe shy to comment, lets get started with a simple question:

How to access a propertygroup (in properties.py) from ui.py ?
most of the time, the error is about register thoses properties before assignement… how to avoid it ?
thx

When it comes to code organisation there are no golden rules.

It depends on the scale and the nature of the project.

Python packages can also nest , so you can have multiple subfolders with their own __init__.py files.

top level init file usually has the bare bones for loading the addon, so register and unregister functions and of the blender info dicitionary that provides the meta data that Blender uses to understand what the addon is about. But even that is not a rule because the init files may just import another file that does this job for it.

If the addon is small it can be all included in a single python module.

thx kilon, i was looking a way to explain to my students how to pack their scripts, i use both techniques for myself but never found a rule on it, so thanks a lot to clarify this point ! i would like to publish for free the course tutorials :wink:

Hi,
i really got problem to understand some issues regarding this topic :frowning:

i give here a clear exemple, that i need to fix and understand: custom icons in this case !

right, this is what i did:

in __init__.py i declare and register my icons:

...
preview_collections = {}
...
def register():
# Register icons preview
	from bpy.utils import previews
	pcoll = bpy.utils.previews.new()
	my_icons_dir = os.path.join(os.path.dirname(__file__), "icons")
	pcoll.load("my_icon_world", os.path.join(my_icons_dir, "WORLD16.png"), 'IMAGE')
	preview_collections["main"] = pcoll

def unregister():
# Unregister icons preview
	for pcoll in preview_collections.values():
		bpy.utils.previews.remove(pcoll)
	preview_collections.clear()

in an other file, ui.py, i want to use this icon
i create a panel and in draw i add:

...
	pcoll = preview_collections["main"]
	my_icon_world = pcoll["my_icon_world"]
...
    col.label(text="Test icon:",icon_value=my_icon_world.icon_id)

It doesn´t work ! why ?
please, help me to understand the relationship between files in this case…

the error shown is:

Traceback (most recent call last):
  File "C:\Users\WIN10PRO\AppData\Roaming\Blender Foundation\Blender\2.80\scripts\addons\myaddon\ui.py", line 43, in draw
    pcoll = preview_collections["main"]
KeyError: 'main'

thx a lot

Up ! :slight_smile: i tried so many thing during the last days… without success…
as the icons have been registered in init , i dont understand why i cannot access them in other addon files !
please, help me to see where i am wrong :wink:

first of all this is not the forum to ask for python problems but C problems. You should use Python Support forum.

Second, you fail to display a lot of your code so it is impossible to see exactly what you are doing.

From the looks of it you have not imported the global dictionary variable preview_collections.

Python modules are namespaces and as function their global variable are not accessible outside the module.

Generally use of globals should be avoided as much as possible because they can end up in problems far more difficult to resolve.

A potential solution in this case is to create a class that has as class variable preview_collections. then create another class in your ui.py and import ui into init. Have another class that has also a class variable named the same, but also a method that takes references the original class variable so it can be accessed by ui.py.

Essentially python cannot access anything from another modules unless you give it explicit permission to do so. So yes not having access to other addon and modules is standard Python behavior and anything otherwise would have been a recipe for disaster. This is because of name conflicts , a not so often problem where two variables of same name can override each other which can lead to data disappearing or acting very weirdly. These kind of problems are a nightmare to resolve even with a very good debugger.

Nonetheless make sure you use a debugger, Python comes with its own debugger called pdb, it will help you find such issues and why they are caused.

1 Like

thank you Kilon, you are right, i post this in the wrong section, apologizes (EDIT: i did change the section myself)
hope an admin can move it to the correct location :wink:

thank you for the explaination, usefull… i will try to fix this today, as i use custom icons for various panels classes and files.
Have a great day

it doesn t work at all :frowning: i really dont what is going on there !

i made a simple script to illustrate the problem: within 2 files only, inside a folder called myCustomIcons !

__init__.py

bl_info = {
	"name": "myCustomIcons",
	"author": "Author name",
	"version": (1, 7, 2),
	"blender": (2, 80, 0),
	"location": "3D View > Tools > N panel",
	"description": "Addon for blender 2.8",
	"category": "ADDON_X"}

# import ui.py
if "bpy" in locals():
	import imp
	imp.reload(ui)
else:
	from . import ui

import bpy
import os

from os import path
icon_dir = path.join(path.dirname(__file__), "icons")
preview_collections = {}

# define classes to register from ui.py
classes = (
	ui.VIEW3D_PT_my_panel,
		)

def register():
# register icon dict
	import bpy.utils.previews
	pcoll = bpy.utils.previews.new()
	icons = {"blogo" : "BLOGO16.png",
		}
	for key, f in icons.items():
		pcoll.load(key, path.join(icon_dir, f), 'IMAGE')
	preview_collections["main"] = pcoll

# Register Classes
	from bpy.utils import register_class
	for cls in classes:
		register_class(cls)


def unregister():

# Unregister classes
	from bpy.utils import unregister_class
	for cls in reversed(classes):
		unregister_class(cls)

# Unregister icons preview
	for pcoll in preview_collections.values():
		bpy.utils.previews.remove(pcoll)
	preview_collections.clear()

if __name__ == "__main__":
	register()

and the ui.py that contains my panel with the use of the supposed custom icon !

import bpy, os

from bpy.types import Panel

# Panel for project settings panel
class VIEW3D_PT_my_panel(Panel):
	bl_space_type = "VIEW_3D"
	bl_region_type = "UI"
	bl_label = "custom icons"
	bl_idname      = "VIEW3D_PT_cicon_panel"
	bl_category = "Templates"
	#bl_options     = {'DEFAULT_CLOSED'}

	def draw(self, context):
		layout = self.layout
		col = layout.column(align=True)
		row = col.row(align=True)

		from myCustomIcons.icons import preview_collections
		pcoll = preview_collections["main"]
		icon = pcoll["blogo"]

		row.label("", icon_value=icon.icon_id)

and i got a folder called “icons” in the addon main folder !
It doesn´t work ! it suppoed to be easy and reliable, but i cant figure out why !
please, tell me where i am wrong… thanks a loooooot ! :slight_smile:

the error in the console:

Traceback (most recent call last):
  File "C:\Users\WIN10PRO\AppData\Roaming\Blender Foundation\Blender\2.80\scripts\addons\script custom icons\ui.py", line 21, in draw
    from myCustomIcons.icons import preview_collections
ModuleNotFoundError: No module named 'myCustomIcons'

location: <unknown location>:-1

BUT, how it doesnt know the name of the main module aka “the name of the addon” aka init?

OOOuuurraaaa ! :slight_smile: so happy to figure out what was the point !

to help others in the same case, in ui.py, i had replace:
from myCustomIcons.icons import preview_collections
by
from . import preview_collections
and it works !!!
still i am not really sure why it is like that, but maybe i am asking an unnecesary question :smiley:

i still have a few more questions regarding files structures, registering and import… but not today… i will take a break… ps, it cost me a full day to get this correction !