What's new

Add icon image from the Alibre file to the parts list

Helioc

Member
Hello,

I would like to export the list of parts from my detailing drawings, in an excel file (for example), and then import it into my management program (ERP).
When doing this import, I would like to automatically create articles corresponding to these *parts. Is it possible to create this Excel list, with the list of parts and the drawing that exists in the icon? for example the drawing that appears in the explorer file? as per image:

1736277717225.png

I would like to obtain this image from the files to later use in creating my articles, to make it easier to identify the parts at the supplier.

Thanks
Helio
 

Cator

Senior Member
Hi Helio,
Do you already have experience with scripts or do you intend to start from scratch?
could you post an example of the files to work with the script and the result you would like to obtain done manually?

If you have experience there is an option in the AlibreScript documentation as you see.



1736286396611.png
Regards,
Francesco
 

Helioc

Member
Hello,

I dont have experience with scripts, but this is possible?

I have the BOM List, i want add a column "Image":

1736328621517.png

After that i will export to csv (excel) and i want like this:

It is possible? :)
1736329005926.png
After this document *Csv I will inport to my ERP program.


Thanks
 

Attachments

  • Estrtutura.AD_PKG
    454.6 KB · Views: 7

stepalibre

Alibre Super User
Do you want an Alibre Script solution or does it matter? There are several methods, but the simplest is a command-line program.
 

Helioc

Member
Good morning,

I would really like it to be at Alibre, it would be very easy. Create the BOM list with the part data and respective image (part and assembly), then export it to Excel.
 

Helioc

Member
Good morning,
I was thinking the following, in the main assembly, I run a script in Alibre to generate "thumbnail" images of all parts and sub parts, in a folder. Then I make a BOM List of all the parts, and export this list to EXcel. So I had my excel file with the respective images. You can also later attach the images within the excel if necessary.
 

Helioc

Member
I try several times... chatgpt.. ;) always get a error...

I only want get image *jpg for all thumbnails files for a drawing, and save in the same paste of my BOM's list ;)
 

Cator

Senior Member
HI,
I'm attaching a script here that allows you to extract images from your assembly and places them in a folder. It splits the subassemblies from the parts so you can easily insert them into your excel sheet. I noticed that it doesn't generate the parts of imported stp, but I think this can already give you a hand.
I tested the direct creation of an excel file with one of my projects but since openpyxl does not work correctly in saves for several versions now in Alibre Script I had to implement dll libraries from EEplus and I don't have the experience to be able to test it on other systems so I I leave it as a personal happy experiment. If it works I'm happy, otherwise I can help you or there will surely be others more knowledgeable than me. Use it first on small assemblies and then see if it satisfies your requests.
Greetings
Francesco
Python:
# ------------------------------------------------------------
# Assembly and Part Thumbnail Generator
# ------------------------------------------------------------
# This script processes an assembly, counts identical parts,
# and saves thumbnails for parts and subassemblies.
# It also prints a consolidated list of parts with quantities.
# ------------------------------------------------------------

from collections import defaultdict
import re
import os

# Initialize Windows object and retrieve options
Win = Windows()
Option = [
    ['Thumbnail Size', WindowsInputTypes.Real, 100],
    ['Save Folder', WindowsInputTypes.Folder, None],
]
Values = Win.OptionsDialog("Image from Assembly", Option, 100)

# Retrieve user input values
dimension_thumb = Values[0]
save_path = Values[1]

if Values is None:
    sys.exit()

def detect_type():
    """
    Detect if the object is an assembly or a part based on common attributes.
    """
    try:
        if hasattr(CurrentAssembly(), 'Parts'): 
            obj = CurrentAssembly()
            obj_type = 'Assembly'
        else:
            raise Exception("Invalid assembly")
    except:
        if hasattr(CurrentPart(), 'Name'): 
            obj = CurrentPart()
            obj_type = 'Part'
        else:
            raise Exception("Invalid part")
    return obj, obj_type

def normalize_part_name(part_name):
    """
    Remove unique identifiers from part names.
    """
    return re.sub(r"<\d+>", "", part_name)

def clean_file_name(name):
    """
    Remove invalid characters for file names.
    """
    invalid_chars = r'<>:"/\|?*'
    return re.sub('[{}]'.format(re.escape(invalid_chars)), '_', name)

