I have a python module for Defold that I use quite frequently when I do batch changes. Removing all layouts, scaling all particle effects, cleaning up gui scenes, generally finding out information and such. Available here if anyone want to check it out https://deftree.readthedocs.io
It works but consider it a beta, as I will highly likely make breaking change to the API in the future (but will keep it versioned properly of course).
You can find the source on
Examples
Here are some examples, updated for 1.0.2
You have removed a lot of images from disk that you know are obsolete and now you need to clean up all atlases.
Remove unused images in atlases
import os
import deftree
def get_files(project_root, ending):
    # Generator to get files
    for root, folders, files in os.walk(project_root):
        for f in files:
            if f.endswith(ending):
                yield os.path.join(root, f)
def get_images(df_object):
    """get all images paths in a given DefTree Element instance"""
    for x in df_object.elements():
        if deftree._is_element(x):
            get_images(x)
        else:
            yield x
def missing_images_in_atlas(project_root, atlas):
    """get images in an atlas that does not exist on disk"""
    tree = deftree.DefTree()
    root = tree.parse(atlas)
    for image in get_images(root):
        if not os.path.exists(os.path.join(project_root, image[1:])):
            yield image
def clean_up_atlas(project_root, atlas):
    """Remove all missing image in entries in the atlas"""
    for missing in missing_images_in_atlas(project_root, atlas):
        tree = deftree.parse(atlas)
        root = tree.get_root()
        for img in get_images(root):
            if img == missing:
                root.remove(img.get_parent())
        tree.write(atlas)
def verify(project_folder):
    """Print images that are missing on disk"""
    for atlas in get_files(project_folder, ".atlas"):
        for missing in missing_images_in_atlas(project_folder, atlas):
            print("{}{}{}".format(atlas.replace(project_folder, ""),
                                  (50 - len(atlas.replace(project_folder, ""))) * " ",
                                  missing))
def clean_up_all_atlases(project_folder):
    """Remove all missing image in entries in the project"""
    for atlas in get_files(project_folder, ".atlas"):
        clean_up_atlas(project_folder, atlas)
You thought you wanted layouts when you started but now you have changed your mind.
Remove Layouts
import deftree
import os
def get_files(project_root, ending):
    # Generator to get files
    for root, folders, files in os.walk(project_root):
        for f in files:
            if f.endswith(ending):
                yield os.path.join(root, f)
def main(project):
    errors = list()
    for gui in get_files(project, ".gui"):
        # Create our tree
        tree = deftree.parse(gui)
        root = tree.get_root()
        rem = False
        # Find all layouts
        for layout in root.iter_elements("layouts"):
            # For error reporting
            errors.append("{}".format(gui))
            rem = True
            # Remove the child layout from the root
            root.remove(layout)
        # If we removed a layout rewrite the file
        if rem:
            tree.write(gui)
    # Report our errors
    if errors:
        [print(x) for x in errors]
You for some reason want to sort your atlases
Sort Atlases
import os
import deftree
def sort_atlas(atlas):
    tree = deftree.parse(atlas)
    root = tree.get_root()
    org = deftree.to_string(root)
    new_tree = deftree.DefTree()
    new_root = new_tree.get_root()
    _atlas = []
    _animations = []
    for at in root.iter_elements("images"):
        _atlas.append(at.get_attribute("image").value)
    for ani in root.iter_elements("animations"):
        _animations.append(ani)
    # Sort the images
    for a in sorted(_atlas, key=lambda x: x.split("/")[-1]):
        ele = new_root.add_element("images")
        ele.add_attribute("image", a)
    # Add the animations
    for animation in _animations:
        new_root.append(animation)
    # Add the atlas attributes. Margin, extrude
    for at in root.attributes():
        if not at.name == "image":
            new_root.append(at)
    new_tree.write(atlas)
    if not deftree.validate(org, deftree.to_string(new_root)):
        print("Sorted: {}".format(atlas))
You noticed that without extrude 2 on your altases you get bleeding
Batch Edit Atlas Property
import os
import deftree
def get_files(project_root, ending):
    # Generator to get files
    for root, folders, files in os.walk(project_root):
        for f in files:
            if f.endswith(ending):
                yield os.path.join(root, f)
def set_atlas_extrude(project_folder, value):
    for atlas in get_files(project_folder, ".atlas"):
        tree = deftree.parse(atlas)
        root = tree.get_root()
        root.set_attribute("extrude_borders", value)
        tree.write(atlas)
It is made for python 3.3 or newer and is easiest installed with pip install deftree



