kicad-python 0.2.0__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. {kicad_python-0.2.0 → kicad_python-0.3.0}/PKG-INFO +24 -5
  2. {kicad_python-0.2.0 → kicad_python-0.3.0}/README.md +19 -2
  3. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/board.py +37 -7
  4. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/board_types.py +178 -23
  5. {kicad_python-0.2.0 → kicad_python-0.3.0/build/lib}/kipy/common_types.py +41 -20
  6. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/geometry.py +80 -4
  7. {kicad_python-0.2.0 → kicad_python-0.3.0/build/lib}/kipy/kicad.py +7 -9
  8. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/kicad_api_version.py +1 -1
  9. kicad_python-0.3.0/build/lib/kipy/proto/board/board_commands_pb2.py +85 -0
  10. {kicad_python-0.2.0 → kicad_python-0.3.0/build/lib}/kipy/proto/board/board_commands_pb2.pyi +134 -69
  11. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/proto/board/board_pb2.pyi +39 -39
  12. {kicad_python-0.2.0 → kicad_python-0.3.0/build/lib}/kipy/proto/board/board_types_pb2.py +66 -66
  13. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/proto/board/board_types_pb2.pyi +250 -175
  14. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/proto/common/commands/base_commands_pb2.pyi +28 -32
  15. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/proto/common/commands/editor_commands_pb2.pyi +123 -123
  16. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/proto/common/commands/project_commands_pb2.pyi +21 -25
  17. {kicad_python-0.2.0 → kicad_python-0.3.0/build/lib}/kipy/proto/common/envelope_pb2.pyi +12 -12
  18. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/proto/common/types/base_types_pb2.pyi +111 -110
  19. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/proto/common/types/project_settings_pb2.pyi +30 -30
  20. {kicad_python-0.2.0 → kicad_python-0.3.0/build/lib}/kipy/proto/schematic/schematic_types_pb2.pyi +20 -20
  21. {kicad_python-0.2.0 → kicad_python-0.3.0/build/lib}/kipy/util/board_layer.py +43 -1
  22. {kicad_python-0.2.0 → kicad_python-0.3.0}/build.py +1 -1
  23. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/board.py +37 -7
  24. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/board_types.py +178 -23
  25. {kicad_python-0.2.0/build/lib → kicad_python-0.3.0}/kipy/common_types.py +41 -20
  26. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/geometry.py +80 -4
  27. {kicad_python-0.2.0/build/lib → kicad_python-0.3.0}/kipy/kicad.py +7 -9
  28. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/kicad_api_version.py +1 -1
  29. kicad_python-0.3.0/kipy/proto/board/board_commands_pb2.py +85 -0
  30. {kicad_python-0.2.0/build/lib → kicad_python-0.3.0}/kipy/proto/board/board_commands_pb2.pyi +134 -69
  31. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/proto/board/board_pb2.pyi +39 -39
  32. {kicad_python-0.2.0/build/lib → kicad_python-0.3.0}/kipy/proto/board/board_types_pb2.py +66 -66
  33. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/proto/board/board_types_pb2.pyi +250 -175
  34. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/proto/common/commands/base_commands_pb2.pyi +28 -32
  35. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/proto/common/commands/editor_commands_pb2.pyi +123 -123
  36. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/proto/common/commands/project_commands_pb2.pyi +21 -25
  37. {kicad_python-0.2.0/build/lib → kicad_python-0.3.0}/kipy/proto/common/envelope_pb2.pyi +12 -12
  38. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/proto/common/types/base_types_pb2.pyi +111 -110
  39. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/proto/common/types/project_settings_pb2.pyi +30 -30
  40. {kicad_python-0.2.0/build/lib → kicad_python-0.3.0}/kipy/proto/schematic/schematic_types_pb2.pyi +20 -20
  41. {kicad_python-0.2.0/build/lib → kicad_python-0.3.0}/kipy/util/board_layer.py +43 -1
  42. {kicad_python-0.2.0 → kicad_python-0.3.0}/pyproject.toml +2 -1
  43. {kicad_python-0.2.0 → kicad_python-0.3.0}/setup.py +3 -3
  44. kicad_python-0.2.0/build/lib/kipy/proto/board/board_commands_pb2.py +0 -79
  45. kicad_python-0.2.0/kipy/proto/board/board_commands_pb2.py +0 -79
  46. {kicad_python-0.2.0 → kicad_python-0.3.0}/LICENSE +0 -0
  47. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/__init__.py +0 -0
  48. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/client.py +0 -0
  49. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/errors.py +0 -0
  50. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/project.py +0 -0
  51. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/project_types.py +0 -0
  52. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/proto/__init__.py +0 -0
  53. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/proto/board/__init__.py +0 -0
  54. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/proto/board/board_pb2.py +0 -0
  55. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/proto/common/__init__.py +0 -0
  56. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/proto/common/commands/__init__.py +0 -0
  57. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/proto/common/commands/base_commands_pb2.py +0 -0
  58. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/proto/common/commands/editor_commands_pb2.py +0 -0
  59. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/proto/common/commands/project_commands_pb2.py +0 -0
  60. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/proto/common/envelope_pb2.py +0 -0
  61. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/proto/common/types/__init__.py +0 -0
  62. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/proto/common/types/base_types_pb2.py +0 -0
  63. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/proto/common/types/enums_pb2.py +0 -0
  64. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/proto/common/types/enums_pb2.pyi +0 -0
  65. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/proto/common/types/project_settings_pb2.py +0 -0
  66. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/proto/schematic/schematic_commands_pb2.py +0 -0
  67. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/proto/schematic/schematic_commands_pb2.pyi +0 -0
  68. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/proto/schematic/schematic_types_pb2.py +0 -0
  69. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/util/__init__.py +0 -0
  70. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/util/proto.py +0 -0
  71. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/util/units.py +0 -0
  72. {kicad_python-0.2.0 → kicad_python-0.3.0}/build/lib/kipy/wrapper.py +0 -0
  73. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/__init__.py +0 -0
  74. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/client.py +0 -0
  75. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/errors.py +0 -0
  76. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/project.py +0 -0
  77. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/project_types.py +0 -0
  78. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/proto/__init__.py +0 -0
  79. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/proto/board/__init__.py +0 -0
  80. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/proto/board/board_pb2.py +0 -0
  81. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/proto/common/__init__.py +0 -0
  82. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/proto/common/commands/__init__.py +0 -0
  83. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/proto/common/commands/base_commands_pb2.py +0 -0
  84. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/proto/common/commands/editor_commands_pb2.py +0 -0
  85. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/proto/common/commands/project_commands_pb2.py +0 -0
  86. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/proto/common/envelope_pb2.py +0 -0
  87. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/proto/common/types/__init__.py +0 -0
  88. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/proto/common/types/base_types_pb2.py +0 -0
  89. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/proto/common/types/enums_pb2.py +0 -0
  90. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/proto/common/types/enums_pb2.pyi +0 -0
  91. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/proto/common/types/project_settings_pb2.py +0 -0
  92. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/proto/schematic/schematic_commands_pb2.py +0 -0
  93. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/proto/schematic/schematic_commands_pb2.pyi +0 -0
  94. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/proto/schematic/schematic_types_pb2.py +0 -0
  95. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/util/__init__.py +0 -0
  96. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/util/proto.py +0 -0
  97. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/util/units.py +0 -0
  98. {kicad_python-0.2.0 → kicad_python-0.3.0}/kipy/wrapper.py +0 -0