def ListPartsInAssembly(assembly, save_path):
    """
    Explore the assembly and subassemblies, group identical parts,
    count quantities, and save thumbnails for parts and subassemblies.
    """
    parts_count = defaultdict(int)  # Dictionary to count part quantities

    def process_assembly(assembly):
        """
        Process an assembly or subassembly.
        """
        for part in assembly.Parts:
            part_name = part.Name if hasattr(part, 'Name') else str(part)
            normalized_name = normalize_part_name(part_name)
            parts_count[normalized_name] += 1
            cleaned_name = clean_file_name(normalized_name)
            part.SaveThumbnail(os.path.join(save_path, cleaned_name + '.jpg'), dimension_thumb, dimension_thumb)
        
        for sub_assembly in assembly.SubAssemblies:
            sub_assembly_name = sub_assembly.Name if hasattr(sub_assembly, 'Name') else str(sub_assembly)
            cleaned_name = clean_file_name(sub_assembly_name)
            sub_assembly.SaveThumbnail(os.path.join(save_path, cleaned_name + '.jpg'), dimension_thumb, dimension_thumb)
            process_assembly(sub_assembly)

    # Process main assembly
    process_assembly(assembly)

    # Print part list with quantities
    print("\nConsolidated Part List:")
    for part, quantity in parts_count.items():
        print("- {}: {}".format(part, quantity))

# Execute the script
obj, obj_type = detect_type()
ListPartsInAssembly(obj, save_path)
 

stepalibre

Alibre Super User
Two heads are always better than one!
implement dll libraries
Same here.

I used your code and AI to add the Excel image insert/import step. I'll share my Syncfusion script when I have a break.


Python:
# ------------------------------------------------------------
# Assembly and Part Thumbnail Generator (IronPython 2.7)
# ------------------------------------------------------------
# This script:
#   1. Prompts the user for thumbnail size and output folder.
#   2. Detects whether the current document is an Assembly or Part.
#   3. Explores the assembly/subassemblies to:
#       - Count identical parts (group by normalized name).
#       - Save part/subassembly thumbnails as .jpg.
#   4. Creates an Excel workbook (Images_NET.xlsx) that embeds
#      all images found in the chosen folder, placing each image
#      inside a cell sized 300×300 pixels (approx).
# ------------------------------------------------------------

import clr
import sys
import re
import os
from collections import defaultdict

# Add references for .NET interop with Excel
clr.AddReference("System")
clr.AddReference("Microsoft.Office.Interop.Excel")

from Microsoft.Office.Interop import Excel

# Minimal MsoTriState "enum" to avoid referencing Microsoft.Office.Core
class MsoTriState:
    msoFalse = 0
    msoTrue = -1

# ------------------------------------------------------------
# STEP 1: Prompt user for inputs (thumbnail size + save folder)
# ------------------------------------------------------------

Win = Windows()  # Provided by your CAD/automation environment
Option = [
    ['Thumbnail Size', WindowsInputTypes.Real, 100],
    ['Save Folder',   WindowsInputTypes.Folder, None],
]
Values = Win.OptionsDialog("Image from Assembly", Option, 100)

if Values is None:
    sys.exit()

dimension_thumb = Values[0]
save_path = Values[1]

# ------------------------------------------------------------
# STEP 2: Detect if we have an Assembly or a Part
# ------------------------------------------------------------

def detect_type():
    """
    Detect if the object is an assembly or a part based on common attributes.
    """
    try:
        if hasattr(CurrentAssembly(), 'Parts'):
            obj = CurrentAssembly()
            obj_type = 'Assembly'
        else:
            raise Exception("Invalid assembly")
    except:
        if hasattr(CurrentPart(), 'Name'):
            obj = CurrentPart()
            obj_type = 'Part'
        else:
            raise Exception("Invalid part")
    return obj, obj_type

# ------------------------------------------------------------
# STEP 3: Generate thumbnails for each Part/Subassembly
# ------------------------------------------------------------

def normalize_part_name(part_name):
    """
    Remove unique identifiers like <1>, <2>, etc. from part names.
    """
    return re.sub(r"<\d+>", "", part_name)

def clean_file_name(name):
    """
    Remove invalid characters for Windows file names.
    """
    invalid_chars = r'<>:"/\|?*'
    return re.sub('[{}]'.format(re.escape(invalid_chars)), '_', name)

def ListPartsInAssembly(assembly, save_path):
    """
    Explore the assembly (and subassemblies), group identical parts,
    count quantities, and save thumbnails for parts/subassemblies.
    """
    parts_count = defaultdict(int)

    def process_assembly(asm):
        for part in asm.Parts:
            part_name = part.Name if hasattr(part, 'Name') else str(part)
            normalized_name = normalize_part_name(part_name)
            parts_count[normalized_name] += 1

            cleaned_name = clean_file_name(normalized_name)
            part.SaveThumbnail(
                os.path.join(save_path, cleaned_name + '.jpg'),
                dimension_thumb,
                dimension_thumb
            )

        for sub_asm in asm.SubAssemblies:
            sub_asm_name = sub_asm.Name if hasattr(sub_asm, 'Name') else str(sub_asm)
            cleaned_name = clean_file_name(sub_asm_name)
            sub_asm.SaveThumbnail(
                os.path.join(save_path, cleaned_name + '.jpg'),
                dimension_thumb,
                dimension_thumb
            )
            # Recurse into subassembly
            process_assembly(sub_asm)

    process_assembly(assembly)

    # Print consolidated part list
    print("\nConsolidated Part List:")
    for part, quantity in parts_count.items():
        print("- {}: {}".format(part, quantity))

