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    rect = (duc.ElementBuilder()
 18        .at_position(0, 0)
 19        .with_size(100, 50)
 20        .with_label("Sample Rectangle")
 21        .with_styles(create_fill_and_stroke_style(
 22            fill_content=create_solid_content("#FF6B6B"),
 23            stroke_content=create_solid_content("#2C3E50"),
 24            stroke_width=2.0,
 25            roundness=5.0
 26        ))
 27        .build_rectangle()
 28        .build())
 29
 30    ellipse = (duc.ElementBuilder()
 31        .at_position(120, 0)
 32        .with_size(60, 40)
 33        .with_label("Sample Ellipse")
 34        .with_styles(create_fill_and_stroke_style(
 35            fill_content=create_solid_content("#4ECDC4"),
 36            stroke_content=create_solid_content("#34495E"),
 37            stroke_width=1.5
 38        ))
 39        .build_ellipse()
 40        .build())
 41
 42    poly = (duc.ElementBuilder()
 43        .at_position(200, 0)
 44        .with_size(50, 50)
 45        .with_label("Hexagon")
 46        .with_styles(create_fill_and_stroke_style(
 47            fill_content=create_solid_content("#45B7D1"),
 48            stroke_content=create_solid_content("#2C3E50"),
 49            stroke_width=1.0,
 50            roundness=0.0
 51        ))
 52        .build_polygon()
 53        .with_sides(6)
 54        .build())
 55
 56    print(f"Rectangle ID: {rect.element.base.id}")
 57    print(f"Ellipse ID: {ellipse.element.base.id}")
 58    print(f"Polygon sides: {poly.element.sides}")
 59
 60    # Demonstrate mutation with random versioning
 61    duc.mutate_element(rect, x=10, label="Moved Rectangle")
 62
 63    return [rect, ellipse, poly]
 64
 65
 66def demo_linear_elements():
 67    """Demo linear and arrow elements with styles."""
 68    print("\n=== Linear Elements Demo ===")
 69
 70    line_points = [(0, 0), (50, 25), (100, 0)]
 71    line = (duc.ElementBuilder()
 72        .with_label("Sample Line")
 73        .with_styles(create_simple_styles(
 74            strokes=[duc.create_stroke(duc.create_solid_content("#E74C3C"), width=3.0)]
 75        ))
 76        .build_linear_element()
 77        .with_points(line_points)
 78        .build())
 79    print(f"Line has {len(line.element.linear_base.points)} points")
 80
 81    arrow_points = [(0, 50), (75, 100)]
 82    arrow = (duc.ElementBuilder()
 83        .with_label("Sample Arrow")
 84        .with_styles(create_simple_styles(
 85            strokes=[duc.create_stroke(duc.create_solid_content("#8E44AD"), width=2.5)]
 86        ))
 87        .build_arrow_element()
 88        .with_points(arrow_points)
 89        .build())
 90    print(f"Arrow element type: {type(arrow.element).__name__}")
 91
 92    return [line, arrow]
 93
 94
 95def demo_text_elements():
 96    """Demo text elements with styles and document formatting."""
 97    print("\n=== Text Elements Demo ===")
 98
 99    text = (duc.ElementBuilder()
100        .at_position(0, 100)
101        .with_size(150, 25)
102        .with_label("Sample Text")
103        .with_styles(create_simple_styles(opacity=0.9))
104        .build_text_element()
105        .with_text("Hello, DucPy!")
106        .build())
107    print(f"Text content: '{text.element.text}'")
108
109    return [text]
110
111
112def demo_stack_elements():
113    """Demo new stack-based elements with styles."""
114    print("\n=== Stack Elements Demo ===")
115
116    frame = (duc.ElementBuilder()
117        .at_position(0, 150)
118        .with_size(200, 100)
119        .with_label("Technical Frame")
120        .with_styles(create_fill_and_stroke_style(
121            fill_content=create_solid_content("#F8F9FA"),
122            stroke_content=create_solid_content("#495057"),
123            stroke_width=2.0,
124            roundness=3.0
125        ))
126        .build_frame_element()
127        .build())
128    print(f"Frame stack label: {frame.element.stack_element_base.stack_base.label}")
129
130    plot = (duc.ElementBuilder()
131        .at_position(220, 150)
132        .with_size(180, 120)
133        .with_label("Engineering Plot")
134        .with_styles(create_fill_and_stroke_style(
135            fill_content=create_solid_content("#E9ECEF"),
136            stroke_content=create_solid_content("#6C757D"),
137            stroke_width=1.5
138        ))
139        .build_plot_element()
140        .with_margins(duc.Margins(top=5, right=5, bottom=5, left=5))
141        .build())
142
143    return [frame, plot]
144
145
146def demo_custom_stack_base():
147    """Demo custom stack base creation."""
148    print("\n=== Custom Stack Base Demo ===")
149
150    custom_frame = (duc.ElementBuilder()
151        .at_position(50, 280)
152        .with_size(150, 80)
153        .with_label("Custom Container")
154        .build_frame_element()
155        .with_stack_base(duc.StateBuilder().build_stack_base()
156            .with_is_collapsed(False)
157            .with_styles(duc.DucStackLikeStyles(opacity=0.8))
158            .build())
159        .build())
160
161    return [custom_frame]
162
163
164def main():
165    """Run all element creation demos."""
166    print("DucPy Element Creation Demo")
167    print("=" * 40)
168
169    elements = []
170    elements.extend(demo_basic_elements())
171    elements.extend(demo_linear_elements())
172    elements.extend(demo_text_elements())
173    elements.extend(demo_stack_elements())
174    elements.extend(demo_custom_stack_base())
175
176    duc_bytes = duc.serialize_duc(
177        name="element_creation_example",
178        elements=elements,
179    )
180
181    print(f"\nCreated {len(elements)} elements → serialized {len(duc_bytes)} bytes.")
182    print("✅ Element creation demo complete!")
183    return duc_bytes
184
185
186if __name__ == "__main__":
187    main()

