Examples

The source for all examples lives in packages/ducpy/src/examples/.


Element Creation

Demonstrates building rectangles, ellipses, polygons, lines, arrows, text, frames and plots using the fluent builder DSL.

  1#!/usr/bin/env python3
  2"""
  3Example demonstrating the element creation functionality using the builders API.
  4This demo shows how to create various types of elements using the modern builders pattern.
  5"""
  6
  7import ducpy as duc
  8from ducpy.builders.style_builders import (create_fill_and_stroke_style,
  9                                           create_simple_styles,
 10                                           create_solid_content)
 11
 12
 13def demo_basic_elements():
 14    """Demo basic elements using the builders API."""
 15    print("=== Basic Elements Demo ===")
 16    
 17    # Create basic shapes with styles
 18    rect = (duc.ElementBuilder()
 19        .at_position(0, 0)
 20        .with_size(100, 50)
 21        .with_label("Sample Rectangle")
 22        .with_styles(create_fill_and_stroke_style(
 23            fill_content=create_solid_content("#FF6B6B"),
 24            stroke_content=create_solid_content("#2C3E50"),
 25            stroke_width=2.0,
 26            roundness=5.0
 27        ))
 28        .build_rectangle()
 29        .build())
 30    
 31    ellipse = (duc.ElementBuilder()
 32        .at_position(120, 0)
 33        .with_size(60, 40)
 34        .with_label("Sample Ellipse")
 35        .with_styles(create_fill_and_stroke_style(
 36            fill_content=create_solid_content("#4ECDC4"),
 37            stroke_content=create_solid_content("#34495E"),
 38            stroke_width=1.5
 39        ))
 40        .build_ellipse()
 41        .build())
 42    
 43    poly = (duc.ElementBuilder()
 44        .at_position(200, 0)
 45        .with_size(50, 50)
 46        .with_label("Hexagon")
 47        .with_styles(create_fill_and_stroke_style(
 48            fill_content=create_solid_content("#45B7D1"),
 49            stroke_content=create_solid_content("#2C3E50"),
 50            stroke_width=1.0,
 51            roundness=0.0
 52        ))
 53        .build_polygon()
 54        .with_sides(6)
 55        .build())
 56    
 57    print(f"Rectangle ID: {rect.element.base.id}")
 58    print(f"Ellipse ID: {ellipse.element.base.id}")  
 59    print(f"Polygon sides: {poly.element.sides}")
 60    
 61    # Demonstrate mutation with random versioning
 62    original_version = rect.element.base.version
 63    duc.mutate_element(rect, x=10, label="Moved Rectangle")
 64    print(f"Version changed: {original_version} -> {rect.element.base.version}")
 65
 66
 67def demo_linear_elements():
 68    """Demo linear and arrow elements with styles."""
 69    print("\n=== Linear Elements Demo ===")
 70    
 71    # Create a styled line
 72    line_points = [(0, 0), (50, 25), (100, 0)]
 73    line = (duc.ElementBuilder()
 74        .with_label("Sample Line")
 75        .with_styles(create_simple_styles(
 76            strokes=[duc.create_stroke(duc.create_solid_content("#E74C3C"), width=3.0)]
 77        ))
 78        .build_linear_element()
 79        .with_points(line_points)
 80        .build())
 81    print(f"Line has {len(line.element.linear_base.points)} points")
 82    
 83    # Create a styled arrow
 84    arrow_points = [(0, 50), (75, 100)]
 85    arrow = (duc.ElementBuilder()
 86        .with_label("Sample Arrow")
 87        .with_styles(create_simple_styles(
 88            strokes=[duc.create_stroke(duc.create_solid_content("#8E44AD"), width=2.5)]
 89        ))
 90        .build_arrow_element()
 91        .with_points(arrow_points)
 92        .build())
 93    print(f"Arrow element type: {type(arrow.element).__name__}")
 94
 95
 96def demo_text_elements():
 97    """Demo text elements with styles and document formatting."""
 98    print("\n=== Text Elements Demo ===")
 99    