# ------------------------------------------------------------
# STEP 4: Create Excel workbook and embed images with 300×300 cells
# ------------------------------------------------------------

def GenerateExcelWithImagesNET(image_directory):
    """
    Creates an Excel workbook that lists all images in `image_directory`
    in two columns:
        1. Image Name
        2. Embedded Image (300×300 px, approx)
    """
    # Start Excel
    excel = Excel.ApplicationClass()
    excel.Visible = False  # Change to True if you want to see Excel UI

    # Create a new Workbook (which has one blank worksheet by default)
    workbook = excel.Workbooks.Add()
    sheet = workbook.Worksheets[1]

    # Header cells
    sheet.Cells[1, 1].Value2 = "Image Name"
    sheet.Cells[1, 2].Value2 = "Image"

    # (Optional) freeze the header row or format as needed
    # sheet.Range("A1:B1").Font.Bold = True

    # Convert 300 px to points (approx: 1 px ≈ 0.75 pt at 96 DPI)
    # 300 px * (72 / 96) = 225 pt
    cell_size_points = 225

    # Set entire column B's width so it can hold ~300 px
    # ~ 42.5 characters in standard Excel measure ≈ 300 px
    sheet.Columns("B").ColumnWidth = 42.5

    row = 2
    valid_extensions = ('.jpg', '.jpeg', '.png', '.bmp', '.gif')

    for filename in os.listdir(image_directory):
        if filename.lower().endswith(valid_extensions):
            image_path = os.path.join(image_directory, filename)

            # Write file name in column A
            sheet.Cells[row, 1].Value2 = filename

            # Set the row height to match 300 px (~225 points)
            sheet.Rows(row).RowHeight = cell_size_points

            # Calculate the cell's top-left in points
            left = sheet.Cells(row, 2).Left
            top = sheet.Cells(row, 2).Top

            # Insert the image as a shape that fits ~300×300 px
            picture = sheet.Shapes.AddPicture(
                Filename=image_path,
                LinkToFile=MsoTriState.msoFalse,      # Do not link to file
                SaveWithDocument=MsoTriState.msoTrue, # Embed in the workbook
                Left=left,
                Top=top,
                Width=cell_size_points,
                Height=cell_size_points
            )

            row += 1

    # Auto-fit Column A for better readability
    sheet.Columns("A").AutoFit()

    # Save workbook in the same folder
    excel_file_path = os.path.join(image_directory, "Images_NET.xlsx")
    workbook.SaveAs(excel_file_path)
    workbook.Close(False)
    excel.Quit()

    print("\nExcel file with images created at: {}".format(excel_file_path))

# ------------------------------------------------------------
# MAIN EXECUTION
# ------------------------------------------------------------

def Main():
    obj, obj_type = detect_type()

    if obj_type == 'Part':
        # Generate a single thumbnail for this part
        print("Current document is a Part; generating thumbnail.")
        cleaned_name = clean_file_name(obj.Name)
        obj.SaveThumbnail(
            os.path.join(save_path, cleaned_name + '.jpg'),
            dimension_thumb,
            dimension_thumb
        )
    else:
        # Treat it as an Assembly
        print("Current document is an Assembly; generating thumbnails.")
        ListPartsInAssembly(obj, save_path)

    # Finally, create the Excel workbook with 300×300 embedded images
    GenerateExcelWithImagesNET(save_path)

Main()

1736679778900.png
 
Last edited:

Cator

Senior Member
Hi @stepalibre ,
you did well to implement this, it could be further improved by inserting a series of columns through an initial selection in which all the data that interests us are inserted. It should contain all standard properties plus custom properties.
The code you posted here on the forum doesn't work while the one in the link you attached works. I advise you to fix that on the forum in order to leave a reliable document.
Greetings,
Francesco
 

stepalibre

Alibre Super User
That's the main module function not supported in Alibre Scirpt addon IronPython deleting if __name__ == '__main__': and fix the indentation so main() gets called. It is removed in my tools but I posted too soon, it hadn't been processed at the time.

Python:
if __name__ == '__main__':
    Main()
Python:
Main()

Yes it can definitely be extended.
 

Helioc

Member
Good morning,

Thank you very much, this works beautifully ;)
Just one more question, is it possible to change the code so that it doesn't duplicate the pieces? ;)

Thank you very much again ;)
 

Attachments

  • imagem_2025-01-13_095409005.png
    imagem_2025-01-13_095409005.png
    18.3 KB · Views: 11

stepalibre

Alibre Super User
Yes. I would need to understand how it handles subassemblies or quantities. I didn't consider anything pass core task.

Can you outline all features and requirements you need in a detailed list, so I can make instructions for AI.
 
Top