Mutating Elements

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

  1#!/usr/bin/env python3
  2"""
  3Example demonstrating the `mutate` API for elements, global state, and
  4external file entries in DUC.
  5
  6This demo shows how to:
  7  1. Build an initial set of elements and supporting state via the
  8     standard builder API.
  9  2. Apply targeted mutations to the elements, the global state, and an
 10     external file entry using `duc.mutate_*` helpers.
 11  3. Serialize the resulting DUC object into a raw `.duc` byte string,
 12     matching the pattern used by the other example scripts.
 13"""
 14
 15import ducpy as duc
 16
 17
 18def main():
 19    print("Mutation Demo")
 20    print("=" * 30)
 21
 22    # ------------------------------------------------------------------
 23    # 1. Build the initial elements + state using the existing builders.
 24    # ------------------------------------------------------------------
 25    rect = (duc.ElementBuilder()
 26        .at_position(0, 0)
 27        .with_size(100, 50)
 28        .with_label("Initial Rectangle")
 29        .build_rectangle()
 30        .build())
 31
 32    ellipse = (duc.ElementBuilder()
 33        .at_position(140, 0)
 34        .with_size(60, 40)
 35        .with_label("Initial Ellipse")
 36        .build_ellipse()
 37        .build())
 38
 39    elements = [rect, ellipse]
 40
 41    duc_global_state = (duc.StateBuilder()
 42        .build_global_state()
 43        .with_name("mutation_demo")
 44        .with_main_scope("mm")
 45        .build())
 46
 47    duc_local_state = (duc.StateBuilder()
 48        .build_local_state()
 49        .build())
 50
 51    # A sample external file entry to exercise mutate_external_file.
 52    external_file = (duc.StateBuilder()
 53        .build_external_file()
 54        .with_key("logo")
 55        .with_mime_type("image/png")
 56        .with_data(b"\x89PNG\r\n\x1a\n" + b"\x00" * 16)
 57        .build())
 58
 59    # ------------------------------------------------------------------
 60    # 2. Apply mutations using the duc.mutate_* API.
 61    #    Each helper mutates in place and also stamps fresh versioning
 62    #    metadata (seed, updated, version, version_nonce) where
 63    #    applicable.
 64    # ------------------------------------------------------------------
 65
 66    # 2a. Mutate the rectangle: move it, resize it, rename, hide it.
 67    duc.mutate_element(
 68        rect,
 69        x=20,
 70        y=30,
 71        width=150,
 72        label="Mutated Rectangle",
 73        is_visible=False,
 74    )
 75
 76    # 2b. Mutate the ellipse: rename and move (size stays the same).
 77    duc.mutate_element(
 78        ellipse,
 79        x=200,
 80        y=75,
 81        label="Mutated Ellipse",
 82    )
 83
 84    # 2c. Mutate the global state (zoom level, background, name).
 85    duc.mutate_global_state(
 86        duc_global_state,
 87        view_background_color="#1E1E2E",
 88        name="mutation_demo_updated",
 89    )
 90
 91    # 2d. Mutate the local state (scroll position, grid mode).
 92    duc.mutate_local_state(
 93        duc_local_state,
 94        scroll_x=42.0,
 95        scroll_y=17.5,
 96        grid_mode_enabled=False,
 97    )
 98
 99    # 2e. Mutate the external file entry's metadata.