100    text = (duc.ElementBuilder()
101        .at_position(0, 100)
102        .with_size(150, 25)
103        .with_label("Sample Text")
104        .with_styles(create_simple_styles(opacity=0.9))
105        .build_text_element()
106        .with_text("Hello, DucPy!")
107        .build())
108    print(f"Text content: '{text.element.text}'")
109    print(f"Text uses random versioning: {text.element.base.version > 0}")
110
111
112def demo_stack_elements():
113    """Demo new stack-based elements with styles."""
114    print("\n=== Stack Elements Demo ===")
115    
116    # Create a styled frame
117    frame = (duc.ElementBuilder()
118        .at_position(0, 150)
119        .with_size(200, 100)
120        .with_label("Technical Frame")
121        .with_styles(create_fill_and_stroke_style(
122            fill_content=create_solid_content("#F8F9FA"),
123            stroke_content=create_solid_content("#495057"),
124            stroke_width=2.0,
125            roundness=3.0
126        ))
127        .build_frame_element()
128        .build())
129    print(f"Frame stack label: {frame.element.stack_element_base.stack_base.label}")
130    
131    # Create a styled plot with margins
132    plot = (duc.ElementBuilder()
133        .at_position(220, 150)
134        .with_size(180, 120)
135        .with_label("Engineering Plot")
136        .with_styles(create_fill_and_stroke_style(
137            fill_content=create_solid_content("#E9ECEF"),
138            stroke_content=create_solid_content("#6C757D"),
139            stroke_width=1.5
140        ))
141        .build_plot_element()
142        .with_margins(duc.Margins(top=5, right=5, bottom=5, left=5))
143        .build())
144    print(f"Plot is marked as plot: {plot.element.stack_element_base.stack_base.is_plot}")
145    print(f"Plot margins: {plot.element.layout.margins.top}mm")
146    
147
148
149
150def demo_custom_stack_base():
151    """Demo custom stack base creation."""
152    print("\n=== Custom Stack Base Demo ===")
153    
154    # Use it in a frame
155    custom_frame = (duc.ElementBuilder()
156        .at_position(50, 280)
157        .with_size(150, 80)
158        .with_label("Custom Container") # Moved label to ElementBuilder
159        .build_frame_element()
160        .with_stack_base(duc.StateBuilder().build_stack_base()
161            .with_is_collapsed(False)
162            .with_styles(duc.DucStackLikeStyles(opacity=0.8))
163            .build())
164        .build())
165    
166    print(f"Custom stack opacity: {custom_frame.element.stack_element_base.stack_base.styles.opacity}")
167
168
169def main():
170    """Run all element creation demos."""
171    print("DucPy Element Creation Demo")
172    print("=" * 40)
173    
174    demo_basic_elements()
175    demo_linear_elements() 
176    demo_text_elements()
177    demo_stack_elements()
178    demo_custom_stack_base()
179    
180    print("\n✅ All demos completed successfully!")
181    print("The refactored code provides:")
182    print("- Reduced code duplication")  
183    print("- Consistent random versioning")
184    print("- New stack-based element support")
185    print("- Improved maintainability")
186
187
188if __name__ == "__main__":
189    main()

Mutating Elements

Shows how to update element properties in place and observe version changes.

 1"""
 2Example demonstrating the mutation of elements within a DUC object.
 3"""
 4
 5import ducpy as duc
 6from ducpy.classes.DataStateClass import ExportedDataState
 7
 8def demonstrate_element_mutation():
 9    """
10    Creates a DUC object with an element, then mutates its properties
11    and prints the changes.
12    
13    """
14    print("Demonstrating element mutation...")
15
16    # 1. Create a simple Rectangle element
17    initial_rect = (duc.ElementBuilder()
18        .at_position(0, 0).with_size(100, 50)
19        .with_label("Initial Rectangle")
20        .build_rectangle()
21        .build())
22
23    elements = [initial_rect]
24
25    # 2. Create minimal Global and Local States
26    global_state = (duc.StateBuilder().build_global_state().build())
27    local_state = (duc.StateBuilder().build_local_state().build())
28
29    print("Initial Rectangle Properties:")
30    print(f"  X: {initial_rect.element.base.x}, Y: {initial_rect.element.base.y}")
31    print(f"  Width: {initial_rect.element.base.width}, Height: {initial_rect.element.base.height}")
32    print(f"  Label: {initial_rect.element.base.label}")
33    print(f"  Version: {initial_rect.element.base.version}")
34    print()
35
36    # 4. Mutate the rectangle element
37    print("Mutating the rectangle element...")
38    duc.mutate_element(initial_rect, 
39                       x=20, 
40                       y=30, 
41                       width=150, 
42                       label="Mutated Rectangle", 
43                       is_visible=False)
44
45    print("Mutated Rectangle Properties:")
46    print(f"  X: {initial_rect.element.base.x}, Y: {initial_rect.element.base.y}")
47    print(f"  Width: {initial_rect.element.base.width}, Height: {initial_rect.element.base.height}")
48    print(f"  Label: {initial_rect.element.base.label}")
49    print(f"  Is Visible: {initial_rect.element.base.is_visible}")
50    print(f"  New Version: {initial_rect.element.base.version}") # Version should have changed
51
52    print("\nElement mutation demo complete!")
53
54def main():
55    """Run the mutation demo."""
56    print("Mutation Demo")
57    print("=" * 30)
58    demonstrate_element_mutation()
59
60
61if __name__ == "__main__":
62    main()

