cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
Showing results for 
Search instead for 
Did you mean: 

Learn to manage BIM workflows and create professional Archicad templates with the BIM Manager Program.

Archicad Python API
About automating tasks in Archicad using the Python API.

Python Error with GenerateLayoutBookfromExcel.py

ares997
Contributor
Does anyone know why when I run the GenerateLayoutBookFromExcel.py script with the Python plug-in I get this error:
Bad Name: A1 Landscape
GenerateLayoutBookFromExcel.py(108): AttributeError: 'NoneType' object has no attribute 'db'

Below is the Python script:
import os
import sys
import re
import xlrd

excelFileName = "LayoutBook.xls"
excelFilePath = os.path.join (os.path.dirname (sys.argv[0]), excelFileName)

if os.path.isfile (excelFilePath) is False:
	sys.exit ("File does not exists: {}".format (excelFilePath))

xl_workbook = xlrd.open_workbook(excelFilePath)
xl_sheet = xl_workbook.sheet_by_index(0)

layoutTree = GetNavigatorTree (API_LayoutMap)
subSetTree = [(list(layoutTree)[0], 0)]

def GetItemsFromTree (tree, typeSet = None):
	l = []
	for k in tree.keys():
		if typeSet is None or k.itemType in typeSet:
			l.append (k)
		l.extend (GetItemsFromTree (tree, typeSet))
	return l

masters = GetItemsFromTree (layoutTree, {API_MasterLayoutNavItem})

def GetChildrenFromTree (tree, subSetTree):
	if not subSetTree:
		return list(tree.keys())
	for k in tree.keys():
		if k.name == subSetTree[0][0].name and k.uiId == subSetTree[0][0].uiId:
			return GetChildrenFromTree (tree, subSetTree[1:])
	return []

def FindItemByName (items, name):
	for item in items:
		if item.name == name:
			return item
	PrintError('Bad Name: {}'.format(name))
	return None

def FindItemByIDName (items, ID, name):
	for item in items:
		if item.name == name and item.uiId == ID:
			return item
	return None

def CreateSubSetItem (ID, name, parent):
	newSubSet = APIObject ()
	newSubSet.uiId = ID
	newSubSet.customUiId = True
	newSubSet.name = name
	newSubSet.customName = True
	newSubSet.itemType = API_SubSetNavItem
	newSubSet.mapId = API_LayoutMap
	CreateNavigatorItem (newSubSet, parent)
	return newSubSet

def CreateOrChangeLayoutItem (ID, name, masterName, parent, oldItem = None):
	newLayout = APIObject () if oldItem is None else oldItem
	newLayout.uiId = ID
	newLayout.customUiId = True
	newLayout.name = name
	newLayout.customName = True
	newLayout.itemType = API_LayoutNavItem
	newLayout.mapId = API_LayoutMap
	newLayout.db = APIObject ()
	newLayout.db.typeID = APIWind_LayoutID
	masterLayoutItem = FindItemByName (masters, masterName)
	newLayout.db.masterLayoutUnId = masterLayoutItem.db.databaseUnId
	if oldItem is None:
		parentChildren = GetChildrenFromTree (layoutTree, subSetTree)
		if not parentChildren:
			CreateNavigatorItem (newLayout, parent)
		else:
			CreateNavigatorItem (newLayout, parent, parentChildren[-1])
	else:
		ChangeNavigatorItem (newLayout)
	return newLayout

def GetParent (actIndex):
	global subSetTree
	for i in reversed(range(1,len(subSetTree))):
		if subSetTree[1] >= actIndex:
			del subSetTree
	return subSetTree[-1][0]

newSubSetNumber, newLayoutNumber = 0, 0
for row_idx in range(1, xl_sheet.nrows):
	for column_idx in range(1, xl_sheet.ncols):
		idCell = xl_sheet.cell(row_idx, column_idx)
		if idCell.value != '':
			ID = idCell.value.strip()
			NAME = xl_sheet.cell(row_idx, column_idx + 1).value.strip()
			MASTER_NAME = xl_sheet.cell(row_idx, column_idx + 3).value.strip()

			PARENT = GetParent(column_idx)
			oldItem = FindItemByIDName (GetChildrenFromTree (layoutTree, subSetTree), ID, NAME)
			if MASTER_NAME == '':
				newSubSetNumber += 1
				if oldItem is None:
					subSetTree.append((CreateSubSetItem (ID, NAME, PARENT), column_idx))
				else:
					subSetTree.append((oldItem, column_idx))
			else:
				newLayoutNumber += 1
				CreateOrChangeLayoutItem (ID, NAME, MASTER_NAME, PARENT, oldItem)

			layoutTree = GetNavigatorTree (API_LayoutMap)
			break
print("Created {} subsets and {} layouts based on {}".format(newSubSetNumber, newLayoutNumber, excelFileName))
Archicad 25 (5005), Windows 11, AMD RYZEN 7 3900 (64 GB RAM)
1 REPLY 1
ares997
Contributor
Figured it out.

Just had to have a Master Layout named correctly "A1 Landscape".
Archicad 25 (5005), Windows 11, AMD RYZEN 7 3900 (64 GB RAM)