100    duc.mutate_external_file(
101        external_file,
102        version=2,
103    )
104
105    # ------------------------------------------------------------------
106    # 3. Serialize the mutated objects into a raw .duc byte string,
107    #    mirroring the pattern used by the other example scripts.
108    # ------------------------------------------------------------------
109    duc_bytes = duc.serialize_duc(
110        name="mutation_demo",
111        elements=elements,
112        duc_global_state=duc_global_state,
113        duc_local_state=duc_local_state,
114        external_files=[external_file],
115    )
116
117    print(f"   Mutated {len(elements)} elements.")
118    print(f"   Global state main scope -> {duc_global_state.main_scope!r}")
119    print(f"   Local state scroll -> ({duc_local_state.scroll_x}, {duc_local_state.scroll_y})")
120    print(f"   External file version -> {external_file.version}")
121    print(f"   Serialized {len(duc_bytes)} bytes.")
122    print("\n✅ Mutation demo complete!")
123    return duc_bytes
124
125
126if __name__ == "__main__":
127    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
 6
 7
 8def create_duc_with_external_files():
 9    """
10    Creates a DUC object and adds multiple external file entries to it
11    using the builder pattern.
12    """
13    print("Creating a DUC object with external files...")
14
15    # Create dummy data for external files
16    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"
17    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"
18
19    image_file_entry = (duc.StateBuilder()
20        .build_external_file()
21        .with_key("my_image_key")
22        .with_mime_type("image/png")
23        .with_data(dummy_image_data)
24        .build())
25
26    pdf_file_entry = (duc.StateBuilder()
27        .build_external_file()
28        .with_key("document_123")
29        .with_mime_type("application/pdf")
30        .with_data(dummy_pdf_data)
31        .build())
32
33    duc_global_state = (duc.StateBuilder()
34        .build_global_state()
35        .with_main_scope("mm")
36        .build())
37
38    duc_local_state = (duc.StateBuilder()
39        .build_local_state()
40        .build())
41
42    duc_object_files = {image_file_entry.id: image_file_entry, pdf_file_entry.id: pdf_file_entry}
43
44    print("DUC object with external files created successfully!")
45    print(f"Total external files: {len(duc_object_files)}")
46    return duc_object_files, duc_global_state, duc_local_state
47
48
49def main():
50    """Run the external files demo."""
51    print("External Files Demo")
52    print("=" * 30)
53    create_duc_with_external_files()
54    print("\nExternal files demo complete!")
55
56
57if __name__ == "__main__":
58    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  7. Round-trip SQL-built data through the high-level parser
 17"""
 18
 19import os
 20import tempfile
 21
 22import ducpy as duc
 23from ducpy.builders.sql_builder import DucSQL
 24
 25
 26def demo_create_new():
 27    print("=== Create new .duc ===")
 28
 29    with DucSQL.new() as db:
 30        db.sql(
 31            "INSERT INTO elements (id, element_type, x, y, width, height, label, opacity) "
 32            "VALUES (?,?,?,?,?,?,?,?)",
 33            "r1", "rectangle", 0, 0, 200, 100, "Main Rectangle", 1.0,
 34        )
 35        db.sql(
 36            "INSERT INTO elements (id, element_type, x, y, width, height, label, opacity) "
 37            "VALUES (?,?,?,?,?,?,?,?)",
 38            "e1", "ellipse", 250, 0, 120, 80, "Side Ellipse", 0.9,
 39        )
 40
 41        for owner_id, colour in [("r1", "#4ECDC4"), ("e1", "#FF6B6B")]:
 42            db.sql(
 43                "INSERT INTO backgrounds (owner_type, owner_id, src, opacity) "
 44                "VALUES (?,?,?,?)",
 45                "element", owner_id, colour, 1.0,
 46            )
 47
 48        rows = db.sql("SELECT id, element_type, label FROM elements ORDER BY id")
 49        for row in rows:
 50            print(f"  [{row['id']}] {row['element_type']}{row['label']}")
 51
 52        db.sql_dict(
 53            "UPDATE elements SET label = :label WHERE id = :id",
 54            {"label": "Renamed Rectangle", "id": "r1"},
 55        )
 56
 57        updated = db.sql("SELECT label FROM elements WHERE id = ?", "r1")[0]
 58        print(f"  After rename: '{updated['label']}'")
 59
 60        tmp = tempfile.NamedTemporaryFile(suffix=".duc", delete=False)
 61        tmp.close()
 62        db.save(tmp.name)
 63        print(f"  Saved to: {tmp.name}")
 64
 65    return tmp.name
 66
 67
 68def demo_open_existing(path: str):
 69    print("\n=== Open existing .duc ===")
 70
 71    with DucSQL(path) as db:
 72        count = db.sql("SELECT COUNT(*) AS n FROM elements")[0]["n"]
 73        print(f"  Total elements: {count}")
 74
 75        rows = db.sql(
 76            "SELECT e.id, e.label, b.src AS colour "
 77            "FROM elements e "
 78            "LEFT JOIN backgrounds b ON b.owner_type = 'element' AND b.owner_id = e.id "
 79            "ORDER BY e.id"
 80        )
 81        for row in rows:
 82            print(f"  {row['label']} → fill: {row['colour']}")
 83
 84    os.unlink(path)
 85
 86
 87def demo_bytes_roundtrip():
 88    print("\n=== Bytes round-trip ===")
 89
 90    with DucSQL.new() as db:
 91        db.sql(
 92            "INSERT INTO elements (id, element_type, x, y, width, height, label) "
 93            "VALUES (?,?,?,?,?,?,?)",
 94            "t1", "text", 10, 10, 300, 40, "Hello, DUC!",
 95        )
 96        raw = db.to_bytes()
 97
 98    print(f"  Serialised to {len(raw):,} bytes")
 99
100    with DucSQL.from_bytes(raw) as db:
101        row = db.sql("SELECT label FROM elements WHERE id = 't1'")[0]
102        print(f"  Restored label: '{row['label']}'")
103
104    return raw
105
106
107def demo_advanced_connection():
108    print("\n=== Advanced: direct connection access ===")
109
110    with DucSQL.new() as db:
111        records = [
112            (f"el{i}", "rectangle", i * 110, 0, 100, 60, f"Box {i}", 1.0)
113            for i in range(5)
114        ]
115        db.conn.executemany(
116            "INSERT INTO elements (id, element_type, x, y, width, height, label, opacity) "
117            "VALUES (?,?,?,?,?,?,?,?)",
118            records,
119        )
120
121        total = db.sql("SELECT COUNT(*) AS n FROM elements")[0]["n"]
122        print(f"  Bulk-inserted {total} elements")
123
124        tables = [
125            row["name"]
126            for row in db.sql(
127                "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
128            )
129        ]
130        print(f"  Schema tables: {', '.join(tables[:6])} …")
131
132
133def demo_serialize_via_sql():
134    print("\n=== Build with SQL, serialize with the high-level API ===")
135
136    with DucSQL.new() as db:
137        db.sql(
138            "INSERT INTO elements (id, element_type, x, y, width, height, label, opacity) "
139            "VALUES (?,?,?,?,?,?,?,?)",
140            "s1", "rectangle", 0, 0, 100, 50, "From SQL", 1.0,
141        )
142        raw = db.to_bytes()
143
144    parsed = duc.parse_duc(raw)
145    print(f"  Parsed {len(parsed.elements)} element(s) built via raw SQL.")
146
147    return raw
148
149
150def main():
151    print("DucSQL Builder Demo")
152    print("=" * 40)
153
154    saved_path = demo_create_new()
155    demo_open_existing(saved_path)
156    raw_bytes = demo_bytes_roundtrip()
157    demo_advanced_connection()
158    demo_serialize_via_sql()
159
160    print(f"\nAll DucSQL demos completed successfully! ({len(raw_bytes):,} byte round-trip payload)")
161    return raw_bytes
162
163
164if __name__ == "__main__":
165    main()

Serialization

Demonstrates how to serialize builder-created elements directly to a .duc file using duc.serialize_duc.

 1#!/usr/bin/env python3
 2"""
 3Example demonstrating how to serialize elements created by the Builder API into a .duc file.
 4
 5This demo shows the correct pattern for taking in-memory python elements
 6and writing them to a raw `.duc` binary blob.
 7"""
 8
 9import ducpy as duc
10from ducpy.builders.style_builders import create_fill_and_stroke_style, create_solid_content
11
12def main():
13    print("Serialization Demo")
14    print("=" * 30)
15    
16    print("1. Creating elements via Builder API...")
17    elements = []
18    
19    # Create some basic elements
20    rect = (duc.ElementBuilder()
21        .at_position(0, 0)
22        .with_size(100, 50)
23        .with_label("Sample Rectangle")
24        .with_styles(create_fill_and_stroke_style(
25            fill_content=create_solid_content("#FF6B6B"),
26            stroke_content=create_solid_content("#2C3E50"),
27            stroke_width=2.0
28        ))
29        .build_rectangle()
30        .build())
31    elements.append(rect)
32    
33    ellipse = (duc.ElementBuilder()
34        .at_position(120, 0)
35        .with_size(60, 40)
36        .with_label("Sample Ellipse")
37        .with_styles(create_fill_and_stroke_style(
38            fill_content=create_solid_content("#4ECDC4"),
39            stroke_content=create_solid_content("#34495E"),
40            stroke_width=1.5
41        ))
42        .build_ellipse()
43        .build())
44    elements.append(ellipse)
45    
46    print(f"   Created {len(elements)} elements.")
47    
48    print("2. Serializing to .duc format...")
49    # NOTE: The serialize_duc function takes keyword arguments for elements, 
50    # blocks, global state, etc. and bridges to the Rust native backend.
51    duc_bytes = duc.serialize_duc(
52        name="serialization_example",
53        elements=elements
54    )
55    
56    print(f"   Successfully serialized {len(duc_bytes)} bytes.")
57    print("\n✅ Serialization demo complete!")
58    return duc_bytes
59
60if __name__ == "__main__":
61    main()

Parsing

Demonstrates how to parse a .duc file or raw binary bytes using duc.parse_duc, allowing attribute-style access to the document’s content.

 1#!/usr/bin/env python3
 2"""
 3Example demonstrating how to parse a .duc file using the parsing API.
 4
 5This demo shows how to read a `.duc` binary blob or file path and access
 6the parsed data using attribute-style access via DucData.
 7"""
 8
 9import os
10import tempfile
11import ducpy as duc
12
13def main():
14    print("Parsing Demo")
15    print("=" * 30)
16
17    # First, let's create a temporary .duc file to parse
18    from ducpy.builders.style_builders import create_fill_and_stroke_style, create_solid_content
19    elements = [
20        duc.ElementBuilder()
21            .at_position(10, 20)
22            .with_size(100, 50)
23            .with_label("Parsed Rectangle")
24            .with_styles(create_fill_and_stroke_style(
25                fill_content=create_solid_content("#FF6B6B"),
26                stroke_content=create_solid_content("#2C3E50"),
27                stroke_width=2.0
28            ))
29            .build_rectangle()
30            .build()
31    ]
32    duc_bytes = duc.serialize_duc(name="parsing_example", elements=elements)
33    
34    with tempfile.NamedTemporaryFile(suffix=".duc", delete=False) as tmp:
35        tmp.write(duc_bytes)
36        tmp_path = tmp.name
37
38    print("1. Parsing a .duc file from a file path...")
39    
40    # You can pass a string path directly to parse_duc
41    parsed_data = duc.parse_duc(tmp_path)
42    
43    print(f"   Document Source: {parsed_data.source}")
44    print(f"   Parsed {len(parsed_data.elements)} elements.")
45    
46    print("\n2. Accessing element attributes (snake_case)...")
47    
48    # Element properties are accessible via dot-notation with snake_case keys
49    # because parse_duc returns a DucData object.
50    first_element = parsed_data.elements[0]
51    print(f"   Element ID: {first_element.id}")
52    print(f"   Element Type: {first_element.type}")
53    print(f"   Element Label: {first_element.label}")
54    print(f"   Element Position: (X: {first_element.x}, Y: {first_element.y})")
55    
56    print("\n3. Parsing directly from raw bytes...")
57    
58    # You can also pass raw bytes directly to parse_duc
59    parsed_from_bytes = duc.parse_duc(duc_bytes)
60    print(f"   Parsed successfully from bytes. Found {len(parsed_from_bytes.elements)} elements.")
61    
62    # Clean up the temporary file
63    os.unlink(tmp_path)
64    
65    print("\n✅ Parsing demo complete!")
66
67if __name__ == "__main__":
68    main()