<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.opensourceecology.org/index.php?action=history&amp;feed=atom&amp;title=Cut_List_Macro_in_FreeCAD</id>
	<title>Cut List Macro in FreeCAD - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.opensourceecology.org/index.php?action=history&amp;feed=atom&amp;title=Cut_List_Macro_in_FreeCAD"/>
	<link rel="alternate" type="text/html" href="https://wiki.opensourceecology.org/index.php?title=Cut_List_Macro_in_FreeCAD&amp;action=history"/>
	<updated>2026-04-20T17:34:26Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.39.13</generator>
	<entry>
		<id>https://wiki.opensourceecology.org/index.php?title=Cut_List_Macro_in_FreeCAD&amp;diff=314323&amp;oldid=prev</id>
		<title>Marcin: Created page with &quot;import FreeCAD  import FreeCADGui  import tempfile  import webbrowser  import os     def print_master_checklist():      doc = FreeCAD.ActiveDocument      if not doc:          FreeCAD.Console.PrintError(&quot;No active document found.\n&quot;)          return         selection = FreeCADGui.Selection.getSelection()      # Even if nothing is selected, we will try to generate the page      # so you can see that the script is working.         # --- HTML Header ---      html = &quot;&quot;&quot;...&quot;</title>
		<link rel="alternate" type="text/html" href="https://wiki.opensourceecology.org/index.php?title=Cut_List_Macro_in_FreeCAD&amp;diff=314323&amp;oldid=prev"/>
		<updated>2025-11-18T15:24:53Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;import FreeCAD  import FreeCADGui  import tempfile  import webbrowser  import os     def print_master_checklist():      doc = FreeCAD.ActiveDocument      if not doc:          FreeCAD.Console.PrintError(&amp;quot;No active document found.\n&amp;quot;)          return         selection = FreeCADGui.Selection.getSelection()      # Even if nothing is selected, we will try to generate the page      # so you can see that the script is working.         # --- HTML Header ---      html = &amp;quot;&amp;quot;&amp;quot;...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;import FreeCAD&lt;br /&gt;
&lt;br /&gt;
import FreeCADGui&lt;br /&gt;
&lt;br /&gt;
import tempfile&lt;br /&gt;
&lt;br /&gt;
import webbrowser&lt;br /&gt;
&lt;br /&gt;
import os&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
def print_master_checklist():&lt;br /&gt;
&lt;br /&gt;
    doc = FreeCAD.ActiveDocument&lt;br /&gt;
&lt;br /&gt;
    if not doc:&lt;br /&gt;