@@ -1,8 +1,7 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: kicad-python
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: KiCad API Python Bindings
5
- Home-page: https://kicad.org/
6
5
  License: MIT
7
6
  Author: The KiCad Development Team
8
7
  Requires-Python: >=3.9,<4.0
@@ -14,9 +13,12 @@ Classifier: Programming Language :: Python :: 3.9
14
13
  Classifier: Programming Language :: Python :: 3.10
15
14
  Classifier: Programming Language :: Python :: 3.11
16
15
  Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
17
  Classifier: Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)
18
+ Requires-Dist: deprecated (>=1.2,<2.0)
18
19
  Requires-Dist: protobuf (>=5.29,<6.0)
19
20
  Requires-Dist: pynng (>=0.8.0,<0.9.0)
21
+ Project-URL: Homepage, https://kicad.org/
20
22
  Project-URL: Repository, https://gitlab.com/kicad/code/kicad-python
21
23
  Description-Content-Type: text/markdown
22
24
 
@@ -81,8 +83,11 @@ This should print out the version of KiCad you have connected to.
81
83
 
82
84
  ## Documentation
83
85
 
84
- There is no documentation separate from the code comments and examples yet, sorry! This will be
85
- more of a priority once the KiCad 9 release is stable.
86
+ The documentation created from this repository (via the `docs` directory and the docstrings in the
87
+ source code) is hosted at https://docs.kicad.org/kicad-python-main
88
+
89
+ Many things are still not documented or underdocumented -- contributions that expand the
90
+ documentation or add docstrings are welcomed.
86
91
 
87
92
  ## Examples
88
93
 
@@ -93,6 +98,20 @@ plugins, copy or symlink them into the appropriate plugins path in order for KiC
93
98
 
94
99
  ## Release History
95
100
 