External Files

Attaching binary blobs (images, PDFs) to a duc document.

 1"""
 2Example demonstrating the creation and management of external files within a DUC object.
 3"""
 4
 5import ducpy as duc
 6from ducpy.classes.DataStateClass import ExportedDataState
 7
 8
 9def create_duc_with_external_files():
10    """
11    Creates a DUC object and adds multiple external file entries to it
12    using the builder pattern.
13    """
14    print("Creating a DUC object with external files...")
15
16    # Create dummy data for external files
17    dummy_image_data = b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\x0cIDATx\xda\xed\xc1\x01\x01\x00\x00\x00\xc2\xa0\xf7Om\x00\x00\x00\x00IEND\xaeB`\x82"
18    dummy_pdf_data = b"%PDF-1.4\n1 0 obj <</Type/Catalog/Pages 2 0 R>> endobj\n2 0 obj <</Type/Pages/Count 0>> endobj\nxref\n0 3\n0000000000 65535 f\n0000000009 00000 n\n0000000074 00000 n\ntrailer<</Size 3/Root 1 0 R>>startxref\n123\n%%EOF"
19
20    # 1. Create External File Entries using builders
21    image_file_entry = (duc.StateBuilder()
22        .build_external_file()
23        .with_key("my_image_key")
24        .with_mime_type("image/png")
25        .with_data(dummy_image_data)
26        .build())
27
28    pdf_file_entry = (duc.StateBuilder()
29        .build_external_file()
30        .with_key("document_123")
31        .with_mime_type("application/pdf")
32        .with_data(dummy_pdf_data)
33        .build())
34
35    # 2. Create Global State (minimal for this example)
36    global_state = (duc.StateBuilder()
37        .build_global_state()
38        .with_main_scope("mm")
39        .build())
40
41    # 3. Create Local State (minimal for this example)
42    local_state = (duc.StateBuilder()
43        .build_local_state()
44        .build())
45
46    # 4. Assemble into an ExportedDataState (DUC object)
47    duc_object = ExportedDataState(
48        type="duc_example",
49        version="1.0.0",
50        source="external_files_demo.py",
51        thumbnail=b"",
52        elements=[],
53        blocks=[],
54        block_instances=[],
55        block_collections=[],
56        groups=[],
57        regions=[],
58        layers=[],
59        dictionary={},
60        duc_local_state=local_state,
61        duc_global_state=global_state,
62        version_graph=None, # Not focusing on versioning here
63        files=[image_file_entry, pdf_file_entry]
64    )
65
66    print("DUC object with external files created successfully!")
67    print(f"Total external files: {len(duc_object.files)}")
68    print(f"First file key: {duc_object.files[0].key}, MIME type: {duc_object.files[0].value.mime_type}")
69    print(f"Second file key: {duc_object.files[1].key}, MIME type: {duc_object.files[1].value.mime_type}")
70    return duc_object
71
72def main():
73    """Run the external files demo."""
74    print("External Files Demo")
75    print("=" * 30)
76    sample_duc = create_duc_with_external_files()
77    print("\nExternal files demo complete!")
78
79
80if __name__ == "__main__":
81    main()

SQL Builder

Direct SQLite access via DucSQL. Use this when you need raw queries, bulk inserts, schema introspection, or anything beyond what the high-level builders expose.

  1#!/usr/bin/env python3
  2"""
  3Example demonstrating direct SQLite access to .duc files via DucSQL.
  4
  5A .duc file is a plain SQLite database.  DucSQL exposes the raw sqlite3
  6connection so you can run any SQL you want while handling the open/save/
  7export lifecycle for you.
  8
  9Topics covered:
 10  1. Create a new .duc file with the full schema bootstrapped
 11  2. Insert elements and style data
 12  3. Query rows back as dict-like objects
 13  4. Update elements in place
 14  5. Export to / round-trip from bytes
 15  6. Open an existing .duc file
 16"""
 17
 18import os
 19import tempfile
 20
 21from ducpy.builders.sql_builder import DucSQL
 22
 23# ---------------------------------------------------------------------------
 24# 1. Create a new .duc from scratch
 25# ---------------------------------------------------------------------------
 26
 27def demo_create_new():
 28    print("=== Create new .duc ===")
 29
 30    with DucSQL.new() as db:
 31        # Insert two elements
 32        db.sql(
 33            "INSERT INTO elements (id, element_type, x, y, width, height, label, opacity) "
 34            "VALUES (?,?,?,?,?,?,?,?)",
 35            "r1", "rectangle", 0, 0, 200, 100, "Main Rectangle", 1.0,
 36        )
 37        db.sql(
 38            "INSERT INTO elements (id, element_type, x, y, width, height, label, opacity) "
 39            "VALUES (?,?,?,?,?,?,?,?)",
 40            "e1", "ellipse", 250, 0, 120, 80, "Side Ellipse", 0.9,
 41        )
 42
 43        # Attach fill colours
 44        for owner_id, colour in [("r1", "#4ECDC4"), ("e1", "#FF6B6B")]:
 45            db.sql(
 46                "INSERT INTO backgrounds (owner_type, owner_id, src, opacity) "
 47                "VALUES (?,?,?,?)",
 48                "element", owner_id, colour, 1.0,
 49            )
 50
 51        # Verify inserts
 52        rows = db.sql("SELECT id, element_type, label FROM elements ORDER BY id")
 53        for row in rows:
 54            print(f"  [{row['id']}] {row['element_type']}{row['label']}")
 55
 56        # Use named parameters for an update
 57        db.sql_dict(
 58            "UPDATE elements SET label = :label WHERE id = :id",
 59            {"label": "Renamed Rectangle", "id": "r1"},
 60        )
 61
 62        updated = db.sql("SELECT label FROM elements WHERE id = ?", "r1")[0]
 63        print(f"  After rename: '{updated['label']}'")
 64
 65        # Save to a temporary file
 66        tmp = tempfile.NamedTemporaryFile(suffix=".duc", delete=False)
 67        tmp.close()
 68        db.save(tmp.name)
 69        print(f"  Saved to: {tmp.name}")
 70
 71    return tmp.name
 72
 73
 74# ---------------------------------------------------------------------------
 75# 2. Open an existing .duc file
 76# ---------------------------------------------------------------------------
 77
 78def demo_open_existing(path: str):
 79    print("\n=== Open existing .duc ===")
 80
 81    with DucSQL(path) as db:
 82        count = db.sql("SELECT COUNT(*) AS n FROM elements")[0]["n"]
 83        print(f"  Total elements: {count}")
 84
 85        rows = db.sql(
 86            "SELECT e.id, e.label, b.src AS colour "
 87            "FROM elements e "
 88            "LEFT JOIN backgrounds b ON b.owner_type = 'element' AND b.owner_id = e.id "
 89            "ORDER BY e.id"
 90        )
 91        for row in rows:
 92            print(f"  {row['label']} → fill: {row['colour']}")
 93
 94    os.unlink(path)
 95
 96
 97# ---------------------------------------------------------------------------
 98# 3. Round-trip via bytes (in-memory, no disk I/O needed)
 99# ---------------------------------------------------------------------------
100
101def demo_bytes_roundtrip():
102    print("\n=== Bytes round-trip ===")
103
104    # Build in memory
105    with DucSQL.new() as db:
106        db.sql(
107            "INSERT INTO elements (id, element_type, x, y, width, height, label) "
108            "VALUES (?,?,?,?,?,?,?)",
109            "t1", "text", 10, 10, 300, 40, "Hello, DUC!",
110        )
111        raw = db.to_bytes()
112
113    print(f"  Serialised to {len(raw):,} bytes")
114
115    # Restore from bytes and query
116    with DucSQL.from_bytes(raw) as db:
117        row = db.sql("SELECT label FROM elements WHERE id = 't1'")[0]
118        print(f"  Restored label: '{row['label']}'")
119
120
121# ---------------------------------------------------------------------------
122# 4. Access the underlying connection for advanced operations
123# ---------------------------------------------------------------------------
124
125def demo_advanced_connection():
126    print("\n=== Advanced: direct connection access ===")
127
128    with DucSQL.new() as db:
129        # Use executemany for bulk inserts via the raw connection
130        records = [
131            (f"el{i}", "rectangle", i * 110, 0, 100, 60, f"Box {i}", 1.0)
132            for i in range(5)
133        ]
134        db.conn.executemany(
135            "INSERT INTO elements (id, element_type, x, y, width, height, label, opacity) "
136            "VALUES (?,?,?,?,?,?,?,?)",
137            records,
138        )
139
140        total = db.sql("SELECT COUNT(*) AS n FROM elements")[0]["n"]
141        print(f"  Bulk-inserted {total} elements")
142
143        # Run a schema introspection query
144        tables = [
145            row["name"]
146            for row in db.sql(
147                "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
148            )
149        ]
150        print(f"  Schema tables: {', '.join(tables[:6])} …")
151
152
153def main():
154    print("DucSQL Builder Demo")
155    print("=" * 40)
156
157    saved_path = demo_create_new()
158    demo_open_existing(saved_path)
159    demo_bytes_roundtrip()
160    demo_advanced_connection()
161
162    print("\nAll DucSQL demos completed successfully!")
163
164
165if __name__ == "__main__":
166    main()