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()