101
+ ### 0.3.0 (March 29, 2025)
102
+
103
+ - Add support for footprint mounting style attribute (#19) (Thanh Duong, !10)
104
+ - Added `visible` property to `Field` and deprecate it from `TextAttributes` to match KiCad changes
105
+ - Improve version checking functions(Lucas Gerads, !11)
106
+ - Add missing board layers User.10 through User.45 (#23)
107
+ - Improve padstack-related APIs for creating new vias and pads (#21)
108
+ - Change arc angle methods to return normalized angles; add degrees versions (#22)
109
+ - Add `board.get_origin` and `board.set_origin` (#20)
110
+ - Add `ArcTrack.length` (Thanh Duong, !12)
111
+ - Add `Footprint.models` (#31)
112
+ - Fix ability to create new graphic shapes on boards
113
+ - Fix the return value of `Board.update_items` and document it (#35)
114
+
96
115
  ### 0.2.0 (February 19, 2025)
97
116
 
98
117
  - Updates for KiCad 9.0.0 release
@@ -59,8 +59,11 @@ This should print out the version of KiCad you have connected to.
59
59
 
60
60
  ## Documentation
61
61
 
62
- There is no documentation separate from the code comments and examples yet, sorry! This will be
63
- more of a priority once the KiCad 9 release is stable.
62
+ The documentation created from this repository (via the `docs` directory and the docstrings in the
63
+ source code) is hosted at https://docs.kicad.org/kicad-python-main
64
+
65
+ Many things are still not documented or underdocumented -- contributions that expand the
66
+ documentation or add docstrings are welcomed.
64
67
 
65
68
  ## Examples
66
69
 
@@ -71,6 +74,20 @@ plugins, copy or symlink them into the appropriate plugins path in order for KiC
71
74
 
72
75
  ## Release History
73
76
 
77
+ ### 0.3.0 (March 29, 2025)
78
+
79
+ - Add support for footprint mounting style attribute (#19) (Thanh Duong, !10)
80
+ - Added `visible` property to `Field` and deprecate it from `TextAttributes` to match KiCad changes
81
+ - Improve version checking functions(Lucas Gerads, !11)
82
+ - Add missing board layers User.10 through User.45 (#23)
83
+ - Improve padstack-related APIs for creating new vias and pads (#21)
84
+ - Change arc angle methods to return normalized angles; add degrees versions (#22)
85
+ - Add `board.get_origin` and `board.set_origin` (#20)
86
+ - Add `ArcTrack.length` (Thanh Duong, !12)
87
+ - Add `Footprint.models` (#31)
88
+ - Fix ability to create new graphic shapes on boards
89
+ - Fix the return value of `Board.update_items` and document it (#35)
90
+
74
91
  ### 0.2.0 (February 19, 2025)
75
92
 
76
93
  - Updates for KiCad 9.0.0 release
@@ -71,6 +71,9 @@ from kipy.proto.board.board_pb2 import ( # noqa
71
71
  from kipy.proto.board.board_types_pb2 import ( #noqa
72
72
  BoardLayer
73
73
  )
74
+ from kipy.proto.board.board_commands_pb2 import ( #noqa
75
+ BoardOriginType
76
+ )
74
77
 
75
78
  class BoardLayerGraphicsDefaults(Wrapper):
76
79
  """The default properties for graphic items added on a given class of board layer"""
@@ -444,10 +447,13 @@ class Board:
444
447
  command = editor_commands_pb2.SaveSelectionToString()
445
448
  return self._kicad.send(command, editor_commands_pb2.SavedSelectionResponse).contents
446
449
 
447
- def update_items(self, items: Union[BoardItem, Sequence[BoardItem]]):
450
+ def update_items(self, items: Union[BoardItem, Sequence[BoardItem]]) -> List[BoardItem]:
448
451
  """Updates the properties of one or more items on the board. The items must already exist
449
452
  on the board, and are matched by internal UUID. All other properties of the items are
450
- updated from those passed in this call."""
453
+ updated from those passed in this call.
454
+
455
+ Returns the updated items, which may be different from the input items if any updates
456
+ failed to apply (for example, if any properties were out of range and were clamped)"""
451
457
  command = UpdateItems()
452
458
  command.header.document.CopyFrom(self._doc)
453
459
 
@@ -457,12 +463,16 @@ class Board:
457
463
  command.items.extend([pack_any(i.proto) for i in items])
458
464
 
459
465
  if len(command.items) == 0:
460
- return
466
+ return []
461
467
 
462
- return [
463
- unwrap(result.item)
464
- for result in self._kicad.send(command, UpdateItemsResponse).updated_items
465
- ]
468
+ return self._to_concrete_items(
469
+ [
470
+ unwrap(result.item)
471
+ for result in self._kicad.send(
472
+ command, UpdateItemsResponse
473
+ ).updated_items
474
+ ]
475
+ )
466
476
 
467
477
  def remove_items(self, items: Union[BoardItem, Sequence[BoardItem]]):
468
478
  """Deletes one or more items from the board"""
@@ -608,6 +618,26 @@ class Board:
608
618
  cmd.document.CopyFrom(self._doc)
609
619
  return TitleBlockInfo(self._kicad.send(cmd, base_types_pb2.TitleBlockInfo))
610
620
 
621
+ def get_origin(self, origin_type: board_commands_pb2.BoardOriginType.ValueType) -> Vector2:
622
+ """Retrieves the specified (grid or drill/place) board origin
623
+
624
+ .. versionadded:: 0.3.0"""
625
+ cmd = board_commands_pb2.GetBoardOrigin()
626
+ cmd.board.CopyFrom(self._doc)
627
+ cmd.type = origin_type
628
+ return Vector2(self._kicad.send(cmd, base_types_pb2.Vector2))
629
+
630
+ def set_origin(self, origin_type: board_commands_pb2.BoardOriginType.ValueType,
631
+ origin: Vector2):
632
+ """Sets the specified (grid or drill/place) board origin
633
+
634
+ .. versionadded:: 0.3.0"""
635
+ cmd = board_commands_pb2.SetBoardOrigin()
636
+ cmd.board.CopyFrom(self._doc)
637
+ cmd.type = origin_type
638
+ cmd.origin.CopyFrom(origin.proto)
639
+ self._kicad.send(cmd, Empty)
640
+
611
641
  @overload
612
642
  def expand_text_variables(self, text: str) -> str:
613
643
  ...
@@ -51,12 +51,14 @@ from kipy.geometry import (
51
51
  arc_end_angle,
52
52
  )
53
53
  from kipy.util import unpack_any
54
- from kipy.util.board_layer import is_copper_layer
54
+ from kipy.util.board_layer import is_copper_layer, iter_copper_layers
55
55
  from kipy.util.units import from_mm
56
56
  from kipy.wrapper import Item, Wrapper
57
57
 
58
58
  # Re-exported protobuf enum types
59
59
  from kipy.proto.board.board_types_pb2 import ( # noqa
60
+ PSS_CIRCLE,
61
+ PST_NORMAL,
60
62
  BoardLayer,
61
63
  ChamferedRectCorners,
62
64
  DrillShape,
@@ -264,6 +266,21 @@ class ArcTrack(BoardItem):
264
266
  def end_angle(self) -> Optional[float]:
265
267
  return arc_end_angle(self.start, self.mid, self.end)
266
268
 
269
+ def length(self) -> float:
270
+ """Calculates arc track length in nanometers
271
+
272
+ :return: The length of the arc, or the distance between the start and end points if the
273
+ arc is degenerate
274
+ .. versionadded:: 0.3.0"""
275
+ start = self.start_angle()
276
+ end = self.end_angle()
277
+
278
+ if start is None or end is None:
279
+ return (self.end - self.start).length()
280
+
281
+ delta_angle = abs(end - start)
282
+ return delta_angle*self.radius()
283
+
267
284
  def bounding_box(self) -> Box2:
268
285
  box = Box2()
269
286
  box.merge(self.start)
@@ -327,10 +344,12 @@ class BoardSegment(BoardShape, Segment):
327
344
  self._proto = board_types_pb2.BoardGraphicShape()
328
345
 
329
346
  if proto is not None:
347
+ assert proto.shape.WhichOneof("geometry") == "segment"
330
348
  self._proto.CopyFrom(proto)
349
+ else:
350
+ self._proto.shape.segment.SetInParent()
331
351
 
332
- assert self._proto.shape.WhichOneof("geometry") == "segment"
333
- Segment.__init__(self, self._proto.shape)
352
+ Segment.__init__(self, proto_ref=self._proto.shape)
334
353
 
335
354
  def __repr__(self) -> str:
336
355
  net_repr = (
@@ -351,10 +370,12 @@ class BoardArc(BoardShape, Arc):
351
370
  self._proto = board_types_pb2.BoardGraphicShape()
352
371
 
353
372
  if proto is not None:
373
+ assert proto.shape.WhichOneof("geometry") == "arc"
354
374
  self._proto.CopyFrom(proto)
375
+ else:
376
+ self._proto.shape.arc.SetInParent()
355
377
 
356
- assert self._proto.shape.WhichOneof("geometry") == "arc"
357
- Arc.__init__(self, self._proto.shape)
378
+ Arc.__init__(self, proto_ref=self._proto.shape)
358
379
 
359
380
  def __repr__(self) -> str:
360
381
  net_repr = (
@@ -375,10 +396,12 @@ class BoardCircle(BoardShape, Circle):
375
396
  self._proto = board_types_pb2.BoardGraphicShape()
376
397
 
377
398
  if proto is not None:
399
+ assert proto.shape.WhichOneof("geometry") == "circle"
378
400
  self._proto.CopyFrom(proto)
401
+ else:
402
+ self._proto.shape.circle.SetInParent()
379
403
 
380
- assert self._proto.shape.WhichOneof("geometry") == "circle"
381
- Circle.__init__(self, self._proto.shape)
404
+ Circle.__init__(self, proto_ref=self._proto.shape)
382
405
 
383
406
  def __repr__(self) -> str:
384
407
  net_repr = (
@@ -399,10 +422,12 @@ class BoardRectangle(BoardShape, Rectangle):
399
422
  self._proto = board_types_pb2.BoardGraphicShape()
400
423
 
401
424
  if proto is not None:
425
+ assert proto.shape.WhichOneof("geometry") == "rectangle"
402
426
  self._proto.CopyFrom(proto)
427
+ else:
428
+ self._proto.shape.rectangle.SetInParent()
403
429
 
404
- assert self._proto.shape.WhichOneof("geometry") == "rectangle"
405
- Rectangle.__init__(self, self._proto.shape)
430
+ Rectangle.__init__(self, proto_ref=self._proto.shape)
406
431
 
407
432
  def __repr__(self) -> str:
408
433
  net_repr = (
@@ -423,10 +448,12 @@ class BoardPolygon(BoardShape, Polygon):
423
448
  self._proto = board_types_pb2.BoardGraphicShape()
424
449
 
425
450
  if proto is not None:
451
+ assert proto.shape.WhichOneof("geometry") == "polygon"
426
452
  self._proto.CopyFrom(proto)
453
+ else:
454
+ self._proto.shape.polygon.SetInParent()
427
455
 
428
- assert self._proto.shape.WhichOneof("geometry") == "polygon"
429
- Polygon.__init__(self, self._proto.shape)
456
+ Polygon.__init__(self, proto_ref=self._proto.shape)
430
457
 
431
458
  def __repr__(self) -> str:
432
459
  net_repr = (
@@ -447,10 +474,12 @@ class BoardBezier(BoardShape, Bezier):
447
474
  self._proto = board_types_pb2.BoardGraphicShape()
448
475
 
449
476
  if proto is not None:
477
+ assert proto.shape.WhichOneof("geometry") == "bezier"
450
478
  self._proto.CopyFrom(proto)
479
+ else:
480
+ self._proto.shape.bezier.SetInParent()
451
481
 
452
- assert self._proto.shape.WhichOneof("geometry") == "bezier"
453
- Bezier.__init__(self, self._proto.shape)
482
+ Bezier.__init__(self, proto_ref=self._proto.shape)
454
483
 
455
484
  def __repr__(self) -> str:
456
485
  net_repr = (
@@ -652,6 +681,17 @@ class Field(BoardItem):
652
681
  def text(self, text: BoardText):
653
682
  self._proto.text.CopyFrom(text.proto)
654
683
 
684
+ @property
685
+ def visible(self) -> bool:
686
+ """
687
+ .. versionadded:: 0.3.0 with KiCad 9.0.1
688
+ """
689
+ return self._proto.visible
690
+
691
+ @visible.setter
692
+ def visible(self, visible: bool):
693
+ self._proto.visible = visible
694
+
655
695
 
656
696
  class ThermalSpokeSettings(Wrapper):
657
697
  def __init__(
@@ -1004,6 +1044,26 @@ class PadStack(BoardItem):
1004
1044
  def type(self, type: board_types_pb2.PadStackType.ValueType):
1005
1045
  self._proto.type = type
1006
1046
 
1047
+ layer_map = {layer.layer: layer for layer in self._proto.copper_layers}
1048
+
1049
+ required_layers = {
1050
+ board_types_pb2.PadStackType.PST_NORMAL: [BoardLayer.BL_F_Cu],
1051
+ board_types_pb2.PadStackType.PST_FRONT_INNER_BACK: [
1052
+ BoardLayer.BL_F_Cu,
1053
+ BoardLayer.BL_In1_Cu,
1054
+ BoardLayer.BL_B_Cu,
1055
+ ],
1056
+ board_types_pb2.PadStackType.PST_CUSTOM: [layer for layer in iter_copper_layers()]
1057
+ }.get(type, [])
1058
+
1059
+ for layer in layer_map.keys():
1060
+ if layer not in required_layers:
1061
+ self._proto.copper_layers.remove(layer_map[layer])
1062
+
1063
+ for layer in required_layers:
1064
+ if layer not in layer_map:
1065
+ self._add_copper_layer(layer)
1066
+
1007
1067
  @property
1008
1068
  def layers(self) -> Sequence[BoardLayer.ValueType]:
1009
1069
  return self._proto.layers
@@ -1071,6 +1131,10 @@ class PadStack(BoardItem):
1071
1131
  return self.back_outer_layers.solder_mask_mode == SolderMaskMode.SMM_MASKED
1072
1132
  return False
1073
1133
 
1134
+ def _add_copper_layer(self, layer: BoardLayer.ValueType) -> board_types_pb2.PadStackLayer:
1135
+ self._proto.copper_layers.append(board_types_pb2.PadStackLayer())
1136
+ self._proto.copper_layers[-1].layer = layer
1137
+ return self._proto.copper_layers[-1]
1074
1138
 
1075
1139
  class Pad(BoardItem):
1076
1140
  def __init__(self, proto: Optional[board_types_pb2.Pad] = None):
@@ -1078,6 +1142,8 @@ class Pad(BoardItem):
1078
1142
 
1079
1143
  if proto is not None:
1080
1144
  self._proto.CopyFrom(proto)
1145
+ else:
1146
+ self.padstack.type = PST_NORMAL
1081
1147
 
1082
1148
  @property
1083
1149
  def id(self) -> KIID:
@@ -1122,6 +1188,9 @@ class Via(BoardItem):
1122
1188
 
1123
1189
  if proto is not None:
1124
1190
  self._proto.CopyFrom(proto)
1191
+ else:
1192
+ self.type = ViaType.VT_THROUGH
1193
+ self.padstack.type = PST_NORMAL
1125
1194
 
1126
1195
  def __repr__(self) -> str:
1127
1196
  return (
@@ -1157,16 +1226,61 @@ class Via(BoardItem):
1157
1226
 
1158
1227
  @property
1159
1228
  def type(self) -> ViaType.ValueType:
1229
+ """The type of the via (through, blind/buried, or micro)
1230
+
1231
+ Setting this property will also update the padstack drill start and end layers as a
1232
+ side effect.
1233
+
1234
+ .. versionadded:: 0.3.0 with KiCad 9.0.1
1235
+ """
1160
1236
  return self._proto.type
1161
1237
 
1162
1238
  @type.setter
1163
1239
  def type(self, type: ViaType.ValueType):
1164
1240
  self._proto.type = type
1165
1241
 
1242
+ if (
1243
+ type == ViaType.VT_THROUGH
1244
+ or self.padstack.drill.start_layer == BoardLayer.BL_UNKNOWN
1245
+ or self.padstack.drill.end_layer == BoardLayer.BL_UNKNOWN
1246
+ ):
1247
+ self.padstack.drill.start_layer = BoardLayer.BL_F_Cu
1248
+ self.padstack.drill.end_layer = BoardLayer.BL_B_Cu
1249
+
1166
1250
  @property
1167
1251
  def padstack(self) -> PadStack:
1168
1252
  return PadStack(proto_ref=self._proto.pad_stack)
1169
1253
 
1254
+ @property
1255
+ def diameter(self) -> int:
1256
+ """A helper property to get or set the diameter of the via on all copper layers.
1257
+
1258
+ Warning: only makes sense if the via's padstack mode is PST_NORMAL. This will return the
1259
+ pad diameter on the front copper layer otherwise. Setting this property will set the
1260
+ padstack mode to PST_NORMAL as a side-effect.
1261
+
1262
+ To get or set the diameter for other padstack types, use the padstack property directly.
1263
+
1264
+ .. versionadded:: 0.3.0 with KiCad 9.0.1"""
1265
+ if len(self.padstack.copper_layers) == 0:
1266
+ raise ValueError("Unexpected empty padstack for via!")
1267
+
1268
+ return self.padstack.copper_layers[0].size.x
1269
+
1270
+ @diameter.setter
1271
+ def diameter(self, diameter: int):
1272
+ self.padstack.type = PST_NORMAL
1273
+ self.padstack.copper_layers[0].size = Vector2.from_xy(diameter, diameter)
1274
+
1275
+ @property
1276
+ def drill_diameter(self) -> int:
1277
+ """The diameter of the via's drill (KiCad only supports circular drills in vias)"""
1278
+ return self.padstack.drill.diameter.x
1279
+
1280
+ @drill_diameter.setter
1281
+ def drill_diameter(self, diameter: int):
1282
+ self.padstack.drill.diameter = Vector2.from_xy(diameter, diameter)
1283
+
1170
1284
 
1171
1285
  class FootprintAttributes(Wrapper):
1172
1286
  """The built-in attributes that a Footprint or FootprintInstance may have"""
@@ -1209,6 +1323,27 @@ class FootprintAttributes(Wrapper):
1209
1323
  def exclude_from_position_files(self, exclude: bool):
1210
1324
  self._proto.exclude_from_position_files = exclude
1211
1325
 
1326
+ @property
1327
+ def do_not_populate(self) -> bool:
1328
+ return self._proto.do_not_populate
1329
+
1330
+ @do_not_populate.setter
1331
+ def do_not_populate(self, do_not_populate: bool):
1332
+ self._proto.do_not_populate = do_not_populate
1333
+
1334
+ @property
1335
+ def mounting_style(self) -> board_types_pb2.FootprintMountingStyle.ValueType:
1336
+ """
1337
+ The mounting style of the footprint (SMD, through-hole, or unspecified)
1338
+
1339
+ .. versionadded:: 0.3.0 with KiCad 9.0.1
1340
+ """
1341
+ return self._proto.mounting_style
1342
+
1343
+ @mounting_style.setter
1344
+ def mounting_style(self, style: board_types_pb2.FootprintMountingStyle.ValueType):
1345
+ self._proto.mounting_style = style
1346
+
1212
1347
  class Footprint3DModel(Wrapper):
1213
1348
  """Represents a 3D model associated with a footprint"""
1214
1349
 
@@ -1218,6 +1353,14 @@ class Footprint3DModel(Wrapper):
1218
1353
  if proto is not None:
1219
1354
  self._proto.CopyFrom(proto)
1220
1355
 
1356
+ def __repr__(self) -> str:
1357
+ return (
1358
+ f"Footprint3DModel(filename='{self.filename}', "
1359
+ f"visisble={self.visible}, opacity={self.opacity}, "
1360
+ f"rotation=({self.rotation.x}, {self.rotation.y}, {self.rotation.z}), "
1361
+ f"scale=({self.scale.x}, {self.scale.y}, {self.scale.z}))"
1362
+ )
1363
+
1221
1364
  @property
1222
1365
  def filename(self) -> str:
1223
1366
  return self._proto.filename
@@ -1324,6 +1467,13 @@ class Footprint(Wrapper):
1324
1467
  or isinstance(item, Field)
1325
1468
  ]
1326
1469
 
1470
+ @property
1471
+ def models(self) -> Sequence[Footprint3DModel]:
1472
+ """Returns all 3D models in the footprint
1473
+
1474
+ .. versionadded:: 0.3.0"""
1475
+ return [item for item in self.items if isinstance(item, Footprint3DModel)]
1476
+
1327
1477
  def add_item(self, item: Wrapper):
1328
1478
  any = Any()
1329
1479
  any.Pack(item.proto)
@@ -1810,9 +1960,10 @@ class AlignedDimension(Dimension):
1810
1960
  self._proto = board_types_pb2.Dimension()
1811
1961
 
1812
1962
  if proto is not None:
1963
+ assert proto.WhichOneof("dimension_style") == "aligned"
1813
1964
  self._proto.CopyFrom(proto)
1814
-
1815
- assert self._proto.WhichOneof("dimension_style") == "aligned"
1965
+ else:
1966
+ self._proto.aligned.SetInParent()
1816
1967
 
1817
1968
  def __repr__(self) -> str:
1818
1969
  return (
@@ -1858,9 +2009,10 @@ class OrthogonalDimension(Dimension):
1858
2009
  self._proto = board_types_pb2.Dimension()
1859
2010
 
1860
2011
  if proto is not None:
2012
+ assert proto.WhichOneof("dimension_style") == "orthogonal"
1861
2013
  self._proto.CopyFrom(proto)
1862
-
1863
- assert self._proto.WhichOneof("dimension_style") == "orthogonal"
2014
+ else:
2015
+ self._proto.orthogonal.SetInParent()
1864
2016
 
1865
2017
  def __repr__(self) -> str:
1866
2018
  return f"OrthogonalDimension(start={self.start}, end={self.end}, alignment={self.alignment})"
@@ -1910,9 +2062,10 @@ class RadialDimension(Dimension):
1910
2062
  self._proto = board_types_pb2.Dimension()
1911
2063
 
1912
2064
  if proto is not None:
2065
+ assert proto.WhichOneof("dimension_style") == "radial"
1913
2066
  self._proto.CopyFrom(proto)
1914
-
1915
- assert self._proto.WhichOneof("dimension_style") == "radial"
2067
+ else:
2068
+ self._proto.radial.SetInParent()
1916
2069
 
1917
2070
  def __repr__(self) -> str:
1918
2071
  return (
@@ -1950,9 +2103,10 @@ class LeaderDimension(Dimension):
1950
2103
  self._proto = board_types_pb2.Dimension()
1951
2104
 
1952
2105
  if proto is not None:
2106
+ assert proto.WhichOneof("dimension_style") == "leader"
1953
2107
  self._proto.CopyFrom(proto)
1954
-
1955
- assert self._proto.WhichOneof("dimension_style") == "leader"
2108
+ else:
2109
+ self._proto.leader.SetInParent()
1956
2110
 
1957
2111
  def __repr__(self) -> str:
1958
2112
  return (
@@ -1990,9 +2144,10 @@ class CenterDimension(Dimension):
1990
2144
  self._proto = board_types_pb2.Dimension()
1991
2145
 
1992
2146
  if proto is not None:
2147
+ assert proto.WhichOneof("dimension_style") == "center"
1993
2148
  self._proto.CopyFrom(proto)
1994
-
1995
- assert self._proto.WhichOneof("dimension_style") == "center"
2149
+ else:
2150
+ self._proto.center.SetInParent()
1996
2151
 
1997
2152
  def __repr__(self) -> str:
1998
2153
  return f"CenterDimension(center={self.center}, end={self.end})"
@@ -18,6 +18,7 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
19
  # SOFTWARE.
20
20
 
21
+ from deprecated import deprecated
21
22
  from typing import Optional, Sequence
22
23
  from kipy.proto.common import types
23
24
  from kipy.proto.common.types import base_types_pb2
@@ -104,8 +105,21 @@ class TextAttributes(Wrapper):
104
105
  if proto is not None:
105
106
  self._proto.CopyFrom(proto)
106
107
 
108
+ def __repr__(self) -> str:
109
+ return (
110
+ f"TextAttributes(font_name={self.font_name}, angle={self.angle}, "
111
+ f"line_spacing={self.line_spacing}, italic={self.italic}, bold={self.bold}, "
112
+ f"underlined={self.underlined}, mirrored={self.mirrored}, multiline={self.multiline}, "
113
+ f"keep_upright={self.keep_upright}, size={self.size})"
114
+ )
115
+
107
116
  @property
117
+ @deprecated("This property will always return True in KiCad 9, and will be removed in KiCad 10")
108
118
  def visible(self) -> bool:
119
+ """
120
+ .. deprecated:: 0.3.0 removed in KiCad 9.0.1
121
+ Text items are always visible as of 9.0.1, only Fields can be set to hidden
122
+ """
109
123
  return self._proto.visible
110
124
 
111
125
  @visible.setter
@@ -121,12 +135,13 @@ class TextAttributes(Wrapper):
121
135
  self._proto.font_name = font_name
122
136
 
123
137
  @property
124
- def angle(self) -> types.Angle:
125
- return self._proto.angle
138
+ def angle(self) -> float:
139
+ """The orientation of the text in degrees"""
140
+ return self._proto.angle.value_degrees
126
141
 
127
142
  @angle.setter
128
- def angle(self, angle: types.Angle):
129
- self._proto.angle.CopyFrom(angle)
143
+ def angle(self, angle: float):
144
+ self._proto.angle.value_degrees = angle
130
145
 
131
146
  @property
132
147
  def line_spacing(self) -> float:
@@ -137,12 +152,12 @@ class TextAttributes(Wrapper):
137
152
  self._proto.line_spacing = line_spacing
138
153
 
139
154
  @property
140
- def stroke_width(self) -> types.Distance:
141
- return self._proto.stroke_width
155
+ def stroke_width(self) -> int:
156
+ return self._proto.stroke_width.value_nm
142
157
 
143
158
  @stroke_width.setter
144
- def stroke_width(self, stroke_width: types.Distance):
145
- self._proto.stroke_width.CopyFrom(stroke_width)
159
+ def stroke_width(self, stroke_width: int):
160
+ self._proto.stroke_width.value_nm = stroke_width
146
161
 
147
162
  @property
148
163
  def italic(self) -> bool:
@@ -461,8 +476,9 @@ class GraphicShape(Wrapper):
461
476
  class Segment(GraphicShape):
462
477
  """Represents a base graphic segment (not a board or schematic item)"""
463
478
 
464
- def __init__(self, proto: Optional[base_types_pb2.GraphicShape] = None):
465
- self._graphic_proto = base_types_pb2.GraphicShape()
479
+ def __init__(self, proto: Optional[base_types_pb2.GraphicShape] = None,
480
+ proto_ref: Optional[base_types_pb2.GraphicShape] = None):
481
+ self._graphic_proto = proto_ref if proto_ref is not None else base_types_pb2.GraphicShape()
466
482
 
467
483
  if proto is not None:
468
484
  self._graphic_proto.CopyFrom(proto)
@@ -496,8 +512,9 @@ class Segment(GraphicShape):
496
512
  class Arc(GraphicShape):
497
513
  """Represents a generic graphical arc (not a board or schematic item)"""
498
514
 
499
- def __init__(self, proto: Optional[base_types_pb2.GraphicShape] = None):
500
- self._graphic_proto = base_types_pb2.GraphicShape()
515
+ def __init__(self, proto: Optional[base_types_pb2.GraphicShape] = None,
516
+ proto_ref: Optional[base_types_pb2.GraphicShape] = None):
517
+ self._graphic_proto = proto_ref if proto_ref is not None else base_types_pb2.GraphicShape()
501
518
 
502
519
  if proto is not None:
503
520
  self._graphic_proto.CopyFrom(proto)
@@ -569,8 +586,9 @@ class Arc(GraphicShape):
569
586
  class Circle(GraphicShape):
570
587
  """Represents a graphic circle (not a board or schematic item)"""
571
588
 
572
- def __init__(self, proto: Optional[base_types_pb2.GraphicShape] = None):
573
- self._graphic_proto = base_types_pb2.GraphicShape()
589
+ def __init__(self, proto: Optional[base_types_pb2.GraphicShape] = None,
590
+ proto_ref: Optional[base_types_pb2.GraphicShape] = None):
591
+ self._graphic_proto = proto_ref if proto_ref is not None else base_types_pb2.GraphicShape()
574
592
 
575
593
  if proto is not None:
576
594
  self._graphic_proto.CopyFrom(proto)
@@ -608,8 +626,9 @@ class Circle(GraphicShape):
608
626
  class Rectangle(GraphicShape):
609
627
  """Represents a graphic rectangle (not a board or schematic item)"""
610
628
 
611
- def __init__(self, proto: Optional[base_types_pb2.GraphicShape] = None):
612
- self._graphic_proto = base_types_pb2.GraphicShape()
629
+ def __init__(self, proto: Optional[base_types_pb2.GraphicShape] = None,
630
+ proto_ref: Optional[base_types_pb2.GraphicShape] = None):
631
+ self._graphic_proto = proto_ref if proto_ref is not None else base_types_pb2.GraphicShape()
613
632
 
614
633
  if proto is not None:
615
634
  self._graphic_proto.CopyFrom(proto)
@@ -640,8 +659,9 @@ class Rectangle(GraphicShape):
640
659
  class Polygon(GraphicShape):
641
660
  """Represents a graphic polygon (not a board or schematic item)"""
642
661
 
643
- def __init__(self, proto: Optional[base_types_pb2.GraphicShape] = None):
644
- self._graphic_proto = base_types_pb2.GraphicShape()
662
+ def __init__(self, proto: Optional[base_types_pb2.GraphicShape] = None,
663
+ proto_ref: Optional[base_types_pb2.GraphicShape] = None):
664
+ self._graphic_proto = proto_ref if proto_ref is not None else base_types_pb2.GraphicShape()
645
665
 
646
666
  if proto is not None:
647
667
  self._graphic_proto.CopyFrom(proto)
@@ -666,8 +686,9 @@ class Polygon(GraphicShape):
666
686
  class Bezier(GraphicShape):
667
687
  """Represents a graphic bezier curve (not a board or schematic item)"""
668
688
 
669
- def __init__(self, proto: Optional[base_types_pb2.GraphicShape] = None):
670
- self._graphic_proto = base_types_pb2.GraphicShape()
689
+ def __init__(self, proto: Optional[base_types_pb2.GraphicShape] = None,
690
+ proto_ref: Optional[base_types_pb2.GraphicShape] = None):
691
+ self._graphic_proto = proto_ref if proto_ref is not None else base_types_pb2.GraphicShape()
671
692
 
672
693
  if proto is not None:
673
694
  self._graphic_proto.CopyFrom(proto)