0
# Layout Viewer and GUI Components
1
2
GUI components for layout visualization, user interaction, layer management, and display customization when running in GUI mode with Qt support.
3
4
## Capabilities
5
6
### Layout Viewing
7
8
```python { .api }
9
class LayoutView:
10
def __init__(self):
11
"""Create a new layout view widget."""
12
13
def load_layout(self, layout: Layout, add_cellview: bool = True) -> int:
14
"""
15
Load a layout into the view.
16
17
Parameters:
18
- layout: Layout object to display
19
- add_cellview: Whether to add a cellview for this layout
20
21
Returns:
22
int: Cellview index
23
"""
24
25
def create_layout(self, add_cellview: bool = True) -> int:
26
"""
27
Create a new empty layout in the view.
28
29
Returns:
30
int: Cellview index
31
"""
32
33
def active_cellview_index(self) -> int:
34
"""Get the index of the currently active cellview."""
35
36
def set_active_cellview_index(self, index: int) -> None:
37
"""Set the active cellview by index."""
38
39
def cellview(self, index: int) -> CellView:
40
"""Get cellview by index."""
41
42
def zoom_fit(self) -> None:
43
"""Zoom to fit all visible content."""
44
45
def zoom_box(self, bbox: DBox) -> None:
46
"""
47
Zoom to specific bounding box.
48
49
Parameters:
50
- bbox: Bounding box to zoom to
51
"""
52
53
def pan_center(self, center: DPoint) -> None:
54
"""
55
Pan view to center on specified point.
56
57
Parameters:
58
- center: Point to center the view on
59
"""
60
61
def set_config(self, config_name: str, config_value: str) -> None:
62
"""Set configuration parameter."""
63
64
def get_config(self, config_name: str) -> str:
65
"""Get configuration parameter value."""
66
67
class CellView:
68
def __init__(self, layout: Layout = None):
69
"""
70
Create a cellview for a layout.
71
72
Parameters:
73
- layout: Layout to view (optional)
74
"""
75
76
@property
77
def layout(self) -> Layout:
78
"""Get the associated layout."""
79
80
@property
81
def cell_index(self) -> int:
82
"""Get the current top cell index."""
83
84
def set_cell(self, cell_index: int) -> None:
85
"""
86
Set the top cell to display.
87
88
Parameters:
89
- cell_index: Index of cell to display
90
"""
91
92
@property
93
def name(self) -> str:
94
"""Get cellview name."""
95
96
def set_name(self, name: str) -> None:
97
"""Set cellview name."""
98
99
def is_valid(self) -> bool:
100
"""Check if cellview is valid."""
101
102
def close(self) -> None:
103
"""Close the cellview."""
104
```
105
106
### Layer Management and Display
107
108
```python { .api }
109
class LayerPropertiesNode:
110
def __init__(self):
111
"""Create a layer properties node."""
112
113
def source_layer_info(self) -> LayerInfo:
114
"""Get source layer information."""
115
116
def set_source_layer_info(self, layer_info: LayerInfo) -> None:
117
"""Set source layer information."""
118
119
@property
120
def name(self) -> str:
121
"""Get layer name."""
122
123
def set_name(self, name: str) -> None:
124
"""Set layer name."""
125
126
@property
127
def visible(self) -> bool:
128
"""Check if layer is visible."""
129
130
def set_visible(self, visible: bool) -> None:
131
"""Set layer visibility."""
132
133
@property
134
def transparent(self) -> bool:
135
"""Check if layer is transparent."""
136
137
def set_transparent(self, transparent: bool) -> None:
138
"""Set layer transparency."""
139
140
@property
141
def width(self) -> int:
142
"""Get line width for layer display."""
143
144
def set_width(self, width: int) -> None:
145
"""Set line width for layer display."""
146
147
def fill_color(self) -> int:
148
"""Get fill color (RGB)."""
149
150
def set_fill_color(self, color: int) -> None:
151
"""Set fill color (RGB value)."""
152
153
def frame_color(self) -> int:
154
"""Get frame/outline color."""
155
156
def set_frame_color(self, color: int) -> None:
157
"""Set frame/outline color."""
158
159
class LayerPropertiesList:
160
def __init__(self):
161
"""Create layer properties list."""
162
163
def append(self, properties: LayerPropertiesNode) -> None:
164
"""Add layer properties to list."""
165
166
def insert(self, index: int, properties: LayerPropertiesNode) -> None:
167
"""Insert layer properties at specific index."""
168
169
def erase(self, index: int) -> None:
170
"""Remove layer properties at index."""
171
172
def clear(self) -> None:
173
"""Clear all layer properties."""
174
175
def size(self) -> int:
176
"""Get number of layer properties."""
177
178
def __getitem__(self, index: int) -> LayerPropertiesNode:
179
"""Get layer properties by index."""
180
```
181
182
### Annotations and Markers
183
184
```python { .api }
185
class Annotation:
186
def __init__(self):
187
"""Create an annotation object."""
188
189
@property
190
def id(self) -> int:
191
"""Get annotation ID."""
192
193
def set_text(self, text: str) -> None:
194
"""
195
Set annotation text.
196
197
Parameters:
198
- text: Text to display
199
"""
200
201
def text(self) -> str:
202
"""Get annotation text."""
203
204
def set_position(self, position: DPoint) -> None:
205
"""Set annotation position."""
206
207
def position(self) -> DPoint:
208
"""Get annotation position."""
209
210
def set_color(self, color: int) -> None:
211
"""Set annotation color."""
212
213
def color(self) -> int:
214
"""Get annotation color."""
215
216
def set_outline_color(self, color: int) -> None:
217
"""Set outline color."""
218
219
def outline_color(self) -> int:
220
"""Get outline color."""
221
222
class Marker:
223
def __init__(self):
224
"""Create a marker for highlighting areas."""
225
226
def set_color(self, color: int) -> None:
227
"""Set marker color."""
228
229
def color(self) -> int:
230
"""Get marker color."""
231
232
def set_vertex_size(self, size: int) -> None:
233
"""Set vertex marker size."""
234
235
def vertex_size(self) -> int:
236
"""Get vertex marker size."""
237
238
def set_line_width(self, width: int) -> None:
239
"""Set line width for marker."""
240
241
def line_width(self) -> int:
242
"""Get marker line width."""
243
244
def set(self, shape) -> None:
245
"""
246
Set marker to highlight a shape.
247
248
Parameters:
249
- shape: Shape to highlight (Box, Polygon, etc.)
250
"""
251
```
252
253
### Display State Management
254
255
```python { .api }
256
class DisplayState:
257
def __init__(self):
258
"""Create display state manager."""
259
260
def visible_layers(self) -> list[int]:
261
"""Get list of visible layer indices."""
262
263
def set_layer_visible(self, layer: int, visible: bool) -> None:
264
"""
265
Set layer visibility.
266
267
Parameters:
268
- layer: Layer index
269
- visible: Visibility state
270
"""
271
272
def layer_visible(self, layer: int) -> bool:
273
"""Check if layer is visible."""
274
275
def set_color(self, layer: int, color: int) -> None:
276
"""Set layer display color."""
277
278
def color(self, layer: int) -> int:
279
"""Get layer display color."""
280
281
class ViewObject:
282
def __init__(self):
283
"""Generic view object for custom display elements."""
284
285
def bbox(self) -> DBox:
286
"""Get bounding box of the view object."""
287
288
def set_visible(self, visible: bool) -> None:
289
"""Set object visibility."""
290
291
def visible(self) -> bool:
292
"""Check if object is visible."""
293
```
294
295
### GUI Utility Functions
296
297
```python { .api }
298
def has_gui() -> bool:
299
"""
300
Check if GUI functionality is available.
301
302
Returns:
303
bool: True if GUI is available (Qt is loaded)
304
"""
305
306
def can_create_layoutview() -> bool:
307
"""
308
Check if LayoutView can be created.
309
310
Returns:
311
bool: True if LayoutView creation is supported
312
"""
313
314
class Application:
315
@staticmethod
316
def instance() -> Application:
317
"""Get the application instance."""
318
319
def main_window(self):
320
"""Get the main application window."""
321
322
def set_config(self, key: str, value: str) -> None:
323
"""Set application configuration value."""
324
325
def get_config(self, key: str) -> str:
326
"""Get application configuration value."""
327
328
def exec_() -> int:
329
"""Run the application event loop."""
330
```
331
332
## Usage Examples
333
334
### Basic Layout Viewing
335
336
```python
337
import klayout.db as db
338
import klayout.lay as lay
339
340
# Check if GUI is available
341
if not lay.has_gui():
342
print("GUI not available - running in headless mode")
343
exit()
344
345
# Create layout with some content
346
layout = db.Layout()
347
top_cell = layout.create_cell("TOP")
348
349
# Add some shapes
350
layer1 = layout.layer(db.LayerInfo(1, 0))
351
layer2 = layout.layer(db.LayerInfo(2, 0))
352
353
top_cell.shapes(layer1).insert(db.Box(0, 0, 1000, 1000))
354
top_cell.shapes(layer2).insert(db.Box(200, 200, 800, 800))
355
356
# Create and configure layout view
357
if lay.can_create_layoutview():
358
view = lay.LayoutView()
359
360
# Load layout into view
361
cellview_index = view.load_layout(layout)
362
363
# Set the top cell
364
cellview = view.cellview(cellview_index)
365
cellview.set_cell(top_cell.cell_index)
366
367
# Zoom to fit content
368
view.zoom_fit()
369
370
print("Layout loaded in viewer")
371
```
372
373
### Layer Display Configuration
374
375
```python
376
import klayout.db as db
377
import klayout.lay as lay
378
379
layout = db.Layout()
380
layout.read("complex_design.gds")
381
382
if lay.can_create_layoutview():
383
view = lay.LayoutView()
384
cellview_index = view.load_layout(layout)
385
386
# Configure layer display properties
387
layer_props_list = lay.LayerPropertiesList()
388
389
# Metal 1 - Blue fill, solid
390
metal1_props = lay.LayerPropertiesNode()
391
metal1_props.set_source_layer_info(db.LayerInfo(1, 0))
392
metal1_props.set_name("METAL1")
393
metal1_props.set_fill_color(0x0000FF) # Blue
394
metal1_props.set_frame_color(0x000080) # Dark blue outline
395
metal1_props.set_visible(True)
396
metal1_props.set_transparent(False)
397
metal1_props.set_width(1)
398
layer_props_list.append(metal1_props)
399
400
# Metal 2 - Red fill, transparent
401
metal2_props = lay.LayerPropertiesNode()
402
metal2_props.set_source_layer_info(db.LayerInfo(2, 0))
403
metal2_props.set_name("METAL2")
404
metal2_props.set_fill_color(0xFF0000) # Red
405
metal2_props.set_frame_color(0x800000) # Dark red outline
406
metal2_props.set_visible(True)
407
metal2_props.set_transparent(True)
408
metal2_props.set_width(2)
409
layer_props_list.append(metal2_props)
410
411
# Via - Green outline only
412
via_props = lay.LayerPropertiesNode()
413
via_props.set_source_layer_info(db.LayerInfo(3, 0))
414
via_props.set_name("VIA")
415
via_props.set_fill_color(0x00FF00) # Green (not used for outline-only)
416
via_props.set_frame_color(0x00FF00) # Green outline
417
via_props.set_visible(True)
418
via_props.set_transparent(True) # Transparent fill
419
via_props.set_width(3)
420
layer_props_list.append(via_props)
421
422
print(f"Configured {layer_props_list.size()} layer display properties")
423
```
424
425
### Adding Annotations and Markers
426
427
```python
428
import klayout.db as db
429
import klayout.lay as lay
430
431
layout = db.Layout()
432
layout.read("design_with_issues.gds")
433
434
if lay.can_create_layoutview():
435
view = lay.LayoutView()
436
cellview_index = view.load_layout(layout)
437
438
# Add annotations for important features
439
annotation1 = lay.Annotation()
440
annotation1.set_text("Critical timing path")
441
annotation1.set_position(db.DPoint(100.5, 200.5))
442
annotation1.set_color(0xFF0000) # Red text
443
annotation1.set_outline_color(0x800000) # Dark red outline
444
445
annotation2 = lay.Annotation()
446
annotation2.set_text("Power distribution node")
447
annotation2.set_position(db.DPoint(500.0, 300.0))
448
annotation2.set_color(0x0000FF) # Blue text
449
450
# Create markers for highlighting specific areas
451
marker1 = lay.Marker()
452
marker1.set_color(0xFFFF00) # Yellow
453
marker1.set_line_width(3)
454
marker1.set_vertex_size(10)
455
456
# Highlight a specific polygon (e.g., design rule violation)
457
violation_area = db.Box(150, 150, 200, 200)
458
marker1.set(violation_area)
459
460
marker2 = lay.Marker()
461
marker2.set_color(0xFF00FF) # Magenta
462
marker2.set_line_width(2)
463
464
# Highlight another area
465
critical_path = db.Path([db.Point(0, 100), db.Point(500, 100),
466
db.Point(500, 400)], 10)
467
marker2.set(critical_path.polygon())
468
469
print("Added annotations and markers to highlight design features")
470
```
471
472
### Interactive View Control
473
474
```python
475
import klayout.db as db
476
import klayout.lay as lay
477
478
def setup_interactive_view(layout_file: str):
479
"""Set up an interactive layout view with custom controls."""
480
481
if not lay.has_gui():
482
print("GUI not available")
483
return None
484
485
layout = db.Layout()
486
layout.read(layout_file)
487
488
view = lay.LayoutView()
489
cellview_index = view.load_layout(layout)
490
491
# Configure view settings
492
view.set_config("grid-visible", "true")
493
view.set_config("grid-color", "#808080")
494
view.set_config("background-color", "#000000") # Black background
495
view.set_config("text-visible", "true")
496
497
# Set up zoom and pan shortcuts
498
bbox = layout.top_cell().bbox_per_layer(layout.layer(db.LayerInfo(1, 0)))
499
if not bbox.empty():
500
# Convert to double precision for view
501
dbbox = db.DBox(bbox.left, bbox.bottom, bbox.right, bbox.top)
502
view.zoom_box(dbbox)
503
504
# Pan to interesting area
505
center = db.DPoint(500.0, 500.0) # Adjust based on your design
506
view.pan_center(center)
507
508
return view
509
510
def create_custom_layer_setup(view: lay.LayoutView, layout: db.Layout):
511
"""Create a custom layer display setup."""
512
513
# Define layer colors and properties
514
layer_configs = [
515
{"layer": (1, 0), "name": "NWELL", "color": 0x80FF80, "transparent": True},
516
{"layer": (2, 0), "name": "ACTIVE", "color": 0x80FFFF, "transparent": False},
517
{"layer": (3, 0), "name": "POLY", "color": 0xFF8080, "transparent": False},
518
{"layer": (4, 0), "name": "CONTACT", "color": 0x8080FF, "transparent": False},
519
{"layer": (5, 0), "name": "METAL1", "color": 0x0080FF, "transparent": False},
520
{"layer": (6, 0), "name": "VIA1", "color": 0xFF0080, "transparent": True},
521
{"layer": (7, 0), "name": "METAL2", "color": 0xFF8000, "transparent": False},
522
]
523
524
layer_props_list = lay.LayerPropertiesList()
525
526
for config in layer_configs:
527
props = lay.LayerPropertiesNode()
528
layer_info = db.LayerInfo(config["layer"][0], config["layer"][1])
529
530
props.set_source_layer_info(layer_info)
531
props.set_name(config["name"])
532
props.set_fill_color(config["color"])
533
props.set_frame_color(config["color"] & 0x808080) # Darker frame
534
props.set_visible(True)
535
props.set_transparent(config["transparent"])
536
props.set_width(1)
537
538
layer_props_list.append(props)
539
540
print(f"Created custom layer setup with {len(layer_configs)} layers")
541
return layer_props_list
542
543
# Example usage
544
if __name__ == "__main__":
545
view = setup_interactive_view("my_design.gds")
546
if view:
547
print("Interactive view created successfully")
548
549
# If running in a Qt application context
550
if lay.Application.instance():
551
app = lay.Application.instance()
552
app.exec_() # Start event loop
553
```
554
555
### Programmatic View Navigation
556
557
```python
558
import klayout.db as db
559
import klayout.lay as lay
560
import time
561
562
def tour_layout(layout_file: str):
563
"""Create an automated tour of a layout."""
564
565
layout = db.Layout()
566
layout.read(layout_file)
567
568
if not lay.can_create_layoutview():
569
print("Cannot create layout view")
570
return
571
572
view = lay.LayoutView()
573
cellview_index = view.load_layout(layout)
574
575
# Find interesting areas to visit
576
tour_points = []
577
578
# Collect bounding boxes of all cells
579
for cell in layout.each_cell():
580
if not cell.is_empty():
581
bbox = cell.bbox()
582
if not bbox.empty():
583
center = db.DPoint((bbox.left + bbox.right) / 2.0,
584
(bbox.bottom + bbox.top) / 2.0)
585
# Create zoom box around center
586
zoom_size = max(bbox.width(), bbox.height()) * 1.2
587
zoom_box = db.DBox(center.x - zoom_size/2, center.y - zoom_size/2,
588
center.x + zoom_size/2, center.y + zoom_size/2)
589
tour_points.append((cell.name, center, zoom_box))
590
591
# Tour through the points
592
for cell_name, center, zoom_box in tour_points[:10]: # Limit to first 10
593
print(f"Visiting cell: {cell_name}")
594
595
# Pan to center
596
view.pan_center(center)
597
time.sleep(1) # Wait 1 second
598
599
# Zoom to show the cell
600
view.zoom_box(zoom_box)
601
time.sleep(2) # Wait 2 seconds
602
603
# Return to fit view
604
view.zoom_fit()
605
print("Tour completed")
606
607
# Example: Analyze what's visible in current view
608
def analyze_visible_content(view: lay.LayoutView):
609
"""Analyze the content visible in the current view."""
610
611
# Get current view bounds (this would require view state access)
612
# This is a simplified example
613
614
cellview = view.cellview(view.active_cellview_index())
615
if not cellview.is_valid():
616
return
617
618
layout = cellview.layout
619
top_cell = layout.cell(cellview.cell_index)
620
621
print(f"Analyzing cell: {top_cell.name}")
622
print(f"Cell has {top_cell.child_cells()} child cells")
623
624
# Count shapes per layer
625
layer_counts = {}
626
for layer_index in range(layout.layers()):
627
if layout.is_valid_layer(layer_index):
628
layer_info = layout.get_info(layer_index)
629
shape_count = top_cell.shapes(layer_index).size()
630
if shape_count > 0:
631
layer_counts[f"{layer_info.layer}/{layer_info.datatype}"] = shape_count
632
633
print("Shapes per layer:")
634
for layer, count in sorted(layer_counts.items()):
635
print(f" {layer}: {count} shapes")
636
637
if __name__ == "__main__":
638
tour_layout("complex_design.gds")
639
```