&lt;br /&gt;
        FreeCAD.Console.PrintError(&amp;quot;No active document found.\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        return&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    selection = FreeCADGui.Selection.getSelection()&lt;br /&gt;
&lt;br /&gt;
    # Even if nothing is selected, we will try to generate the page&lt;br /&gt;
&lt;br /&gt;
    # so you can see that the script is working.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    # --- HTML Header ---&lt;br /&gt;
&lt;br /&gt;
    html = &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;!DOCTYPE html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        &amp;lt;title&amp;gt;Shop Master List&amp;lt;/title&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        &amp;lt;style&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            body { font-family: sans-serif; padding: 20px; }&lt;br /&gt;
&lt;br /&gt;
            h1 { border-bottom: 2px solid #333; padding-bottom: 10px; margin-bottom: 5px;}&lt;br /&gt;
&lt;br /&gt;
            h2 { margin-top: 20px; background-color: #eee; padding: 5px; border-bottom: 1px solid #aaa;}&lt;br /&gt;
&lt;br /&gt;
            table { width: 100%; border-collapse: collapse; margin-top: 10px; }&lt;br /&gt;
&lt;br /&gt;
            th { background-color: #f0f0f0; text-align: left; border: 1px solid #333; padding: 8px; }&lt;br /&gt;
&lt;br /&gt;
            td { border: 1px solid #333; padding: 8px; }&lt;br /&gt;
&lt;br /&gt;
            .dim { font-family: monospace; font-weight: bold; white-space: nowrap; font-size: 1.1em;}&lt;br /&gt;
&lt;br /&gt;
            .checkbox { width: 50px; text-align: center; font-size: 20px; }&lt;br /&gt;
&lt;br /&gt;
            .warning { color: red; font-weight: bold; }&lt;br /&gt;
&lt;br /&gt;
            @media print { .no-print { display: none; } body { padding: 0; } }&lt;br /&gt;
&lt;br /&gt;
        &amp;lt;/style&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;/head&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;body onload=&amp;quot;window.print()&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        &amp;lt;h1&amp;gt;Shop Master List&amp;lt;/h1&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        &amp;lt;p&amp;gt;&amp;lt;b&amp;gt;Project:&amp;lt;/b&amp;gt; &amp;quot;&amp;quot;&amp;quot; + doc.Label + &amp;quot;&amp;quot;&amp;quot;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        &amp;lt;b&amp;gt;Date:&amp;lt;/b&amp;gt; &amp;lt;script&amp;gt;document.write(new Date().toLocaleDateString());&amp;lt;/script&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    # Lists to hold our data&lt;br /&gt;
&lt;br /&gt;
    part_rows = &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    measure_rows = &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    part_count = 0&lt;br /&gt;
&lt;br /&gt;
    measure_count = 0&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    # --- Helper to process objects ---&lt;br /&gt;
&lt;br /&gt;
    if selection:&lt;br /&gt;
&lt;br /&gt;
        items_to_process = list(selection)&lt;br /&gt;
&lt;br /&gt;
        processed_ids = set()&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
        while items_to_process:&lt;br /&gt;
&lt;br /&gt;
            obj = items_to_process.pop(0)&lt;br /&gt;
&lt;br /&gt;
           &lt;br /&gt;
&lt;br /&gt;
            # Avoid infinite loops or duplicates&lt;br /&gt;
&lt;br /&gt;
            if obj.Name in processed_ids:&lt;br /&gt;
&lt;br /&gt;
                continue&lt;br /&gt;
&lt;br /&gt;
            processed_ids.add(obj.Name)&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
            # 1. If it&amp;#039;s a Folder/Group, add its contents to the queue&lt;br /&gt;
&lt;br /&gt;
            if hasattr(obj, &amp;quot;Group&amp;quot;) and obj.Group:&lt;br /&gt;
&lt;br /&gt;
                items_to_process.extend(obj.Group)&lt;br /&gt;
&lt;br /&gt;
                # We don&amp;#039;t &amp;#039;continue&amp;#039; here because a group might ALSO have a shape (rare but possible)&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
            # 2. Check for Measurement (Distance)&lt;br /&gt;
&lt;br /&gt;
            is_measurement = False&lt;br /&gt;
&lt;br /&gt;
            # Check for standard Measure tool or Draft Dimensions&lt;br /&gt;
&lt;br /&gt;
            if hasattr(obj, &amp;quot;Distance&amp;quot;) or hasattr(obj, &amp;quot;Length&amp;quot;):&lt;br /&gt;
&lt;br /&gt;
                # Distinguish between a Part that happens to have a Length (like a Cube)&lt;br /&gt;
&lt;br /&gt;
                # and a Measurement object.&lt;br /&gt;
&lt;br /&gt;
                # Measurement objects usually don&amp;#039;t have a &amp;quot;Shape&amp;quot; that is a solid.&lt;br /&gt;
&lt;br /&gt;
                if not hasattr(obj, &amp;quot;Shape&amp;quot;) or obj.Shape.Volume &amp;lt; 1e-6:&lt;br /&gt;
&lt;br /&gt;
                    val = 0.0&lt;br /&gt;
&lt;br /&gt;
                    try:&lt;br /&gt;
&lt;br /&gt;
                        if hasattr(obj, &amp;quot;Distance&amp;quot;):&lt;br /&gt;
&lt;br /&gt;
                            # Handle wrapped values vs direct floats&lt;br /&gt;
&lt;br /&gt;
                            val = obj.Distance.Value if hasattr(obj.Distance, &amp;quot;Value&amp;quot;) else obj.Distance&lt;br /&gt;
&lt;br /&gt;
                        elif hasattr(obj, &amp;quot;Length&amp;quot;):&lt;br /&gt;
&lt;br /&gt;
                            val = obj.Length.Value if hasattr(obj.Length, &amp;quot;Value&amp;quot;) else obj.Length&lt;br /&gt;
&lt;br /&gt;
                       &lt;br /&gt;
&lt;br /&gt;
                        # Only add if we got a valid number&lt;br /&gt;
&lt;br /&gt;
                        if isinstance(val, (int, float)):&lt;br /&gt;
&lt;br /&gt;
                            val_str = FreeCAD.Units.Quantity(val, &amp;#039;mm&amp;#039;).UserString&lt;br /&gt;
&lt;br /&gt;
                            measure_rows += f&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
                                &amp;lt;tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                                    &amp;lt;td&amp;gt;{obj.Label}&amp;lt;/td&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                                    &amp;lt;td class=&amp;quot;dim&amp;quot;&amp;gt;{val_str}&amp;lt;/td&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                                    &amp;lt;td class=&amp;quot;checkbox&amp;quot;&amp;gt;&amp;amp;#9744;&amp;lt;/td&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                                &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                            &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
                            measure_count += 1&lt;br /&gt;
&lt;br /&gt;
                            is_measurement = True&lt;br /&gt;
&lt;br /&gt;
                    except:&lt;br /&gt;
&lt;br /&gt;
                        pass # Skip if calculation fails&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
            # 3. Check for 3D Part (Cutlist) - Only if it wasn&amp;#039;t a measurement&lt;br /&gt;
&lt;br /&gt;
            if not is_measurement and hasattr(obj, &amp;#039;Shape&amp;#039;) and not obj.Shape.isNull():&lt;br /&gt;
&lt;br /&gt;
                # We filter out things with 0 volume (like lines or planes) for the Cutlist&lt;br /&gt;
&lt;br /&gt;
                if obj.Shape.Volume &amp;gt; 0:&lt;br /&gt;
&lt;br /&gt;
                    bbox = obj.Shape.BoundBox&lt;br /&gt;
&lt;br /&gt;
                    raw_dims = sorted([bbox.XLength, bbox.YLength, bbox.ZLength], reverse=True)&lt;br /&gt;
&lt;br /&gt;
                   &lt;br /&gt;
&lt;br /&gt;
                    L_str = FreeCAD.Units.Quantity(raw_dims[0], &amp;#039;mm&amp;#039;).UserString&lt;br /&gt;
&lt;br /&gt;
                    W_str = FreeCAD.Units.Quantity(raw_dims[1], &amp;#039;mm&amp;#039;).UserString&lt;br /&gt;
&lt;br /&gt;
                    T_str = FreeCAD.Units.Quantity(raw_dims[2], &amp;#039;mm&amp;#039;).UserString&lt;br /&gt;
&lt;br /&gt;
                   &lt;br /&gt;
&lt;br /&gt;
                    part_rows += f&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
                        &amp;lt;tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                            &amp;lt;td&amp;gt;{obj.Label}&amp;lt;/td&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                            &amp;lt;td class=&amp;quot;dim&amp;quot;&amp;gt;{L_str}&amp;lt;/td&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                            &amp;lt;td class=&amp;quot;dim&amp;quot;&amp;gt;{W_str}&amp;lt;/td&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                            &amp;lt;td class=&amp;quot;dim&amp;quot;&amp;gt;{T_str}&amp;lt;/td&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                            &amp;lt;td class=&amp;quot;checkbox&amp;quot;&amp;gt;&amp;amp;#9744;&amp;lt;/td&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                        &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
                    part_count += 1&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    # --- Build the HTML Tables ---&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    if part_count &amp;gt; 0:&lt;br /&gt;
&lt;br /&gt;
        html += &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
        &amp;lt;h2&amp;gt;Material Cut List&amp;lt;/h2&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        &amp;lt;table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;thead&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                &amp;lt;tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                    &amp;lt;th&amp;gt;Part Name&amp;lt;/th&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                    &amp;lt;th&amp;gt;Length&amp;lt;/th&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                    &amp;lt;th&amp;gt;Width&amp;lt;/th&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                    &amp;lt;th&amp;gt;Thickness&amp;lt;/th&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                    &amp;lt;th class=&amp;quot;checkbox&amp;quot;&amp;gt;Cut&amp;lt;/th&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;/thead&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;tbody&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; + part_rows + &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;/tbody&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        &amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    if measure_count &amp;gt; 0:&lt;br /&gt;
&lt;br /&gt;
        html += &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
        &amp;lt;h2&amp;gt;Critical Measurements&amp;lt;/h2&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        &amp;lt;table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;thead&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                &amp;lt;tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                    &amp;lt;th&amp;gt;Measurement Name&amp;lt;/th&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                    &amp;lt;th&amp;gt;Value&amp;lt;/th&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                    &amp;lt;th class=&amp;quot;checkbox&amp;quot;&amp;gt;Check&amp;lt;/th&amp;gt;&lt;br /&gt;
&lt;br /&gt;
                &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;/thead&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;tbody&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot; + measure_rows + &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
            &amp;lt;/tbody&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        &amp;lt;/table&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    # If nothing found, show a friendly message in the HTML instead of crashing&lt;br /&gt;
&lt;br /&gt;
    if part_count == 0 and measure_count == 0:&lt;br /&gt;
&lt;br /&gt;
        html += &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
        &amp;lt;h2 style=&amp;quot;color:red;&amp;quot;&amp;gt;No Parts Selected&amp;lt;/h2&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        &amp;lt;p&amp;gt;Please select your &amp;lt;b&amp;gt;3D Parts&amp;lt;/b&amp;gt; or &amp;lt;b&amp;gt;Measurement Objects&amp;lt;/b&amp;gt; in the FreeCAD tree before running this macro.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    html += &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;/body&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;/html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    # --- Launch Browser ---&lt;br /&gt;
&lt;br /&gt;
    try:&lt;br /&gt;
&lt;br /&gt;
        fd, path = tempfile.mkstemp(suffix=&amp;quot;.html&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        with os.fdopen(fd, &amp;#039;w&amp;#039;) as tmp:&lt;br /&gt;
&lt;br /&gt;
            tmp.write(html)&lt;br /&gt;
&lt;br /&gt;
        webbrowser.open(&amp;#039;file://&amp;#039; + path)&lt;br /&gt;
&lt;br /&gt;
        print(&amp;quot;HTML Page generated.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    except Exception as e:&lt;br /&gt;
&lt;br /&gt;
        FreeCAD.Console.PrintError(f&amp;quot;Error creating print file: {e}\n&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
    print_master_checklist()&lt;/div&gt;</summary>
		<author><name>Marcin</name></author>
	</entry>
</feed>