Refactored the BaseOutput class to make it more similar to the YAML
Now the options are just an attribute of the outputs. It means we have a set of classes for the options. All the output content is parsed by the classes, including the Layers. Now the layers support a simple string (with interesting shortcuts), a list of strings or the original list of dicts.
This commit is contained in:
parent
1c4c94cfa2
commit
4368364489
|
|
@ -17,6 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
them.
|
||||
|
||||
### Added
|
||||
- The layers entry is much more flexible now.
|
||||
Many changes, read the README.md
|
||||
- -e/--schematic option to specify any schematic (not just derived from the PCB
|
||||
name.
|
||||
- -x/--example option to generate a complete configuration example.
|
||||
|
|
|
|||
1
Makefile
1
Makefile
|
|
@ -24,6 +24,7 @@ test: lint
|
|||
|
||||
test_local: lint
|
||||
rm -rf output
|
||||
rm -f example.kiplot.yaml
|
||||
$(PY_COV) erase
|
||||
pytest-3 --test_dir output
|
||||
$(PY_COV) report
|
||||
|
|
|
|||
527
README.md
527
README.md
|
|
@ -194,177 +194,323 @@ outputs:
|
|||
use_gerber_net_attributes: false
|
||||
|
||||
layers:
|
||||
- layer: F.Cu
|
||||
suffix: F_Cu
|
||||
- layer: B.Cu
|
||||
suffix: B_Cu
|
||||
- 'F.Cu'
|
||||
- 'B.Cu'
|
||||
```
|
||||
|
||||
Most options are the same you'll find in the KiCad dialogs.
|
||||
|
||||
|
||||
### Specifying the layers
|
||||
|
||||
You have various ways to specify the layers. If you need to specify just one layer you can just use its name:
|
||||
|
||||
```
|
||||
layers: 'F.Cu'
|
||||
```
|
||||
|
||||
If you want to specify all the available layers:
|
||||
|
||||
```
|
||||
layers: 'all'
|
||||
```
|
||||
|
||||
You can also select the layers you want in KiCad (using File, Plot dialog) and save your PCB.
|
||||
Then you just need to use:
|
||||
|
||||
```
|
||||
layers: 'selected'
|
||||
```
|
||||
|
||||
You can also use any of the following grup of layers:
|
||||
|
||||
- **copper** all the copper layers
|
||||
- **technical** all the technical layers (silk sreen, solder mask, paste, adhesive, etc.)
|
||||
- **user** all the user layers (draw, comments, eco, margin, edge cuts, etc.)
|
||||
|
||||
You can also mix the above definitions using a list:
|
||||
|
||||
```
|
||||
layers:
|
||||
- 'copper'
|
||||
- 'Dwgs.User'
|
||||
```
|
||||
|
||||
This will select all the copper layers and the user drawings.
|
||||
Note that the above mentioned options will use file name suffixes and descriptions selected automatically.
|
||||
If you want to use a particular suffix and provide better descriptions you can use the following format:
|
||||
|
||||
```
|
||||
layers:
|
||||
- layer: 'F.Cu'
|
||||
suffix: 'F_Cu'
|
||||
description: 'Front copper'
|
||||
- layer: 'B.Cu'
|
||||
suffix: 'B_Cu'
|
||||
description: 'Bottom copper'
|
||||
```
|
||||
|
||||
You can also mix the styles:
|
||||
|
||||
```
|
||||
layers:
|
||||
- 'copper'
|
||||
- layer: 'Cmts.User'
|
||||
suffix: 'Cmts_User'
|
||||
description: 'User comments'
|
||||
- 'Dwgs.User'
|
||||
```
|
||||
|
||||
If you need to use the same list of layers for various outputs you can use YAML anchors.
|
||||
The first time you define the list of layers just assign an ancho, here is an example:
|
||||
|
||||
```
|
||||
layers: &copper_and_cmts
|
||||
- copper
|
||||
- 'Cmts.User'
|
||||
```
|
||||
|
||||
Next time you need this list just use an alias, like this:
|
||||
|
||||
```
|
||||
layers: *copper_and_cmts
|
||||
```
|
||||
|
||||
### Supported outputs:
|
||||
|
||||
* DXF (Drawing Exchange Format)
|
||||
* Type: `dxf`
|
||||
* Description: Exports the PCB to 2D mechanical EDA tools (like AutoCAD).
|
||||
This output is what you get from the File/Plot menu in pcbnew.
|
||||
* Options:
|
||||
- `drill_marks`: [string='full'] what to use to indicate the drill places, can be none, small or full (for real scale).
|
||||
- `exclude_edge_layer`: [boolean=true] do not include the PCB edge layer.
|
||||
- `exclude_pads_from_silkscreen`: [boolean=false] do not plot the component pads in the silk screen.
|
||||
- `force_plot_invisible_refs_vals`: [boolean=false] include references and values even when they are marked as invisible.
|
||||
- `metric_units`: [boolean=false] use mm instead of inches.
|
||||
- `plot_footprint_refs`: [boolean=true] include the footprint references.
|
||||
- `plot_footprint_values`: [boolean=true] include the footprint values.
|
||||
- `plot_sheet_reference`: [boolean=false] currently without effect.
|
||||
- `polygon_mode`: [boolean=true] plot using the contour, instead of the center line.
|
||||
- `sketch_plot`: [boolean=false] don't fill objects, just draw the outline.
|
||||
- `tent_vias`: [boolean=true] cover the vias.
|
||||
- `use_aux_axis_as_origin`: [boolean=false] use the auxiliar axis as origin for coordinates.
|
||||
* Valid keys:
|
||||
- `comment`: [string=''] A comment for documentation purposes.
|
||||
- `dir`: [string='.'] Output directory for the generated files.
|
||||
- `layers`: [list(dict)|list(string)|string] [all,selected,copper,technical,user]
|
||||
List of PCB layers to plot.
|
||||
* Valid keys:
|
||||
- `description`: [string=''] A description for the layer, for documentation purposes.
|
||||
- `layer`: [string=''] Name of the layer. As you see it in KiCad.
|
||||
- `suffix`: [string=''] Suffix used in file names related to this layer. Derived from the name if not specified.
|
||||
- `name`: [string=''] Used to identify this particular output definition.
|
||||
- `options`: [dict] Options for the `dxf` output.
|
||||
* Valid keys:
|
||||
- `drill_marks`: [string='full'] what to use to indicate the drill places, can be none, small or full (for real scale).
|
||||
- `exclude_edge_layer`: [boolean=true] do not include the PCB edge layer.
|
||||
- `exclude_pads_from_silkscreen`: [boolean=false] do not plot the component pads in the silk screen.
|
||||
- `force_plot_invisible_refs_vals`: [boolean=false] include references and values even when they are marked as invisible.
|
||||
- `metric_units`: [boolean=false] use mm instead of inches.
|
||||
- `plot_footprint_refs`: [boolean=true] include the footprint references.
|
||||
- `plot_footprint_values`: [boolean=true] include the footprint values.
|
||||
- `plot_sheet_reference`: [boolean=false] currently without effect.
|
||||
- `polygon_mode`: [boolean=true] plot using the contour, instead of the center line.
|
||||
- `sketch_plot`: [boolean=false] don't fill objects, just draw the outline.
|
||||
- `tent_vias`: [boolean=true] cover the vias.
|
||||
- `use_aux_axis_as_origin`: [boolean=false] use the auxiliar axis as origin for coordinates.
|
||||
|
||||
* Excellon drill format
|
||||
* Type: `excellon`
|
||||
* Description: This is the main format for the drilling machine.
|
||||
You can create a map file for documentation purposes.
|
||||
This output is what you get from the 'File/Fabrication output/Drill Files' menu in pcbnew.
|
||||
* Options:
|
||||
- `map`: [dict|string] [hpgl,ps,gerber,dxf,svg,pdf] format for a graphical drill map.
|
||||
Not generated unless a format is specified.
|
||||
* Options:
|
||||
- `type`: [string='pdf'] [hpgl,ps,gerber,dxf,svg,pdf] format for a graphical drill map.
|
||||
- `metric_units`: [boolean=true] use metric units instead of inches.
|
||||
- `minimal_header`: [boolean=false] use a minimal header in the file.
|
||||
- `mirror_y_axis`: [boolean=false] invert the Y axis.
|
||||
- `pth_and_npth_single_file`: [boolean=true] generate one file for both, plated holes and non-plated holes, instead of two separated files.
|
||||
- `report`: [dict|string] name of the drill report. Not generated unless a name is specified.
|
||||
* Options:
|
||||
- `filename`: [string=''] name of the drill report. Not generated unless a name is specified.
|
||||
- `use_aux_axis_as_origin`: [boolean=false] use the auxiliar axis as origin for coordinates.
|
||||
* Valid keys:
|
||||
- `comment`: [string=''] A comment for documentation purposes.
|
||||
- `dir`: [string='.'] Output directory for the generated files.
|
||||
- `name`: [string=''] Used to identify this particular output definition.
|
||||
- `options`: [dict] Options for the `excellon` output.
|
||||
* Valid keys:
|
||||
- `map`: [dict|string] [hpgl,ps,gerber,dxf,svg,pdf] format for a graphical drill map.
|
||||
Not generated unless a format is specified.
|
||||
* Valid keys:
|
||||
- No available options
|
||||
- `metric_units`: [boolean=true] use metric units instead of inches.
|
||||
- `minimal_header`: [boolean=false] use a minimal header in the file.
|
||||
- `mirror_y_axis`: [boolean=false] invert the Y axis.
|
||||
- `pth_and_npth_single_file`: [boolean=true] generate one file for both, plated holes and non-plated holes, instead of two separated files.
|
||||
- `report`: [dict|string] name of the drill report. Not generated unless a name is specified.
|
||||
* Valid keys:
|
||||
- `filename`: [string=''] name of the drill report. Not generated unless a name is specified.
|
||||
- `use_aux_axis_as_origin`: [boolean=false] use the auxiliar axis as origin for coordinates.
|
||||
|
||||
* Gerber drill format
|
||||
* Type: `gerb_drill`
|
||||
* Description: This is the information for the drilling machine in gerber format.
|
||||
You can create a map file for documentation purposes.
|
||||
This output is what you get from the 'File/Fabrication output/Drill Files' menu in pcbnew.
|
||||
* Options:
|
||||
- `map`: [dict|string] [hpgl,ps,gerber,dxf,svg,pdf] format for a graphical drill map.
|
||||
Not generated unless a format is specified.
|
||||
* Options:
|
||||
- `type`: [string='pdf'] [hpgl,ps,gerber,dxf,svg,pdf] format for a graphical drill map.
|
||||
- `report`: [dict|string] name of the drill report. Not generated unless a name is specified.
|
||||
* Options:
|
||||
- `filename`: [string=''] name of the drill report. Not generated unless a name is specified.
|
||||
- `use_aux_axis_as_origin`: [boolean=false] use the auxiliar axis as origin for coordinates.
|
||||
* Valid keys:
|
||||
- `comment`: [string=''] A comment for documentation purposes.
|
||||
- `dir`: [string='.'] Output directory for the generated files.
|
||||
- `name`: [string=''] Used to identify this particular output definition.
|
||||
- `options`: [dict] Options for the `gerb_drill` output.
|
||||
* Valid keys:
|
||||
- `map`: [dict|string] [hpgl,ps,gerber,dxf,svg,pdf] format for a graphical drill map.
|
||||
Not generated unless a format is specified.
|
||||
* Valid keys:
|
||||
- No available options
|
||||
- `report`: [dict|string] name of the drill report. Not generated unless a name is specified.
|
||||
* Valid keys:
|
||||
- `filename`: [string=''] name of the drill report. Not generated unless a name is specified.
|
||||
- `use_aux_axis_as_origin`: [boolean=false] use the auxiliar axis as origin for coordinates.
|
||||
|
||||
* Gerber format
|
||||
* Type: `gerber`
|
||||
* Description: This is the main fabrication format for the PCB.
|
||||
This output is what you get from the File/Plot menu in pcbnew.
|
||||
* Options:
|
||||
- `create_gerber_job_file`: [boolean=true] creates a file with information about all the generated gerbers.
|
||||
You can use it in gerbview to load all gerbers at once.
|
||||
- `exclude_edge_layer`: [boolean=true] do not include the PCB edge layer.
|
||||
- `exclude_pads_from_silkscreen`: [boolean=false] do not plot the component pads in the silk screen.
|
||||
- `force_plot_invisible_refs_vals`: [boolean=false] include references and values even when they are marked as invisible.
|
||||
- `gerber_precision`: [number=4.6] this the gerber coordinate format, can be 4.5 or 4.6.
|
||||
- `line_width`: [number=0.1] [0.02,2] line_width for objects without width [mm].
|
||||
- `plot_footprint_refs`: [boolean=true] include the footprint references.
|
||||
- `plot_footprint_values`: [boolean=true] include the footprint values.
|
||||
- `plot_sheet_reference`: [boolean=false] currently without effect.
|
||||
- `subtract_mask_from_silk`: [boolean=false] substract the solder mask from the silk screen.
|
||||
- `tent_vias`: [boolean=true] cover the vias.
|
||||
- `use_aux_axis_as_origin`: [boolean=false] use the auxiliar axis as origin for coordinates.
|
||||
- `use_gerber_net_attributes`: [boolean=true] include netlist metadata.
|
||||
- `use_gerber_x2_attributes`: [boolean=true] use the extended X2 format.
|
||||
- `use_protel_extensions`: [boolean=false] use legacy Protel file extensions.
|
||||
* Valid keys:
|
||||
- `comment`: [string=''] A comment for documentation purposes.
|
||||
- `dir`: [string='.'] Output directory for the generated files.
|
||||
- `layers`: [list(dict)|list(string)|string] [all,selected,copper,technical,user]
|
||||
List of PCB layers to plot.
|
||||
* Valid keys:
|
||||
- `description`: [string=''] A description for the layer, for documentation purposes.
|
||||
- `layer`: [string=''] Name of the layer. As you see it in KiCad.
|
||||
- `suffix`: [string=''] Suffix used in file names related to this layer. Derived from the name if not specified.
|
||||
- `name`: [string=''] Used to identify this particular output definition.
|
||||
- `options`: [dict] Options for the `gerber` output.
|
||||
* Valid keys:
|
||||
- `create_gerber_job_file`: [boolean=true] creates a file with information about all the generated gerbers.
|
||||
You can use it in gerbview to load all gerbers at once.
|
||||
- `exclude_edge_layer`: [boolean=true] do not include the PCB edge layer.
|
||||
- `exclude_pads_from_silkscreen`: [boolean=false] do not plot the component pads in the silk screen.
|
||||
- `force_plot_invisible_refs_vals`: [boolean=false] include references and values even when they are marked as invisible.
|
||||
- `gerber_precision`: [number=4.6] this the gerber coordinate format, can be 4.5 or 4.6.
|
||||
- `line_width`: [number=0.1] [0.02,2] line_width for objects without width [mm].
|
||||
- `plot_footprint_refs`: [boolean=true] include the footprint references.
|
||||
- `plot_footprint_values`: [boolean=true] include the footprint values.
|
||||
- `plot_sheet_reference`: [boolean=false] currently without effect.
|
||||
- `subtract_mask_from_silk`: [boolean=false] substract the solder mask from the silk screen.
|
||||
- `tent_vias`: [boolean=true] cover the vias.
|
||||
- `use_aux_axis_as_origin`: [boolean=false] use the auxiliar axis as origin for coordinates.
|
||||
- `use_gerber_net_attributes`: [boolean=true] include netlist metadata.
|
||||
- `use_gerber_x2_attributes`: [boolean=true] use the extended X2 format.
|
||||
- `use_protel_extensions`: [boolean=false] use legacy Protel file extensions.
|
||||
|
||||
* HPGL (Hewlett & Packard Graphics Language)
|
||||
* Type: `hpgl`
|
||||
* Description: Exports the PCB for plotters and laser printers.
|
||||
This output is what you get from the File/Plot menu in pcbnew.
|
||||
* Options:
|
||||
- `drill_marks`: [string='full'] what to use to indicate the drill places, can be none, small or full (for real scale).
|
||||
- `exclude_edge_layer`: [boolean=true] do not include the PCB edge layer.
|
||||
- `exclude_pads_from_silkscreen`: [boolean=false] do not plot the component pads in the silk screen.
|
||||
- `force_plot_invisible_refs_vals`: [boolean=false] include references and values even when they are marked as invisible.
|
||||
- `mirror_plot`: [boolean=false] plot mirrored.
|
||||
- `pen_number`: [number=1] [1,16] pen number.
|
||||
- `pen_speed`: [number=20] [1,99] pen speed.
|
||||
- `pen_width`: [number=15] [0,100] pen diameter in MILS, useful to fill areas. However, it is in mm in HPGL files.
|
||||
- `plot_footprint_refs`: [boolean=true] include the footprint references.
|
||||
- `plot_footprint_values`: [boolean=true] include the footprint values.
|
||||
- `plot_sheet_reference`: [boolean=false] currently without effect.
|
||||
- `scaling`: [number=0] scale factor (0 means autoscaling).
|
||||
- `sketch_plot`: [boolean=false] don't fill objects, just draw the outline.
|
||||
- `tent_vias`: [boolean=true] cover the vias.
|
||||
* Valid keys:
|
||||
- `comment`: [string=''] A comment for documentation purposes.
|
||||
- `dir`: [string='.'] Output directory for the generated files.
|
||||
- `layers`: [list(dict)|list(string)|string] [all,selected,copper,technical,user]
|
||||
List of PCB layers to plot.
|
||||
* Valid keys:
|
||||
- `description`: [string=''] A description for the layer, for documentation purposes.
|
||||
- `layer`: [string=''] Name of the layer. As you see it in KiCad.
|
||||
- `suffix`: [string=''] Suffix used in file names related to this layer. Derived from the name if not specified.
|
||||
- `name`: [string=''] Used to identify this particular output definition.
|
||||
- `options`: [dict] Options for the `hpgl` output.
|
||||
* Valid keys:
|
||||
- `drill_marks`: [string='full'] what to use to indicate the drill places, can be none, small or full (for real scale).
|
||||
- `exclude_edge_layer`: [boolean=true] do not include the PCB edge layer.
|
||||
- `exclude_pads_from_silkscreen`: [boolean=false] do not plot the component pads in the silk screen.
|
||||
- `force_plot_invisible_refs_vals`: [boolean=false] include references and values even when they are marked as invisible.
|
||||
- `mirror_plot`: [boolean=false] plot mirrored.
|
||||
- `pen_number`: [number=1] [1,16] pen number.
|
||||
- `pen_speed`: [number=20] [1,99] pen speed.
|
||||
- `pen_width`: [number=15] [0,100] pen diameter in MILS, useful to fill areas. However, it is in mm in HPGL files.
|
||||
- `plot_footprint_refs`: [boolean=true] include the footprint references.
|
||||
- `plot_footprint_values`: [boolean=true] include the footprint values.
|
||||
- `plot_sheet_reference`: [boolean=false] currently without effect.
|
||||
- `scaling`: [number=0] scale factor (0 means autoscaling).
|
||||
- `sketch_plot`: [boolean=false] don't fill objects, just draw the outline.
|
||||
- `tent_vias`: [boolean=true] cover the vias.
|
||||
|
||||
* IBoM (Interactive HTML BoM)
|
||||
* Type: `ibom`
|
||||
* Description: Generates an interactive web page useful to identify the position of the components in the PCB.
|
||||
For more information: https://github.com/INTI-CMNB/InteractiveHtmlBom
|
||||
This output is what you get from the InteractiveHtmlBom plug-in (pcbnew).
|
||||
* Options:
|
||||
- `blacklist`: [string=''] List of comma separated blacklisted components or prefixes with *. E.g. 'X1,MH*'.
|
||||
- `blacklist_empty_val`: [boolean=false] Blacklist components with empty value.
|
||||
- `board_rotation`: [number=0] Board rotation in degrees (-180 to 180). Will be rounded to multiple of 5.
|
||||
- `bom_view`: [string='left-right'] [bom-only,left-right,top-bottom] Default BOM view.
|
||||
- `checkboxes`: [string='Sourced,Placed'] Comma separated list of checkbox columns.
|
||||
- `dark_mode`: [boolean=false] Default to dark mode.
|
||||
- `dnp_field`: [string=''] Name of the extra field that indicates do not populate status. Components with this field not empty will be
|
||||
blacklisted.
|
||||
- `extra_fields`: [string=''] Comma separated list of extra fields to pull from netlist or xml file.
|
||||
- `hide_pads`: [boolean=false] Hide footprint pads by default.
|
||||
- `hide_silkscreen`: [boolean=false] Hide silkscreen by default.
|
||||
- `highlight_pin1`: [boolean=false] Highlight pin1 by default.
|
||||
- `include_nets`: [boolean=false] Include netlist information in output..
|
||||
- `include_tracks`: [boolean=false] Include track/zone information in output. F.Cu and B.Cu layers only.
|
||||
- `layer_view`: [string='FB'] [F,FB,B] Default layer view.
|
||||
- `name_format`: [string='ibom'] Output file name format supports substitutions:
|
||||
%f : original pcb file name without extension.
|
||||
%p : pcb/project title from pcb metadata.
|
||||
%c : company from pcb metadata.
|
||||
%r : revision from pcb metadata.
|
||||
%d : pcb date from metadata if available, file modification date otherwise.
|
||||
%D : bom generation date.
|
||||
%T : bom generation time. Extension .html will be added automatically.
|
||||
- `netlist_file`: [string=''] Path to netlist or xml file.
|
||||
- `no_blacklist_virtual`: [boolean=false] Do not blacklist virtual components.
|
||||
- `no_redraw_on_drag`: [boolean=false] Do not redraw pcb on drag by default.
|
||||
- `normalize_field_case`: [boolean=false] Normalize extra field name case. E.g. 'MPN' and 'mpn' will be considered the same field.
|
||||
- `show_fabrication`: [boolean=false] Show fabrication layer by default.
|
||||
- `sort_order`: [string='C,R,L,D,U,Y,X,F,SW,A,~,HS,CNN,J,P,NT,MH'] Default sort order for components. Must contain '~' once.
|
||||
- `variant_field`: [string=''] Name of the extra field that stores board variant for component.
|
||||
- `variants_blacklist`: [string=''] List of board variants to exclude from the BOM.
|
||||
- `variants_whitelist`: [string=''] List of board variants to include in the BOM.
|
||||
* Valid keys:
|
||||
- `comment`: [string=''] A comment for documentation purposes.
|
||||
- `dir`: [string='.'] Output directory for the generated files.
|
||||
- `name`: [string=''] Used to identify this particular output definition.
|
||||
- `options`: [dict] Options for the `ibom` output.
|
||||
* Valid keys:
|
||||
- `blacklist`: [string=''] List of comma separated blacklisted components or prefixes with *. E.g. 'X1,MH*'.
|
||||
- `blacklist_empty_val`: [boolean=false] Blacklist components with empty value.
|
||||
- `board_rotation`: [number=0] Board rotation in degrees (-180 to 180). Will be rounded to multiple of 5.
|
||||
- `bom_view`: [string='left-right'] [bom-only,left-right,top-bottom] Default BOM view.
|
||||
- `checkboxes`: [string='Sourced,Placed'] Comma separated list of checkbox columns.
|
||||
- `dark_mode`: [boolean=false] Default to dark mode.
|
||||
- `dnp_field`: [string=''] Name of the extra field that indicates do not populate status. Components with this field not empty will be
|
||||
blacklisted.
|
||||
- `extra_fields`: [string=''] Comma separated list of extra fields to pull from netlist or xml file.
|
||||
- `hide_pads`: [boolean=false] Hide footprint pads by default.
|
||||
- `hide_silkscreen`: [boolean=false] Hide silkscreen by default.
|
||||
- `highlight_pin1`: [boolean=false] Highlight pin1 by default.
|
||||
- `include_nets`: [boolean=false] Include netlist information in output..
|
||||
- `include_tracks`: [boolean=false] Include track/zone information in output. F.Cu and B.Cu layers only.
|
||||
- `layer_view`: [string='FB'] [F,FB,B] Default layer view.
|
||||
- `name_format`: [string='ibom'] Output file name format supports substitutions:
|
||||
%f : original pcb file name without extension.
|
||||
%p : pcb/project title from pcb metadata.
|
||||
%c : company from pcb metadata.
|
||||
%r : revision from pcb metadata.
|
||||
%d : pcb date from metadata if available, file modification date otherwise.
|
||||
%D : bom generation date.
|
||||
%T : bom generation time. Extension .html will be added automatically.
|
||||
- `netlist_file`: [string=''] Path to netlist or xml file.
|
||||
- `no_blacklist_virtual`: [boolean=false] Do not blacklist virtual components.
|
||||
- `no_redraw_on_drag`: [boolean=false] Do not redraw pcb on drag by default.
|
||||
- `normalize_field_case`: [boolean=false] Normalize extra field name case. E.g. 'MPN' and 'mpn' will be considered the same field.
|
||||
- `show_fabrication`: [boolean=false] Show fabrication layer by default.
|
||||
- `sort_order`: [string='C,R,L,D,U,Y,X,F,SW,A,~,HS,CNN,J,P,NT,MH'] Default sort order for components. Must contain '~' once.
|
||||
- `variant_field`: [string=''] Name of the extra field that stores board variant for component.
|
||||
- `variants_blacklist`: [string=''] List of board variants to exclude from the BOM.
|
||||
- `variants_whitelist`: [string=''] List of board variants to include in the BOM.
|
||||
|
||||
* KiBoM (KiCad Bill of Materials)
|
||||
* Type: `kibom`
|
||||
* Description: Used to generate the BoM in HTML or CSV format using the KiBoM plug-in.
|
||||
For more information: https://github.com/INTI-CMNB/KiBoM
|
||||
This output is what you get from the 'Tools/Generate Bill of Materials' menu in eeschema.
|
||||
* Options:
|
||||
- `conf`: [string='bom.ini'] BoM configuration file, relative to PCB.
|
||||
- `format`: [string='HTML'] [HTML,CSV] format for the BoM.
|
||||
- `number`: [number=1] Number of boards to build (components multiplier).
|
||||
- `separator`: [string=','] CSV Separator.
|
||||
- `variant`: [string=''] Board variant(s), used to determine which components
|
||||
are output to the BoM. To specify multiple variants,
|
||||
with a BOM file exported for each variant, separate
|
||||
variants with the ';' (semicolon) character.
|
||||
* Valid keys:
|
||||
- `comment`: [string=''] A comment for documentation purposes.
|
||||
- `dir`: [string='.'] Output directory for the generated files.
|
||||
- `name`: [string=''] Used to identify this particular output definition.
|
||||
- `options`: [dict] Options for the `kibom` output.
|
||||
* Valid keys:
|
||||
- `conf`: [string='bom.ini'] BoM configuration file, relative to PCB.
|
||||
- `format`: [string='HTML'] [HTML,CSV] format for the BoM.
|
||||
- `number`: [number=1] Number of boards to build (components multiplier).
|
||||
- `separator`: [string=','] CSV Separator.
|
||||
- `variant`: [string=''] Board variant(s), used to determine which components
|
||||
are output to the BoM. To specify multiple variants,
|
||||
with a BOM file exported for each variant, separate
|
||||
variants with the ';' (semicolon) character.
|
||||
|
||||
* PDF (Portable Document Format)
|
||||
* Type: `pdf`
|
||||
* Description: Exports the PCB to the most common exhange format. Suitable for printing.
|
||||
Note that this output isn't the best for documating your project.
|
||||
This output is what you get from the File/Plot menu in pcbnew.
|
||||
* Options:
|
||||
* Valid keys:
|
||||
- `comment`: [string=''] A comment for documentation purposes.
|
||||
- `dir`: [string='.'] Output directory for the generated files.
|
||||
- `drill_marks`: [string='full'] what to use to indicate the drill places, can be none, small or full (for real scale).
|
||||
- `exclude_edge_layer`: [boolean=true] do not include the PCB edge layer.
|
||||
- `exclude_pads_from_silkscreen`: [boolean=false] do not plot the component pads in the silk screen.
|
||||
- `force_plot_invisible_refs_vals`: [boolean=false] include references and values even when they are marked as invisible.
|
||||
- `line_width`: [number=0.1] [0.02,2] for objects without width [mm].
|
||||
- `mirror_plot`: [boolean=false] plot mirrored.
|
||||
- `negative_plot`: [boolean=false] invert black and white.
|
||||
- `layers`: [list(dict)|list(string)|string] [all,selected,copper,technical,user]
|
||||
List of PCB layers to plot.
|
||||
* Valid keys:
|
||||
- `description`: [string=''] A description for the layer, for documentation purposes.
|
||||
- `layer`: [string=''] Name of the layer. As you see it in KiCad.
|
||||
- `suffix`: [string=''] Suffix used in file names related to this layer. Derived from the name if not specified.
|
||||
- `name`: [string=''] Used to identify this particular output definition.
|
||||
- `options`: [dict] Options for the `pdf` output.
|
||||
* Valid keys:
|
||||
- `drill_marks`: [string='full'] what to use to indicate the drill places, can be none, small or full (for real scale).
|
||||
- `exclude_edge_layer`: [boolean=true] do not include the PCB edge layer.
|
||||
- `exclude_pads_from_silkscreen`: [boolean=false] do not plot the component pads in the silk screen.
|
||||
- `force_plot_invisible_refs_vals`: [boolean=false] include references and values even when they are marked as invisible.
|
||||
- `line_width`: [number=0.1] [0.02,2] for objects without width [mm].
|
||||
- `mirror_plot`: [boolean=false] plot mirrored.
|
||||
- `negative_plot`: [boolean=false] invert black and white.
|
||||
- `plot_footprint_refs`: [boolean=true] include the footprint references.
|
||||
- `plot_footprint_values`: [boolean=true] include the footprint values.
|
||||
- `plot_sheet_reference`: [boolean=false] currently without effect.
|
||||
- `tent_vias`: [boolean=true] cover the vias.
|
||||
- `plot_footprint_refs`: [boolean=true] include the footprint references.
|
||||
- `plot_footprint_values`: [boolean=true] include the footprint values.
|
||||
- `plot_sheet_reference`: [boolean=false] currently without effect.
|
||||
|
|
@ -375,82 +521,130 @@ Most options are the same you'll find in the KiCad dialogs.
|
|||
* Description: Exports the PCB to the most common exhange format. Suitable for printing.
|
||||
This is the main format to document your PCB.
|
||||
This output is what you get from the 'File/Print' menu in pcbnew.
|
||||
* Options:
|
||||
- `output_name`: [string=''] filename for the output PDF (the name of the PCB if empty).
|
||||
* Valid keys:
|
||||
- `comment`: [string=''] A comment for documentation purposes.
|
||||
- `dir`: [string='.'] Output directory for the generated files.
|
||||
- `layers`: [list(dict)|list(string)|string] [all,selected,copper,technical,user]
|
||||
List of PCB layers to include in the PDF.
|
||||
* Valid keys:
|
||||
- `description`: [string=''] A description for the layer, for documentation purposes.
|
||||
- `layer`: [string=''] Name of the layer. As you see it in KiCad.
|
||||
- `suffix`: [string=''] Suffix used in file names related to this layer. Derived from the name if not specified.
|
||||
- `name`: [string=''] Used to identify this particular output definition.
|
||||
- `options`: [dict] Options for the `pdf_pcb_print` output.
|
||||
* Valid keys:
|
||||
- `output_name`: [string=''] filename for the output PDF (the name of the PCB if empty).
|
||||
|
||||
* PDF Schematic Print (Portable Document Format)
|
||||
* Type: `pdf_sch_print`
|
||||
* Description: Exports the PCB to the most common exhange format. Suitable for printing.
|
||||
This is the main format to document your schematic.
|
||||
This output is what you get from the 'File/Print' menu in eeschema.
|
||||
* Options:
|
||||
- `output`: [string=''] filename for the output PDF (the name of the schematic if empty).
|
||||
* Valid keys:
|
||||
- `comment`: [string=''] A comment for documentation purposes.
|
||||
- `dir`: [string='.'] Output directory for the generated files.
|
||||
- `name`: [string=''] Used to identify this particular output definition.
|
||||
- `options`: [dict] Options for the `pdf_sch_print` output.
|
||||
* Valid keys:
|
||||
- `output`: [string=''] filename for the output PDF (the name of the schematic if empty).
|
||||
|
||||
* Pick & place
|
||||
* Type: `position`
|
||||
* Description: Generates the file with position information for the PCB components, used by the pick and place machine.
|
||||
This output is what you get from the 'File/Fabrication output/Footprint poistion (.pos) file' menu in pcbnew.
|
||||
* Options:
|
||||
- `format`: [string='ASCII'] [ASCII,CSV] format for the position file.
|
||||
- `only_smd`: [boolean=true] only include the surface mount components.
|
||||
- `separate_files_for_front_and_back`: [boolean=true] generate two separated files, one for the top and another for the bottom.
|
||||
- `units`: [string='millimeters'] [millimeters,inches] units used for the positions.
|
||||
* Valid keys:
|
||||
- `comment`: [string=''] A comment for documentation purposes.
|
||||
- `dir`: [string='.'] Output directory for the generated files.
|
||||
- `name`: [string=''] Used to identify this particular output definition.
|
||||
- `options`: [dict] Options for the `position` output.
|
||||
* Valid keys:
|
||||
- `format`: [string='ASCII'] [ASCII,CSV] format for the position file.
|
||||
- `only_smd`: [boolean=true] only include the surface mount components.
|
||||
- `separate_files_for_front_and_back`: [boolean=true] generate two separated files, one for the top and another for the bottom.
|
||||
- `units`: [string='millimeters'] [millimeters,inches] units used for the positions.
|
||||
|
||||
* PS (Postscript)
|
||||
* Type: `ps`
|
||||
* Description: Exports the PCB to a format suitable for printing.
|
||||
This output is what you get from the File/Plot menu in pcbnew.
|
||||
* Options:
|
||||
- `a4_output`: [boolean=true] force A4 paper size.
|
||||
- `drill_marks`: [string='full'] what to use to indicate the drill places, can be none, small or full (for real scale).
|
||||
- `exclude_edge_layer`: [boolean=true] do not include the PCB edge layer.
|
||||
- `exclude_pads_from_silkscreen`: [boolean=false] do not plot the component pads in the silk screen.
|
||||
- `force_plot_invisible_refs_vals`: [boolean=false] include references and values even when they are marked as invisible.
|
||||
- `line_width`: [number=0.15] [0.02,2] for objects without width [mm].
|
||||
- `mirror_plot`: [boolean=false] plot mirrored.
|
||||
- `negative_plot`: [boolean=false] invert black and white.
|
||||
- `plot_footprint_refs`: [boolean=true] include the footprint references.
|
||||
- `plot_footprint_values`: [boolean=true] include the footprint values.
|
||||
- `plot_sheet_reference`: [boolean=false] currently without effect.
|
||||
- `scale_adjust_x`: [number=1.0] fine grain adjust for the X scale (floating point multiplier).
|
||||
- `scale_adjust_y`: [number=1.0] fine grain adjust for the Y scale (floating point multiplier).
|
||||
- `scaling`: [number=1] scale factor (0 means autoscaling).
|
||||
- `sketch_plot`: [boolean=false] don't fill objects, just draw the outline.
|
||||
- `tent_vias`: [boolean=true] cover the vias.
|
||||
- `width_adjust`: [number=0] this width factor is intended to compensate PS printers/plotters that do not strictly obey line width settings.
|
||||
Only used to plot pads and tracks.
|
||||
* Valid keys:
|
||||
- `comment`: [string=''] A comment for documentation purposes.
|
||||
- `dir`: [string='.'] Output directory for the generated files.
|
||||
- `layers`: [list(dict)|list(string)|string] [all,selected,copper,technical,user]
|
||||
List of PCB layers to plot.
|
||||
* Valid keys:
|
||||
- `description`: [string=''] A description for the layer, for documentation purposes.
|
||||
- `layer`: [string=''] Name of the layer. As you see it in KiCad.
|
||||
- `suffix`: [string=''] Suffix used in file names related to this layer. Derived from the name if not specified.
|
||||
- `name`: [string=''] Used to identify this particular output definition.
|
||||
- `options`: [dict] Options for the `ps` output.
|
||||
* Valid keys:
|
||||
- `a4_output`: [boolean=true] force A4 paper size.
|
||||
- `drill_marks`: [string='full'] what to use to indicate the drill places, can be none, small or full (for real scale).
|
||||
- `exclude_edge_layer`: [boolean=true] do not include the PCB edge layer.
|
||||
- `exclude_pads_from_silkscreen`: [boolean=false] do not plot the component pads in the silk screen.
|
||||
- `force_plot_invisible_refs_vals`: [boolean=false] include references and values even when they are marked as invisible.
|
||||
- `line_width`: [number=0.15] [0.02,2] for objects without width [mm].
|
||||
- `mirror_plot`: [boolean=false] plot mirrored.
|
||||
- `negative_plot`: [boolean=false] invert black and white.
|
||||
- `plot_footprint_refs`: [boolean=true] include the footprint references.
|
||||
- `plot_footprint_values`: [boolean=true] include the footprint values.
|
||||
- `plot_sheet_reference`: [boolean=false] currently without effect.
|
||||
- `scale_adjust_x`: [number=1.0] fine grain adjust for the X scale (floating point multiplier).
|
||||
- `scale_adjust_y`: [number=1.0] fine grain adjust for the Y scale (floating point multiplier).
|
||||
- `scaling`: [number=1] scale factor (0 means autoscaling).
|
||||
- `sketch_plot`: [boolean=false] don't fill objects, just draw the outline.
|
||||
- `tent_vias`: [boolean=true] cover the vias.
|
||||
- `width_adjust`: [number=0] this width factor is intended to compensate PS printers/plotters that do not strictly obey line width settings.
|
||||
Only used to plot pads and tracks.
|
||||
|
||||
* STEP (ISO 10303-21 Clear Text Encoding of the Exchange Structure)
|
||||
* Type: `step`
|
||||
* Description: Exports the PCB as a 3D model.
|
||||
This is the most common 3D format for exchange purposes.
|
||||
This output is what you get from the 'File/Export/STEP' menu in pcbnew.
|
||||
* Options:
|
||||
- `metric_units`: [boolean=true] use metric units instead of inches..
|
||||
- `min_distance`: [number=-1] the minimum distance between points to treat them as separate ones (-1 is KiCad default: 0.01 mm).
|
||||
- `no_virtual`: [boolean=false] used to exclude 3D models for components with 'virtual' attribute.
|
||||
- `origin`: [string='grid'] determines the coordinates origin. Using grid the coordinates are the same as you have in the design sheet.
|
||||
The drill option uses the auxiliar reference defined by the user.
|
||||
You can define any other origin using the format 'X,Y', i.e. '3.2,-10'.
|
||||
- `output`: [string=''] name for the generated STEP file (the name of the PCB if empty).
|
||||
* Valid keys:
|
||||
- `comment`: [string=''] A comment for documentation purposes.
|
||||
- `dir`: [string='.'] Output directory for the generated files.
|
||||
- `name`: [string=''] Used to identify this particular output definition.
|
||||
- `options`: [dict] Options for the `step` output.
|
||||
* Valid keys:
|
||||
- `metric_units`: [boolean=true] use metric units instead of inches..
|
||||
- `min_distance`: [number=-1] the minimum distance between points to treat them as separate ones (-1 is KiCad default: 0.01 mm).
|
||||
- `no_virtual`: [boolean=false] used to exclude 3D models for components with 'virtual' attribute.
|
||||
- `origin`: [string='grid'] determines the coordinates origin. Using grid the coordinates are the same as you have in the design sheet.
|
||||
The drill option uses the auxiliar reference defined by the user.
|
||||
You can define any other origin using the format 'X,Y', i.e. '3.2,-10'.
|
||||
- `output`: [string=''] name for the generated STEP file (the name of the PCB if empty).
|
||||
|
||||
* SVG (Scalable Vector Graphics)
|
||||
* Type: `svg`
|
||||
* Description: Exports the PCB to a format suitable for 2D graphics software.
|
||||
Unlike bitmaps SVG drawings can be scaled without losing resolution.
|
||||
This output is what you get from the File/Plot menu in pcbnew.
|
||||
* Options:
|
||||
- `drill_marks`: [string='full'] what to use to indicate the drill places, can be none, small or full (for real scale).
|
||||
- `exclude_edge_layer`: [boolean=true] do not include the PCB edge layer.
|
||||
- `exclude_pads_from_silkscreen`: [boolean=false] do not plot the component pads in the silk screen.
|
||||
- `force_plot_invisible_refs_vals`: [boolean=false] include references and values even when they are marked as invisible.
|
||||
- `line_width`: [number=0.25] [0.02,2] for objects without width [mm].
|
||||
- `mirror_plot`: [boolean=false] plot mirrored.
|
||||
- `negative_plot`: [boolean=false] invert black and white.
|
||||
- `plot_footprint_refs`: [boolean=true] include the footprint references.
|
||||
- `plot_footprint_values`: [boolean=true] include the footprint values.
|
||||
- `plot_sheet_reference`: [boolean=false] currently without effect.
|
||||
- `tent_vias`: [boolean=true] cover the vias.
|
||||
* Valid keys:
|
||||
- `comment`: [string=''] A comment for documentation purposes.
|
||||
- `dir`: [string='.'] Output directory for the generated files.
|
||||
- `layers`: [list(dict)|list(string)|string] [all,selected,copper,technical,user]
|
||||
List of PCB layers to plot.
|
||||
* Valid keys:
|
||||
- `description`: [string=''] A description for the layer, for documentation purposes.
|
||||
- `layer`: [string=''] Name of the layer. As you see it in KiCad.
|
||||
- `suffix`: [string=''] Suffix used in file names related to this layer. Derived from the name if not specified.
|
||||
- `name`: [string=''] Used to identify this particular output definition.
|
||||
- `options`: [dict] Options for the `svg` output.
|
||||
* Valid keys:
|
||||
- `drill_marks`: [string='full'] what to use to indicate the drill places, can be none, small or full (for real scale).
|
||||
- `exclude_edge_layer`: [boolean=true] do not include the PCB edge layer.
|
||||
- `exclude_pads_from_silkscreen`: [boolean=false] do not plot the component pads in the silk screen.
|
||||
- `force_plot_invisible_refs_vals`: [boolean=false] include references and values even when they are marked as invisible.
|
||||
- `line_width`: [number=0.25] [0.02,2] for objects without width [mm].
|
||||
- `mirror_plot`: [boolean=false] plot mirrored.
|
||||
- `negative_plot`: [boolean=false] invert black and white.
|
||||
- `plot_footprint_refs`: [boolean=true] include the footprint references.
|
||||
- `plot_footprint_values`: [boolean=true] include the footprint values.
|
||||
- `plot_sheet_reference`: [boolean=false] currently without effect.
|
||||
- `tent_vias`: [boolean=true] cover the vias.
|
||||
|
||||
|
||||
## Using KiPlot
|
||||
|
|
@ -544,7 +738,7 @@ Usage:
|
|||
kiplot [-b BOARD] [-e SCHEMA] [-c CONFIG] [-d OUT_DIR] [-s PRE]
|
||||
[-q | -v...] [-i] [TARGET...]
|
||||
kiplot [-c PLOT_CONFIG] --list
|
||||
kiplot [-b BOARD] [-d OUT_DIR] [-p] --example
|
||||
kiplot [-b BOARD] [-d OUT_DIR] [-p | -P] --example
|
||||
kiplot [-v] --help-list-outputs
|
||||
kiplot --help-output=HELP_OUTPUT
|
||||
kiplot --help-outputs
|
||||
|
|
@ -568,6 +762,7 @@ Options:
|
|||
-i, --invert-sel Generate the outputs not listed as targets
|
||||
-l, --list List available outputs (in the config file)
|
||||
-p, --copy-options Copy plot options from the PCB file
|
||||
-P, --copy-and-expand As -p but expand the list of layers
|
||||
-q, --quiet Remove information logs
|
||||
-s PRE, --skip-pre PRE Skip preflights, comma separated or `all`
|
||||
-v, --verbose Show debugging information
|
||||
|
|
|
|||
|
|
@ -194,14 +194,88 @@ outputs:
|
|||
use_gerber_net_attributes: false
|
||||
|
||||
layers:
|
||||
- layer: F.Cu
|
||||
suffix: F_Cu
|
||||
- layer: B.Cu
|
||||
suffix: B_Cu
|
||||
- 'F.Cu'
|
||||
- 'B.Cu'
|
||||
```
|
||||
|
||||
Most options are the same you'll find in the KiCad dialogs.
|
||||
|
||||
|
||||
### Specifying the layers
|
||||
|
||||
You have various ways to specify the layers. If you need to specify just one layer you can just use its name:
|
||||
|
||||
```
|
||||
layers: 'F.Cu'
|
||||
```
|
||||
|
||||
If you want to specify all the available layers:
|
||||
|
||||
```
|
||||
layers: 'all'
|
||||
```
|
||||
|
||||
You can also select the layers you want in KiCad (using File, Plot dialog) and save your PCB.
|
||||
Then you just need to use:
|
||||
|
||||
```
|
||||
layers: 'selected'
|
||||
```
|
||||
|
||||
You can also use any of the following grup of layers:
|
||||
|
||||
- **copper** all the copper layers
|
||||
- **technical** all the technical layers (silk sreen, solder mask, paste, adhesive, etc.)
|
||||
- **user** all the user layers (draw, comments, eco, margin, edge cuts, etc.)
|
||||
|
||||
You can also mix the above definitions using a list:
|
||||
|
||||
```
|
||||
layers:
|
||||
- 'copper'
|
||||
- 'Dwgs.User'
|
||||
```
|
||||
|
||||
This will select all the copper layers and the user drawings.
|
||||
Note that the above mentioned options will use file name suffixes and descriptions selected automatically.
|
||||
If you want to use a particular suffix and provide better descriptions you can use the following format:
|
||||
|
||||
```
|
||||
layers:
|
||||
- layer: 'F.Cu'
|
||||
suffix: 'F_Cu'
|
||||
description: 'Front copper'
|
||||
- layer: 'B.Cu'
|
||||
suffix: 'B_Cu'
|
||||
description: 'Bottom copper'
|
||||
```
|
||||
|
||||
You can also mix the styles:
|
||||
|
||||
```
|
||||
layers:
|
||||
- 'copper'
|
||||
- layer: 'Cmts.User'
|
||||
suffix: 'Cmts_User'
|
||||
description: 'User comments'
|
||||
- 'Dwgs.User'
|
||||
```
|
||||
|
||||
If you need to use the same list of layers for various outputs you can use YAML anchors.
|
||||
The first time you define the list of layers just assign an ancho, here is an example:
|
||||
|
||||
```
|
||||
layers: &copper_and_cmts
|
||||
- copper
|
||||
- 'Cmts.User'
|
||||
```
|
||||
|
||||
Next time you need this list just use an alias, like this:
|
||||
|
||||
```
|
||||
layers: *copper_and_cmts
|
||||
```
|
||||
|
||||
### @outputs@
|
||||
|
||||
## Using KiPlot
|
||||
|
|
|
|||
|
|
@ -50,67 +50,7 @@ outputs:
|
|||
tent_vias: true
|
||||
# [boolean=false] use the auxiliar axis as origin for coordinates
|
||||
use_aux_axis_as_origin: false
|
||||
layers:
|
||||
- layer: 'F.Cu'
|
||||
suffix: 'F_Cu'
|
||||
description: 'Front copper'
|
||||
- layer: 'B.Cu'
|
||||
suffix: 'B_Cu'
|
||||
description: 'Bottom copper'
|
||||
- layer: 'F.Adhes'
|
||||
suffix: 'F_Adhes'
|
||||
description: 'Front adhesive (glue)'
|
||||
- layer: 'B.Adhes'
|
||||
suffix: 'B_Adhes'
|
||||
description: 'Bottom adhesive (glue)'
|
||||
- layer: 'F.Paste'
|
||||
suffix: 'F_Paste'
|
||||
description: 'Front solder paste'
|
||||
- layer: 'B.Paste'
|
||||
suffix: 'B_Paste'
|
||||
description: 'Bottom solder paste'
|
||||
- layer: 'F.SilkS'
|
||||
suffix: 'F_SilkS'
|
||||
description: 'Front silkscreen (artwork)'
|
||||
- layer: 'B.SilkS'
|
||||
suffix: 'B_SilkS'
|
||||
description: 'Bottom silkscreen (artwork)'
|
||||
- layer: 'F.Mask'
|
||||
suffix: 'F_Mask'
|
||||
description: 'Front soldermask (negative)'
|
||||
- layer: 'B.Mask'
|
||||
suffix: 'B_Mask'
|
||||
description: 'Bottom soldermask (negative)'
|
||||
- layer: 'Dwgs.User'
|
||||
suffix: 'Dwgs_User'
|
||||
description: 'User drawings'
|
||||
- layer: 'Cmts.User'
|
||||
suffix: 'Cmts_User'
|
||||
description: 'User comments'
|
||||
- layer: 'Eco1.User'
|
||||
suffix: 'Eco1_User'
|
||||
description: 'For user usage 1'
|
||||
- layer: 'Eco2.User'
|
||||
suffix: 'Eco2_User'
|
||||
description: 'For user usage 2'
|
||||
- layer: 'Edge.Cuts'
|
||||
suffix: 'Edge_Cuts'
|
||||
description: 'Board shape'
|
||||
- layer: 'Margin'
|
||||
suffix: 'Margin'
|
||||
description: 'Margin relative to edge cut'
|
||||
- layer: 'F.CrtYd'
|
||||
suffix: 'F_CrtYd'
|
||||
description: 'Front courtyard area'
|
||||
- layer: 'B.CrtYd'
|
||||
suffix: 'B_CrtYd'
|
||||
description: 'Bottom courtyard area'
|
||||
- layer: 'F.Fab'
|
||||
suffix: 'F_Fab'
|
||||
description: 'Front documentation'
|
||||
- layer: 'B.Fab'
|
||||
suffix: 'B_Fab'
|
||||
description: 'Bottom documentation'
|
||||
layers: all
|
||||
|
||||
# Excellon drill format:
|
||||
# You can create a map file for documentation purposes.
|
||||
|
|
@ -198,67 +138,7 @@ outputs:
|
|||
use_gerber_x2_attributes: true
|
||||
# [boolean=false] use legacy Protel file extensions
|
||||
use_protel_extensions: false
|
||||
layers:
|
||||
- layer: 'F.Cu'
|
||||
suffix: 'F_Cu'
|
||||
description: 'Front copper'
|
||||
- layer: 'B.Cu'
|
||||
suffix: 'B_Cu'
|
||||
description: 'Bottom copper'
|
||||
- layer: 'F.Adhes'
|
||||
suffix: 'F_Adhes'
|
||||
description: 'Front adhesive (glue)'
|
||||
- layer: 'B.Adhes'
|
||||
suffix: 'B_Adhes'
|
||||
description: 'Bottom adhesive (glue)'
|
||||
- layer: 'F.Paste'
|
||||
suffix: 'F_Paste'
|
||||
description: 'Front solder paste'
|
||||
- layer: 'B.Paste'
|
||||
suffix: 'B_Paste'
|
||||
description: 'Bottom solder paste'
|
||||
- layer: 'F.SilkS'
|
||||
suffix: 'F_SilkS'
|
||||
description: 'Front silkscreen (artwork)'
|
||||
- layer: 'B.SilkS'
|
||||
suffix: 'B_SilkS'
|
||||
description: 'Bottom silkscreen (artwork)'
|
||||
- layer: 'F.Mask'
|
||||
suffix: 'F_Mask'
|
||||
description: 'Front soldermask (negative)'
|
||||
- layer: 'B.Mask'
|
||||
suffix: 'B_Mask'
|
||||
description: 'Bottom soldermask (negative)'
|
||||
- layer: 'Dwgs.User'
|
||||
suffix: 'Dwgs_User'
|
||||
description: 'User drawings'
|
||||
- layer: 'Cmts.User'
|
||||
suffix: 'Cmts_User'
|
||||
description: 'User comments'
|
||||
- layer: 'Eco1.User'
|
||||
suffix: 'Eco1_User'
|
||||
description: 'For user usage 1'
|
||||
- layer: 'Eco2.User'
|
||||
suffix: 'Eco2_User'
|
||||
description: 'For user usage 2'
|
||||
- layer: 'Edge.Cuts'
|
||||
suffix: 'Edge_Cuts'
|
||||
description: 'Board shape'
|
||||
- layer: 'Margin'
|
||||
suffix: 'Margin'
|
||||
description: 'Margin relative to edge cut'
|
||||
- layer: 'F.CrtYd'
|
||||
suffix: 'F_CrtYd'
|
||||
description: 'Front courtyard area'
|
||||
- layer: 'B.CrtYd'
|
||||
suffix: 'B_CrtYd'
|
||||
description: 'Bottom courtyard area'
|
||||
- layer: 'F.Fab'
|
||||
suffix: 'F_Fab'
|
||||
description: 'Front documentation'
|
||||
- layer: 'B.Fab'
|
||||
suffix: 'B_Fab'
|
||||
description: 'Bottom documentation'
|
||||
layers: all
|
||||
|
||||
# HPGL (Hewlett & Packard Graphics Language):
|
||||
# This output is what you get from the File/Plot menu in pcbnew.
|
||||
|
|
@ -295,67 +175,7 @@ outputs:
|
|||
sketch_plot: false
|
||||
# [boolean=true] cover the vias
|
||||
tent_vias: true
|
||||
layers:
|
||||
- layer: 'F.Cu'
|
||||
suffix: 'F_Cu'
|
||||
description: 'Front copper'
|
||||
- layer: 'B.Cu'
|
||||
suffix: 'B_Cu'
|
||||
description: 'Bottom copper'
|
||||
- layer: 'F.Adhes'
|
||||
suffix: 'F_Adhes'
|
||||
description: 'Front adhesive (glue)'
|
||||
- layer: 'B.Adhes'
|
||||
suffix: 'B_Adhes'
|
||||
description: 'Bottom adhesive (glue)'
|
||||
- layer: 'F.Paste'
|
||||
suffix: 'F_Paste'
|
||||
description: 'Front solder paste'
|
||||
- layer: 'B.Paste'
|
||||
suffix: 'B_Paste'
|
||||
description: 'Bottom solder paste'
|
||||
- layer: 'F.SilkS'
|
||||
suffix: 'F_SilkS'
|
||||
description: 'Front silkscreen (artwork)'
|
||||
- layer: 'B.SilkS'
|
||||
suffix: 'B_SilkS'
|
||||
description: 'Bottom silkscreen (artwork)'
|
||||
- layer: 'F.Mask'
|
||||
suffix: 'F_Mask'
|
||||
description: 'Front soldermask (negative)'
|
||||
- layer: 'B.Mask'
|
||||
suffix: 'B_Mask'
|
||||
description: 'Bottom soldermask (negative)'
|
||||
- layer: 'Dwgs.User'
|
||||
suffix: 'Dwgs_User'
|
||||
description: 'User drawings'
|
||||
- layer: 'Cmts.User'
|
||||
suffix: 'Cmts_User'
|
||||
description: 'User comments'
|
||||
- layer: 'Eco1.User'
|
||||
suffix: 'Eco1_User'
|
||||
description: 'For user usage 1'
|
||||
- layer: 'Eco2.User'
|
||||
suffix: 'Eco2_User'
|
||||
description: 'For user usage 2'
|
||||
- layer: 'Edge.Cuts'
|
||||
suffix: 'Edge_Cuts'
|
||||
description: 'Board shape'
|
||||
- layer: 'Margin'
|
||||
suffix: 'Margin'
|
||||
description: 'Margin relative to edge cut'
|
||||
- layer: 'F.CrtYd'
|
||||
suffix: 'F_CrtYd'
|
||||
description: 'Front courtyard area'
|
||||
- layer: 'B.CrtYd'
|
||||
suffix: 'B_CrtYd'
|
||||
description: 'Bottom courtyard area'
|
||||
- layer: 'F.Fab'
|
||||
suffix: 'F_Fab'
|
||||
description: 'Front documentation'
|
||||
- layer: 'B.Fab'
|
||||
suffix: 'B_Fab'
|
||||
description: 'Bottom documentation'
|
||||
layers: all
|
||||
|
||||
# IBoM (Interactive HTML BoM):
|
||||
# For more information: https://github.com/INTI-CMNB/InteractiveHtmlBom
|
||||
|
|
@ -474,67 +294,7 @@ outputs:
|
|||
plot_sheet_reference: false
|
||||
# [boolean=true] cover the vias
|
||||
tent_vias: true
|
||||
layers:
|
||||
- layer: 'F.Cu'
|
||||
suffix: 'F_Cu'
|
||||
description: 'Front copper'
|
||||
- layer: 'B.Cu'
|
||||
suffix: 'B_Cu'
|
||||
description: 'Bottom copper'
|
||||
- layer: 'F.Adhes'
|
||||
suffix: 'F_Adhes'
|
||||
description: 'Front adhesive (glue)'
|
||||
- layer: 'B.Adhes'
|
||||
suffix: 'B_Adhes'
|
||||
description: 'Bottom adhesive (glue)'
|
||||
- layer: 'F.Paste'
|
||||
suffix: 'F_Paste'
|
||||
description: 'Front solder paste'
|
||||
- layer: 'B.Paste'
|
||||
suffix: 'B_Paste'
|
||||
description: 'Bottom solder paste'
|
||||
- layer: 'F.SilkS'
|
||||
suffix: 'F_SilkS'
|
||||
description: 'Front silkscreen (artwork)'
|
||||
- layer: 'B.SilkS'
|
||||
suffix: 'B_SilkS'
|
||||
description: 'Bottom silkscreen (artwork)'
|
||||
- layer: 'F.Mask'
|
||||
suffix: 'F_Mask'
|
||||
description: 'Front soldermask (negative)'
|
||||
- layer: 'B.Mask'
|
||||
suffix: 'B_Mask'
|
||||
description: 'Bottom soldermask (negative)'
|
||||
- layer: 'Dwgs.User'
|
||||
suffix: 'Dwgs_User'
|
||||
description: 'User drawings'
|
||||
- layer: 'Cmts.User'
|
||||
suffix: 'Cmts_User'
|
||||
description: 'User comments'
|
||||
- layer: 'Eco1.User'
|
||||
suffix: 'Eco1_User'
|
||||
description: 'For user usage 1'
|
||||
- layer: 'Eco2.User'
|
||||
suffix: 'Eco2_User'
|
||||
description: 'For user usage 2'
|
||||
- layer: 'Edge.Cuts'
|
||||
suffix: 'Edge_Cuts'
|
||||
description: 'Board shape'
|
||||
- layer: 'Margin'
|
||||
suffix: 'Margin'
|
||||
description: 'Margin relative to edge cut'
|
||||
- layer: 'F.CrtYd'
|
||||
suffix: 'F_CrtYd'
|
||||
description: 'Front courtyard area'
|
||||
- layer: 'B.CrtYd'
|
||||
suffix: 'B_CrtYd'
|
||||
description: 'Bottom courtyard area'
|
||||
- layer: 'F.Fab'
|
||||
suffix: 'F_Fab'
|
||||
description: 'Front documentation'
|
||||
- layer: 'B.Fab'
|
||||
suffix: 'B_Fab'
|
||||
description: 'Bottom documentation'
|
||||
layers: all
|
||||
|
||||
# PDF PCB Print (Portable Document Format):
|
||||
# This is the main format to document your PCB.
|
||||
|
|
@ -546,67 +306,7 @@ outputs:
|
|||
options:
|
||||
# [string=''] filename for the output PDF (the name of the PCB if empty)
|
||||
output_name: ''
|
||||
layers:
|
||||
- layer: 'F.Cu'
|
||||
suffix: 'F_Cu'
|
||||
description: 'Front copper'
|
||||
- layer: 'B.Cu'
|
||||
suffix: 'B_Cu'
|
||||
description: 'Bottom copper'
|
||||
- layer: 'F.Adhes'
|
||||
suffix: 'F_Adhes'
|
||||
description: 'Front adhesive (glue)'
|
||||
- layer: 'B.Adhes'
|
||||
suffix: 'B_Adhes'
|
||||
description: 'Bottom adhesive (glue)'
|
||||
- layer: 'F.Paste'
|
||||
suffix: 'F_Paste'
|
||||
description: 'Front solder paste'
|
||||
- layer: 'B.Paste'
|
||||
suffix: 'B_Paste'
|
||||
description: 'Bottom solder paste'
|
||||
- layer: 'F.SilkS'
|
||||
suffix: 'F_SilkS'
|
||||
description: 'Front silkscreen (artwork)'
|
||||
- layer: 'B.SilkS'
|
||||
suffix: 'B_SilkS'
|
||||
description: 'Bottom silkscreen (artwork)'
|
||||
- layer: 'F.Mask'
|
||||
suffix: 'F_Mask'
|
||||
description: 'Front soldermask (negative)'
|
||||
- layer: 'B.Mask'
|
||||
suffix: 'B_Mask'
|
||||
description: 'Bottom soldermask (negative)'
|
||||
- layer: 'Dwgs.User'
|
||||
suffix: 'Dwgs_User'
|
||||
description: 'User drawings'
|
||||
- layer: 'Cmts.User'
|
||||
suffix: 'Cmts_User'
|
||||
description: 'User comments'
|
||||
- layer: 'Eco1.User'
|
||||
suffix: 'Eco1_User'
|
||||
description: 'For user usage 1'
|
||||
- layer: 'Eco2.User'
|
||||
suffix: 'Eco2_User'
|
||||
description: 'For user usage 2'
|
||||
- layer: 'Edge.Cuts'
|
||||
suffix: 'Edge_Cuts'
|
||||
description: 'Board shape'
|
||||
- layer: 'Margin'
|
||||
suffix: 'Margin'
|
||||
description: 'Margin relative to edge cut'
|
||||
- layer: 'F.CrtYd'
|
||||
suffix: 'F_CrtYd'
|
||||
description: 'Front courtyard area'
|
||||
- layer: 'B.CrtYd'
|
||||
suffix: 'B_CrtYd'
|
||||
description: 'Bottom courtyard area'
|
||||
- layer: 'F.Fab'
|
||||
suffix: 'F_Fab'
|
||||
description: 'Front documentation'
|
||||
- layer: 'B.Fab'
|
||||
suffix: 'B_Fab'
|
||||
description: 'Bottom documentation'
|
||||
layers: all
|
||||
|
||||
# PDF Schematic Print (Portable Document Format):
|
||||
# This is the main format to document your schematic.
|
||||
|
|
@ -677,67 +377,7 @@ outputs:
|
|||
# [number=0] this width factor is intended to compensate PS printers/plotters that do not strictly obey line width settings.
|
||||
# Only used to plot pads and tracks
|
||||
width_adjust: 0
|
||||
layers:
|
||||
- layer: 'F.Cu'
|
||||
suffix: 'F_Cu'
|
||||
description: 'Front copper'
|
||||
- layer: 'B.Cu'
|
||||
suffix: 'B_Cu'
|
||||
description: 'Bottom copper'
|
||||
- layer: 'F.Adhes'
|
||||
suffix: 'F_Adhes'
|
||||
description: 'Front adhesive (glue)'
|
||||
- layer: 'B.Adhes'
|
||||
suffix: 'B_Adhes'
|
||||
description: 'Bottom adhesive (glue)'
|
||||
- layer: 'F.Paste'
|
||||
suffix: 'F_Paste'
|
||||
description: 'Front solder paste'
|
||||
- layer: 'B.Paste'
|
||||
suffix: 'B_Paste'
|
||||
description: 'Bottom solder paste'
|
||||
- layer: 'F.SilkS'
|
||||
suffix: 'F_SilkS'
|
||||
description: 'Front silkscreen (artwork)'
|
||||
- layer: 'B.SilkS'
|
||||
suffix: 'B_SilkS'
|
||||
description: 'Bottom silkscreen (artwork)'
|
||||
- layer: 'F.Mask'
|
||||
suffix: 'F_Mask'
|
||||
description: 'Front soldermask (negative)'
|
||||
- layer: 'B.Mask'
|
||||
suffix: 'B_Mask'
|
||||
description: 'Bottom soldermask (negative)'
|
||||
- layer: 'Dwgs.User'
|
||||
suffix: 'Dwgs_User'
|
||||
description: 'User drawings'
|
||||
- layer: 'Cmts.User'
|
||||
suffix: 'Cmts_User'
|
||||
description: 'User comments'
|
||||
- layer: 'Eco1.User'
|
||||
suffix: 'Eco1_User'
|
||||
description: 'For user usage 1'
|
||||
- layer: 'Eco2.User'
|
||||
suffix: 'Eco2_User'
|
||||
description: 'For user usage 2'
|
||||
- layer: 'Edge.Cuts'
|
||||
suffix: 'Edge_Cuts'
|
||||
description: 'Board shape'
|
||||
- layer: 'Margin'
|
||||
suffix: 'Margin'
|
||||
description: 'Margin relative to edge cut'
|
||||
- layer: 'F.CrtYd'
|
||||
suffix: 'F_CrtYd'
|
||||
description: 'Front courtyard area'
|
||||
- layer: 'B.CrtYd'
|
||||
suffix: 'B_CrtYd'
|
||||
description: 'Bottom courtyard area'
|
||||
- layer: 'F.Fab'
|
||||
suffix: 'F_Fab'
|
||||
description: 'Front documentation'
|
||||
- layer: 'B.Fab'
|
||||
suffix: 'B_Fab'
|
||||
description: 'Bottom documentation'
|
||||
layers: all
|
||||
|
||||
# STEP (ISO 10303-21 Clear Text Encoding of the Exchange Structure):
|
||||
# This is the most common 3D format for exchange purposes.
|
||||
|
|
@ -790,65 +430,5 @@ outputs:
|
|||
plot_sheet_reference: false
|
||||
# [boolean=true] cover the vias
|
||||
tent_vias: true
|
||||
layers:
|
||||
- layer: 'F.Cu'
|
||||
suffix: 'F_Cu'
|
||||
description: 'Front copper'
|
||||
- layer: 'B.Cu'
|
||||
suffix: 'B_Cu'
|
||||
description: 'Bottom copper'
|
||||
- layer: 'F.Adhes'
|
||||
suffix: 'F_Adhes'
|
||||
description: 'Front adhesive (glue)'
|
||||
- layer: 'B.Adhes'
|
||||
suffix: 'B_Adhes'
|
||||
description: 'Bottom adhesive (glue)'
|
||||
- layer: 'F.Paste'
|
||||
suffix: 'F_Paste'
|
||||
description: 'Front solder paste'
|
||||
- layer: 'B.Paste'
|
||||
suffix: 'B_Paste'
|
||||
description: 'Bottom solder paste'
|
||||
- layer: 'F.SilkS'
|
||||
suffix: 'F_SilkS'
|
||||
description: 'Front silkscreen (artwork)'
|
||||
- layer: 'B.SilkS'
|
||||
suffix: 'B_SilkS'
|
||||
description: 'Bottom silkscreen (artwork)'
|
||||
- layer: 'F.Mask'
|
||||
suffix: 'F_Mask'
|
||||
description: 'Front soldermask (negative)'
|
||||
- layer: 'B.Mask'
|
||||
suffix: 'B_Mask'
|
||||
description: 'Bottom soldermask (negative)'
|
||||
- layer: 'Dwgs.User'
|
||||
suffix: 'Dwgs_User'
|
||||
description: 'User drawings'
|
||||
- layer: 'Cmts.User'
|
||||
suffix: 'Cmts_User'
|
||||
description: 'User comments'
|
||||
- layer: 'Eco1.User'
|
||||
suffix: 'Eco1_User'
|
||||
description: 'For user usage 1'
|
||||
- layer: 'Eco2.User'
|
||||
suffix: 'Eco2_User'
|
||||
description: 'For user usage 2'
|
||||
- layer: 'Edge.Cuts'
|
||||
suffix: 'Edge_Cuts'
|
||||
description: 'Board shape'
|
||||
- layer: 'Margin'
|
||||
suffix: 'Margin'
|
||||
description: 'Margin relative to edge cut'
|
||||
- layer: 'F.CrtYd'
|
||||
suffix: 'F_CrtYd'
|
||||
description: 'Front courtyard area'
|
||||
- layer: 'B.CrtYd'
|
||||
suffix: 'B_CrtYd'
|
||||
description: 'Bottom courtyard area'
|
||||
- layer: 'F.Fab'
|
||||
suffix: 'F_Fab'
|
||||
description: 'Front documentation'
|
||||
- layer: 'B.Fab'
|
||||
suffix: 'B_Fab'
|
||||
description: 'Bottom documentation'
|
||||
layers: all
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ Usage:
|
|||
kiplot [-b BOARD] [-e SCHEMA] [-c CONFIG] [-d OUT_DIR] [-s PRE]
|
||||
[-q | -v...] [-i] [TARGET...]
|
||||
kiplot [-c PLOT_CONFIG] --list
|
||||
kiplot [-b BOARD] [-d OUT_DIR] [-p] --example
|
||||
kiplot [-b BOARD] [-d OUT_DIR] [-p | -P] --example
|
||||
kiplot [-v] --help-list-outputs
|
||||
kiplot --help-output=HELP_OUTPUT
|
||||
kiplot --help-outputs
|
||||
|
|
@ -29,6 +29,7 @@ Options:
|
|||
-i, --invert-sel Generate the outputs not listed as targets
|
||||
-l, --list List available outputs (in the config file)
|
||||
-p, --copy-options Copy plot options from the PCB file
|
||||
-P, --copy-and-expand As -p but expand the list of layers
|
||||
-q, --quiet Remove information logs
|
||||
-s PRE, --skip-pre PRE Skip preflights, comma separated or `all`
|
||||
-v, --verbose Show debugging information
|
||||
|
|
@ -179,7 +180,7 @@ def main():
|
|||
if args.copy_options and not args.board_file:
|
||||
logger.error('Asked to copy options but no PCB specified.')
|
||||
sys.exit(EXIT_BAD_ARGS)
|
||||
create_example(args.board_file, GS.out_dir, args.copy_options)
|
||||
create_example(args.board_file, GS.out_dir, args.copy_options, args.copy_and_expand)
|
||||
sys.exit(0)
|
||||
|
||||
# Determine the YAML file
|
||||
|
|
|
|||
|
|
@ -3,17 +3,16 @@ Class to read KiPlot config files
|
|||
"""
|
||||
|
||||
import os
|
||||
from sys import (exit, maxsize, exc_info)
|
||||
from traceback import print_tb
|
||||
from sys import (exit, maxsize)
|
||||
from collections import OrderedDict
|
||||
|
||||
from .error import (KiPlotConfigurationError)
|
||||
from .gs import GS
|
||||
from .kiplot import (Layer, load_board)
|
||||
from .misc import (NO_YAML_MODULE, EXIT_BAD_CONFIG, EXIT_BAD_ARGS, EXAMPLE_CFG, WONT_OVERWRITE)
|
||||
from .optionable import Optionable
|
||||
from .out_base import BaseOutput
|
||||
from .error import (KiPlotConfigurationError, config_error)
|
||||
from .kiplot import (load_board)
|
||||
from .misc import (NO_YAML_MODULE, EXIT_BAD_ARGS, EXAMPLE_CFG, WONT_OVERWRITE)
|
||||
|
||||
from .reg_out import RegOutput
|
||||
from .pre_base import BasePreFlight
|
||||
|
||||
# Logger
|
||||
from . import log
|
||||
|
||||
|
|
@ -27,15 +26,6 @@ except ImportError: # pragma: no cover
|
|||
exit(NO_YAML_MODULE)
|
||||
|
||||
|
||||
def config_error(msg):
|
||||
if GS.debug_enabled:
|
||||
logger.error('Trace stack:')
|
||||
(type, value, traceback) = exc_info()
|
||||
print_tb(traceback)
|
||||
logger.error(msg)
|
||||
exit(EXIT_BAD_CONFIG)
|
||||
|
||||
|
||||
class CfgYamlReader(object):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
|
@ -51,76 +41,28 @@ class CfgYamlReader(object):
|
|||
config_error("Unknown KiPlot config version: "+str(version))
|
||||
return version
|
||||
|
||||
def _parse_layers(self, layers_to_parse):
|
||||
# Check we have a list of layers
|
||||
if not isinstance(layers_to_parse, list):
|
||||
raise KiPlotConfigurationError("`layers` must be a list")
|
||||
# Parse the elements
|
||||
layers = []
|
||||
for l in layers_to_parse:
|
||||
# Extract the attributes
|
||||
layer = None
|
||||
description = 'no desc'
|
||||
suffix = ''
|
||||
for k, v in l.items():
|
||||
if k == 'layer':
|
||||
layer = str(v)
|
||||
elif k == 'description':
|
||||
description = str(v)
|
||||
elif k == 'suffix':
|
||||
suffix = str(v)
|
||||
else:
|
||||
raise KiPlotConfigurationError("Unknown `{}` attribute for `layer`".format(k))
|
||||
# Check we got the layer name
|
||||
if layer is None:
|
||||
raise KiPlotConfigurationError("Missing `layer` attribute for layer entry ({})".format(l))
|
||||
# Create an object for it
|
||||
layers.append(Layer(layer, suffix, description))
|
||||
return layers
|
||||
|
||||
def _parse_output(self, o_obj):
|
||||
# Default values
|
||||
name = None
|
||||
desc = None
|
||||
otype = None
|
||||
options = None
|
||||
outdir = '.'
|
||||
layers = []
|
||||
# Parse all of them
|
||||
for k, v in o_obj.items():
|
||||
if k == 'name':
|
||||
name = v
|
||||
elif k == 'comment':
|
||||
desc = v
|
||||
elif k == 'type':
|
||||
otype = v
|
||||
elif k == 'options':
|
||||
options = v
|
||||
elif k == 'dir':
|
||||
outdir = v
|
||||
elif k == 'layers':
|
||||
layers = v
|
||||
else:
|
||||
config_error("Unknown key `{}` in `{}` ({})".format(k, name, otype))
|
||||
# Validate them
|
||||
if not name:
|
||||
try:
|
||||
name = o_obj['name']
|
||||
except KeyError:
|
||||
config_error("Output needs a name in: "+str(o_obj))
|
||||
if not otype:
|
||||
|
||||
try:
|
||||
otype = o_obj['type']
|
||||
except KeyError:
|
||||
config_error("Output `"+name+"` needs a type")
|
||||
|
||||
name_type = "`"+name+"` ("+otype+")"
|
||||
|
||||
# Is a valid type?
|
||||
if not BaseOutput.is_registered(otype):
|
||||
if not RegOutput.is_registered(otype):
|
||||
config_error("Unknown output type: `{}`".format(otype))
|
||||
# Load it
|
||||
logger.debug("Parsing output options for "+name_type)
|
||||
o_out = BaseOutput.get_class_for(otype)(name, otype, desc)
|
||||
o_out = RegOutput.get_class_for(otype)()
|
||||
# Apply the options
|
||||
try:
|
||||
# If we have layers parse them
|
||||
if layers:
|
||||
layers = self._parse_layers(layers)
|
||||
o_out.config(outdir, options, layers)
|
||||
o_out.config(o_obj)
|
||||
except KiPlotConfigurationError as e:
|
||||
config_error("In section '"+name+"' ("+otype+"): "+str(e))
|
||||
|
||||
|
|
@ -207,8 +149,6 @@ class CfgYamlReader(object):
|
|||
|
||||
def trim(docstring):
|
||||
""" PEP 257 recommended trim for __doc__ """
|
||||
if not docstring:
|
||||
return ''
|
||||
# Convert tabs to spaces (following the normal Python rules)
|
||||
# and split into a list of lines:
|
||||
lines = docstring.expandtabs().splitlines()
|
||||
|
|
@ -234,16 +174,16 @@ def trim(docstring):
|
|||
|
||||
def print_output_options(name, cl, indent):
|
||||
ind_str = indent*' '
|
||||
if issubclass(cl, BaseOutput):
|
||||
obj = cl('', name, '')
|
||||
else:
|
||||
obj = cl(name, '')
|
||||
print(ind_str+'* Options:')
|
||||
obj = cl()
|
||||
print(ind_str+'* Valid keys:')
|
||||
num_opts = 0
|
||||
for k, v in Optionable.get_attrs_gen(obj):
|
||||
for k, v in obj.get_attrs_gen():
|
||||
if k == 'type':
|
||||
# Type is fixed for an output
|
||||
continue
|
||||
help = getattr(obj, '_help_'+k)
|
||||
if help is None:
|
||||
help = 'Undocumented'
|
||||
help = 'Undocumented' # pragma: no cover
|
||||
lines = help.split('\n')
|
||||
preface = ind_str+' - `{}`: '.format(k)
|
||||
clines = len(lines)
|
||||
|
|
@ -261,7 +201,7 @@ def print_output_options(name, cl, indent):
|
|||
def print_one_out_help(details, n, o):
|
||||
lines = trim(o.__doc__)
|
||||
if len(lines) == 0:
|
||||
lines = ['Undocumented', 'No description']
|
||||
lines = ['Undocumented', 'No description'] # pragma: no cover
|
||||
if details:
|
||||
print('* '+lines[0])
|
||||
print(' * Type: `{}`'.format(n))
|
||||
|
|
@ -274,7 +214,7 @@ def print_one_out_help(details, n, o):
|
|||
|
||||
|
||||
def print_outputs_help(details=False):
|
||||
outs = BaseOutput.get_registered()
|
||||
outs = RegOutput.get_registered()
|
||||
logger.debug('{} supported outputs'.format(len(outs)))
|
||||
print('Supported outputs:')
|
||||
for n, o in OrderedDict(sorted(outs.items())).items():
|
||||
|
|
@ -284,10 +224,10 @@ def print_outputs_help(details=False):
|
|||
|
||||
|
||||
def print_output_help(name):
|
||||
if not BaseOutput.is_registered(name):
|
||||
if not RegOutput.is_registered(name):
|
||||
logger.error('Unknown output type `{}`, try --help-list-outputs'.format(name))
|
||||
exit(EXIT_BAD_ARGS)
|
||||
print_one_out_help(True, name, BaseOutput.get_class_for(name))
|
||||
print_one_out_help(True, name, RegOutput.get_class_for(name))
|
||||
|
||||
|
||||
def print_preflights_help():
|
||||
|
|
@ -297,19 +237,16 @@ def print_preflights_help():
|
|||
for n, o in pres.items():
|
||||
help = o.__doc__
|
||||
if help is None:
|
||||
help = 'Undocumented'
|
||||
help = 'Undocumented' # pragma: no cover
|
||||
print('- {}: {}.'.format(n, help.rstrip()))
|
||||
|
||||
|
||||
def print_example_options(f, cls, name, indent, po):
|
||||
ind_str = indent*' '
|
||||
if issubclass(cls, BaseOutput):
|
||||
obj = cls('', name, '')
|
||||
else:
|
||||
obj = cls(name, '')
|
||||
obj = cls()
|
||||
if po:
|
||||
obj.read_vals_from_po(po)
|
||||
for k, v in Optionable.get_attrs_gen(obj):
|
||||
for k, v in obj.get_attrs_gen():
|
||||
help = getattr(obj, '_help_'+k)
|
||||
if help:
|
||||
help_lines = help.split('\n')
|
||||
|
|
@ -328,7 +265,7 @@ def print_example_options(f, cls, name, indent, po):
|
|||
return obj
|
||||
|
||||
|
||||
def create_example(pcb_file, out_dir, copy_options):
|
||||
def create_example(pcb_file, out_dir, copy_options, copy_expand):
|
||||
if not os.path.exists(out_dir):
|
||||
os.makedirs(out_dir)
|
||||
fname = os.path.join(out_dir, EXAMPLE_CFG)
|
||||
|
|
@ -346,26 +283,22 @@ def create_example(pcb_file, out_dir, copy_options):
|
|||
f.write(' #'+o.__doc__.rstrip()+'\n')
|
||||
f.write(' {}: {}\n'.format(n, o.get_example()))
|
||||
# Outputs
|
||||
outs = BaseOutput.get_registered()
|
||||
outs = RegOutput.get_registered()
|
||||
f.write('\noutputs:\n')
|
||||
# List of layers
|
||||
po = None
|
||||
layers = 'all'
|
||||
if pcb_file:
|
||||
# We have a PCB to take as reference
|
||||
board = load_board(pcb_file)
|
||||
if copy_options:
|
||||
if copy_options or copy_expand:
|
||||
# Layers and plot options from the PCB
|
||||
layers = Layer.get_plot_layers()
|
||||
layers = 'selected'
|
||||
po = board.GetPlotOptions()
|
||||
else:
|
||||
layers = Layer.get_pcb_layers()
|
||||
else:
|
||||
# Use the default list of layers
|
||||
layers = Layer.get_default_layers()
|
||||
for n, cls in OrderedDict(sorted(outs.items())).items():
|
||||
lines = trim(cls.__doc__)
|
||||
if len(lines) == 0:
|
||||
lines = ['Undocumented', 'No description']
|
||||
lines = ['Undocumented', 'No description'] # pragma: no cover
|
||||
f.write(' # '+lines[0].rstrip()+':\n')
|
||||
for ln in range(2, len(lines)):
|
||||
f.write(' # '+lines[ln].rstrip()+'\n')
|
||||
|
|
@ -374,12 +307,17 @@ def create_example(pcb_file, out_dir, copy_options):
|
|||
f.write(" type: '{}'\n".format(n))
|
||||
f.write(" dir: 'Example/{}_dir'\n".format(n))
|
||||
f.write(" options:\n")
|
||||
obj = print_example_options(f, cls, n, 6, po)
|
||||
if '_layers' in obj.__dict__:
|
||||
f.write(' layers:\n')
|
||||
for layer in layers:
|
||||
f.write(" - layer: '{}'\n".format(layer.name))
|
||||
f.write(" suffix: '{}'\n".format(layer.suffix))
|
||||
if layer.desc:
|
||||
f.write(" description: '{}'\n".format(layer.desc))
|
||||
obj = cls()
|
||||
print_example_options(f, obj.options, n, 6, po)
|
||||
if 'layers' in obj.__dict__:
|
||||
if copy_expand:
|
||||
f.write(' layers:\n')
|
||||
layers = obj.layers.solve(layers)
|
||||
for layer in layers:
|
||||
f.write(" - layer: '{}'\n".format(layer.layer))
|
||||
f.write(" suffix: '{}'\n".format(layer.suffix))
|
||||
if layer.description:
|
||||
f.write(" description: '{}'\n".format(layer.description))
|
||||
else:
|
||||
f.write(' layers: {}\n'.format(layers))
|
||||
f.write('\n')
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
from pcbnew import (PCB_PLOT_PARAMS)
|
||||
from .error import KiPlotConfigurationError
|
||||
from kiplot.macros import macros, document, output_class # noqa: F401
|
||||
from .out_any_layer import AnyLayerOptions
|
||||
from kiplot.macros import macros, document # noqa: F401
|
||||
from . import log
|
||||
|
||||
logger = log.get_logger(__name__)
|
||||
|
||||
|
||||
class DrillMarks(object):
|
||||
class DrillMarks(AnyLayerOptions):
|
||||
""" This class provides the drill_marks attribute.
|
||||
Used by DXF, HPGL, PDF, PS and SVG formats. """
|
||||
# Mappings to KiCad values
|
||||
|
|
@ -37,12 +38,15 @@ class DrillMarks(object):
|
|||
raise KiPlotConfigurationError("Unknown drill mark type: {}".format(val))
|
||||
self._drill_marks = val
|
||||
|
||||
def config(self):
|
||||
def config(self, tree):
|
||||
super().config(tree)
|
||||
self._drill_marks = DrillMarks._drill_marks_map[self._drill_marks]
|
||||
|
||||
def _configure_plot_ctrl(self, po, output_dir):
|
||||
super()._configure_plot_ctrl(po, output_dir)
|
||||
# How we draw drill marks
|
||||
po.SetDrillMarksType(self._drill_marks)
|
||||
|
||||
def read_vals_from_po(self, po):
|
||||
super().read_vals_from_po(po)
|
||||
self._drill_marks = DrillMarks._drill_marks_rev_map[po.GetDrillMarksType()]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,14 @@
|
|||
"""
|
||||
KiPlot errors
|
||||
"""
|
||||
from sys import (exit, exc_info)
|
||||
from traceback import print_tb
|
||||
from .gs import GS
|
||||
from .misc import (EXIT_BAD_CONFIG)
|
||||
# Logger
|
||||
from . import log
|
||||
|
||||
logger = log.get_logger(__name__)
|
||||
|
||||
|
||||
class KiPlotError(Exception):
|
||||
|
|
@ -13,3 +21,12 @@ class PlotError(KiPlotError):
|
|||
|
||||
class KiPlotConfigurationError(KiPlotError):
|
||||
pass
|
||||
|
||||
|
||||
def config_error(msg):
|
||||
if GS.debug_enabled:
|
||||
logger.error('Trace stack:')
|
||||
(type, value, traceback) = exc_info()
|
||||
print_tb(traceback)
|
||||
logger.error(msg)
|
||||
exit(EXIT_BAD_CONFIG)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ class GS(object):
|
|||
sch_file = None
|
||||
out_dir = None
|
||||
filter_file = None
|
||||
board = None
|
||||
debug_enabled = False
|
||||
|
||||
@staticmethod
|
||||
|
|
|
|||
141
kiplot/kiplot.py
141
kiplot/kiplot.py
|
|
@ -14,7 +14,7 @@ from importlib.util import (spec_from_file_location, module_from_spec)
|
|||
from .gs import (GS)
|
||||
from .misc import (PLOT_ERROR, NO_PCBNEW_MODULE, MISSING_TOOL, CMD_EESCHEMA_DO, URL_EESCHEMA_DO, NO_SCH_FILE, CORRUPTED_PCB,
|
||||
EXIT_BAD_ARGS)
|
||||
from .error import (PlotError)
|
||||
from .error import (PlotError, KiPlotConfigurationError, config_error)
|
||||
from .pre_base import BasePreFlight
|
||||
from . import log
|
||||
|
||||
|
|
@ -31,134 +31,6 @@ except ImportError: # pragma: no cover
|
|||
exit(NO_PCBNEW_MODULE)
|
||||
|
||||
|
||||
class Layer(object):
|
||||
""" A layer description """
|
||||
# Default names
|
||||
DEFAULT_LAYER_NAMES = {
|
||||
'F.Cu': pcbnew.F_Cu,
|
||||
'B.Cu': pcbnew.B_Cu,
|
||||
'F.Adhes': pcbnew.F_Adhes,
|
||||
'B.Adhes': pcbnew.B_Adhes,
|
||||
'F.Paste': pcbnew.F_Paste,
|
||||
'B.Paste': pcbnew.B_Paste,
|
||||
'F.SilkS': pcbnew.F_SilkS,
|
||||
'B.SilkS': pcbnew.B_SilkS,
|
||||
'F.Mask': pcbnew.F_Mask,
|
||||
'B.Mask': pcbnew.B_Mask,
|
||||
'Dwgs.User': pcbnew.Dwgs_User,
|
||||
'Cmts.User': pcbnew.Cmts_User,
|
||||
'Eco1.User': pcbnew.Eco1_User,
|
||||
'Eco2.User': pcbnew.Eco2_User,
|
||||
'Edge.Cuts': pcbnew.Edge_Cuts,
|
||||
'Margin': pcbnew.Margin,
|
||||
'F.CrtYd': pcbnew.F_CrtYd,
|
||||
'B.CrtYd': pcbnew.B_CrtYd,
|
||||
'F.Fab': pcbnew.F_Fab,
|
||||
'B.Fab': pcbnew.B_Fab,
|
||||
}
|
||||
# Default names
|
||||
DEFAULT_LAYER_DESC = {
|
||||
'F.Cu': 'Front copper',
|
||||
'B.Cu': 'Bottom copper',
|
||||
'F.Adhes': 'Front adhesive (glue)',
|
||||
'B.Adhes': 'Bottom adhesive (glue)',
|
||||
'F.Paste': 'Front solder paste',
|
||||
'B.Paste': 'Bottom solder paste',
|
||||
'F.SilkS': 'Front silkscreen (artwork)',
|
||||
'B.SilkS': 'Bottom silkscreen (artwork)',
|
||||
'F.Mask': 'Front soldermask (negative)',
|
||||
'B.Mask': 'Bottom soldermask (negative)',
|
||||
'Dwgs.User': 'User drawings',
|
||||
'Cmts.User': 'User comments',
|
||||
'Eco1.User': 'For user usage 1',
|
||||
'Eco2.User': 'For user usage 2',
|
||||
'Edge.Cuts': 'Board shape',
|
||||
'Margin': 'Margin relative to edge cut',
|
||||
'F.CrtYd': 'Front courtyard area',
|
||||
'B.CrtYd': 'Bottom courtyard area',
|
||||
'F.Fab': 'Front documentation',
|
||||
'B.Fab': 'Bottom documentation',
|
||||
}
|
||||
# Names from the board file
|
||||
pcb_layers = {}
|
||||
plot_layers = {}
|
||||
|
||||
def __init__(self, name, suffix, desc):
|
||||
self.id = pcbnew.UNDEFINED_LAYER
|
||||
self.is_inner = False
|
||||
self.name = name
|
||||
self.suffix = suffix
|
||||
if desc is None and name in Layer.DEFAULT_LAYER_DESC:
|
||||
desc = Layer.DEFAULT_LAYER_DESC[name]
|
||||
self.desc = desc
|
||||
|
||||
@staticmethod
|
||||
def set_pcb_layers(board):
|
||||
for id in board.GetEnabledLayers().Seq():
|
||||
Layer.pcb_layers[board.GetLayerName(id)] = id
|
||||
|
||||
@staticmethod
|
||||
def _get_layers(d_layers):
|
||||
layers = []
|
||||
for n, id in d_layers.items():
|
||||
s = n.replace('.', '_')
|
||||
d = Layer.DEFAULT_LAYER_DESC.get(n)
|
||||
layers.append(Layer(n, s, d))
|
||||
return layers
|
||||
|
||||
@staticmethod
|
||||
def get_pcb_layers():
|
||||
return Layer._get_layers(Layer.pcb_layers)
|
||||
|
||||
@staticmethod
|
||||
def set_plot_layers(board):
|
||||
enabled = board.GetEnabledLayers().Seq()
|
||||
for id in board.GetPlotOptions().GetLayerSelection().Seq():
|
||||
if id in enabled:
|
||||
Layer.plot_layers[board.GetLayerName(id)] = id
|
||||
|
||||
@staticmethod
|
||||
def get_plot_layers():
|
||||
return Layer._get_layers(Layer.plot_layers)
|
||||
|
||||
def get_layer_id_from_name(self, layer_cnt):
|
||||
""" Get the pcbnew layer from the string provided in the config """
|
||||
# Priority
|
||||
# 1) Internal list
|
||||
if self.name in Layer.DEFAULT_LAYER_NAMES:
|
||||
self.id = Layer.DEFAULT_LAYER_NAMES[self.name]
|
||||
else:
|
||||
id = Layer.pcb_layers.get(self.name)
|
||||
if id is not None:
|
||||
# 2) List from the PCB
|
||||
self.id = id
|
||||
self.is_inner = id < pcbnew.B_Cu
|
||||
elif self.name.startswith("Inner"):
|
||||
# 3) Inner.N names
|
||||
m = re.match(r"^Inner\.([0-9]+)$", self.name)
|
||||
if not m:
|
||||
raise PlotError("Malformed inner layer name: {}, use Inner.N".format(self.name))
|
||||
self.id = int(m.group(1))
|
||||
self.is_inner = True
|
||||
else:
|
||||
raise PlotError("Unknown layer name: "+self.name)
|
||||
# Check if the layer is in use
|
||||
if self.is_inner and (self.id < 1 or self.id >= layer_cnt - 1):
|
||||
raise PlotError("Inner layer `{}` is not valid for this board".format(self))
|
||||
return self.id
|
||||
|
||||
@staticmethod
|
||||
def get_default_layers():
|
||||
layers = []
|
||||
for n, d in Layer.DEFAULT_LAYER_DESC.items():
|
||||
s = n.replace('.', '_')
|
||||
layers.append(Layer(n, s, d))
|
||||
return layers
|
||||
|
||||
def __str__(self):
|
||||
return "{} ({} '{}' {})".format(self.name, self.id, self.desc, self.suffix)
|
||||
|
||||
|
||||
def _import(name, path):
|
||||
# Python 3.4+ import mechanism
|
||||
spec = spec_from_file_location("kiplot."+name, path)
|
||||
|
|
@ -178,6 +50,7 @@ def _load_actions(path):
|
|||
def load_actions():
|
||||
""" Load all the available ouputs and preflights """
|
||||
from mcpy import activate
|
||||
# activate.activate()
|
||||
_load_actions(os.path.abspath(os.path.dirname(__file__)))
|
||||
home = os.environ.get('HOME')
|
||||
if home:
|
||||
|
|
@ -229,9 +102,7 @@ def load_board(pcb_file=None):
|
|||
board = pcbnew.LoadBoard(pcb_file)
|
||||
if BasePreFlight.get_option('check_zone_fills'):
|
||||
pcbnew.ZONE_FILLER(board).Fill(board.Zones())
|
||||
# Now we know the names of the layers for this board
|
||||
Layer.set_pcb_layers(board)
|
||||
Layer.set_plot_layers(board)
|
||||
GS.board = board
|
||||
except OSError as e:
|
||||
logger.error('Error loading PCB file. Currupted?')
|
||||
logger.error(e)
|
||||
|
|
@ -290,7 +161,7 @@ def generate_outputs(outputs, target, invert, skip_pre):
|
|||
# Generate outputs
|
||||
board = None
|
||||
for out in outputs:
|
||||
if (n == 0) or ((out.get_name() in target) ^ invert):
|
||||
if (n == 0) or ((out.name in target) ^ invert):
|
||||
logger.info('- '+str(out))
|
||||
# Should we load the PCB?
|
||||
if out.is_pcb() and (board is None):
|
||||
|
|
@ -298,9 +169,11 @@ def generate_outputs(outputs, target, invert, skip_pre):
|
|||
if out.is_sch():
|
||||
GS.check_sch()
|
||||
try:
|
||||
out.run(get_output_dir(out.get_outdir()), board)
|
||||
out.run(get_output_dir(out.dir), board)
|
||||
except PlotError as e:
|
||||
logger.error("In output `"+str(out)+"`: "+str(e))
|
||||
exit(PLOT_ERROR)
|
||||
except KiPlotConfigurationError as e:
|
||||
config_error("In section '"+out.name+"' ("+out.type+"): "+str(e))
|
||||
else:
|
||||
logger.debug('Skipping `%s` output', str(out))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,211 @@
|
|||
import pcbnew
|
||||
from .optionable import Optionable
|
||||
from .gs import GS
|
||||
from re import match
|
||||
|
||||
from .error import (PlotError, KiPlotConfigurationError)
|
||||
from kiplot.macros import macros, document, output_class # noqa: F401
|
||||
|
||||
|
||||
class Layer(Optionable):
|
||||
""" A layer description """
|
||||
# Default names
|
||||
DEFAULT_LAYER_NAMES = {
|
||||
'F.Cu': pcbnew.F_Cu,
|
||||
'B.Cu': pcbnew.B_Cu,
|
||||
'F.Adhes': pcbnew.F_Adhes,
|
||||
'B.Adhes': pcbnew.B_Adhes,
|
||||
'F.Paste': pcbnew.F_Paste,
|
||||
'B.Paste': pcbnew.B_Paste,
|
||||
'F.SilkS': pcbnew.F_SilkS,
|
||||
'B.SilkS': pcbnew.B_SilkS,
|
||||
'F.Mask': pcbnew.F_Mask,
|
||||
'B.Mask': pcbnew.B_Mask,
|
||||
'Dwgs.User': pcbnew.Dwgs_User,
|
||||
'Cmts.User': pcbnew.Cmts_User,
|
||||
'Eco1.User': pcbnew.Eco1_User,
|
||||
'Eco2.User': pcbnew.Eco2_User,
|
||||
'Edge.Cuts': pcbnew.Edge_Cuts,
|
||||
'Margin': pcbnew.Margin,
|
||||
'F.CrtYd': pcbnew.F_CrtYd,
|
||||
'B.CrtYd': pcbnew.B_CrtYd,
|
||||
'F.Fab': pcbnew.F_Fab,
|
||||
'B.Fab': pcbnew.B_Fab,
|
||||
}
|
||||
# Default names
|
||||
DEFAULT_LAYER_DESC = {
|
||||
'F.Cu': 'Front copper',
|
||||
'B.Cu': 'Bottom copper',
|
||||
'F.Adhes': 'Front adhesive (glue)',
|
||||
'B.Adhes': 'Bottom adhesive (glue)',
|
||||
'F.Paste': 'Front solder paste',
|
||||
'B.Paste': 'Bottom solder paste',
|
||||
'F.SilkS': 'Front silkscreen (artwork)',
|
||||
'B.SilkS': 'Bottom silkscreen (artwork)',
|
||||
'F.Mask': 'Front soldermask (negative)',
|
||||
'B.Mask': 'Bottom soldermask (negative)',
|
||||
'Dwgs.User': 'User drawings',
|
||||
'Cmts.User': 'User comments',
|
||||
'Eco1.User': 'For user usage 1',
|
||||
'Eco2.User': 'For user usage 2',
|
||||
'Edge.Cuts': 'Board shape',
|
||||
'Margin': 'Margin relative to edge cut',
|
||||
'F.CrtYd': 'Front courtyard area',
|
||||
'B.CrtYd': 'Bottom courtyard area',
|
||||
'F.Fab': 'Front documentation',
|
||||
'B.Fab': 'Bottom documentation',
|
||||
}
|
||||
# Names from the board file
|
||||
_pcb_layers = None
|
||||
_plot_layers = None
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.layer = ''
|
||||
""" Name of the layer. As you see it in KiCad """
|
||||
self.suffix = ''
|
||||
""" Suffix used in file names related to this layer. Derived from the name if not specified """
|
||||
self.description = ''
|
||||
""" A description for the layer, for documentation purposes """
|
||||
self._unkown_is_error = True
|
||||
|
||||
def config(self, tree):
|
||||
super().config(tree)
|
||||
if not self.layer:
|
||||
raise KiPlotConfigurationError("Missing or empty `layer` attribute for layer entry ({})".format(tree))
|
||||
if not self.description:
|
||||
if self.layer in Layer.DEFAULT_LAYER_DESC:
|
||||
self.description = Layer.DEFAULT_LAYER_DESC[self.layer]
|
||||
else:
|
||||
self.description = 'No description'
|
||||
if not self.suffix:
|
||||
self.suffix = self.layer.replace('.', '_')
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._id
|
||||
|
||||
@staticmethod
|
||||
def solve(values):
|
||||
board = GS.board
|
||||
layer_cnt = 2
|
||||
if board:
|
||||
layer_cnt = board.GetCopperLayerCount()
|
||||
# Get the list of used layers from the board
|
||||
# Used for 'all' but also to validate the layer names
|
||||
if Layer._pcb_layers is None:
|
||||
Layer._pcb_layers = {}
|
||||
if board:
|
||||
Layer._set_pcb_layers()
|
||||
# Get the list of selected layers for plot operations from the board
|
||||
if Layer._plot_layers is None:
|
||||
Layer._plot_layers = {}
|
||||
if board:
|
||||
Layer._set_plot_layers()
|
||||
# Solve string
|
||||
if isinstance(values, str):
|
||||
values = [values]
|
||||
# Solve list
|
||||
if isinstance(values, list):
|
||||
new_vals = []
|
||||
for layer in values:
|
||||
if isinstance(layer, Layer):
|
||||
layer._get_layer_id_from_name()
|
||||
# Check if the layer is in use
|
||||
if layer._is_inner and (layer._id < 1 or layer._id >= layer_cnt - 1):
|
||||
raise PlotError("Inner layer `{}` is not valid for this board".format(layer))
|
||||
new_vals.append(layer)
|
||||
else: # A string
|
||||
ext = None
|
||||
if layer == 'all':
|
||||
ext = Layer._get_layers(Layer._pcb_layers)
|
||||
elif layer == 'selected':
|
||||
ext = Layer._get_layers(Layer._plot_layers)
|
||||
elif layer == 'copper':
|
||||
ext = Layer._get_layers(Layer._get_copper())
|
||||
elif layer == 'technical':
|
||||
ext = Layer._get_layers(Layer._get_technical())
|
||||
elif layer == 'user':
|
||||
ext = Layer._get_layers(Layer._get_user())
|
||||
elif layer in Layer._pcb_layers:
|
||||
ext = [Layer.create_layer(layer)]
|
||||
if ext is None:
|
||||
raise KiPlotConfigurationError("Unknown layer spec: `{}`".format(layer))
|
||||
new_vals.extend(ext)
|
||||
return new_vals
|
||||
assert False, "Unimplemented layer type "+str(type(values)) # pragma: no cover
|
||||
|
||||
@staticmethod
|
||||
def _get_copper():
|
||||
return {GS.board.GetLayerName(id): id for id in GS.board.GetEnabledLayers().CuStack()}
|
||||
|
||||
@staticmethod
|
||||
def _get_technical():
|
||||
return {GS.board.GetLayerName(id): id for id in GS.board.GetEnabledLayers().Technicals()}
|
||||
|
||||
@staticmethod
|
||||
def _get_user():
|
||||
return {GS.board.GetLayerName(id): id for id in GS.board.GetEnabledLayers().Users()}
|
||||
|
||||
@staticmethod
|
||||
def _set_pcb_layers():
|
||||
Layer._pcb_layers = {GS.board.GetLayerName(id): id for id in GS.board.GetEnabledLayers().Seq()}
|
||||
|
||||
@classmethod
|
||||
def create_layer(cls, name):
|
||||
layer = cls()
|
||||
layer.layer = name
|
||||
layer.suffix = name.replace('.', '_')
|
||||
layer.description = Layer.DEFAULT_LAYER_DESC.get(name)
|
||||
layer._get_layer_id_from_name()
|
||||
return layer
|
||||
|
||||
@staticmethod
|
||||
def _get_layers(d_layers):
|
||||
layers = []
|
||||
for n, id in d_layers.items():
|
||||
layers.append(Layer.create_layer(n))
|
||||
return layers
|
||||
|
||||
@staticmethod
|
||||
def _set_plot_layers():
|
||||
board = GS.board
|
||||
enabled = board.GetEnabledLayers().Seq()
|
||||
for id in board.GetPlotOptions().GetLayerSelection().Seq():
|
||||
if id in enabled:
|
||||
Layer._plot_layers[board.GetLayerName(id)] = id
|
||||
|
||||
def _get_layer_id_from_name(self):
|
||||
""" Get the pcbnew layer from the string provided in the config """
|
||||
# Priority
|
||||
# 1) Internal list
|
||||
if self.layer in Layer.DEFAULT_LAYER_NAMES:
|
||||
self._id = Layer.DEFAULT_LAYER_NAMES[self.layer]
|
||||
self._is_inner = False
|
||||
else:
|
||||
id = Layer._pcb_layers.get(self.layer)
|
||||
if id is not None:
|
||||
# 2) List from the PCB
|
||||
self._id = id
|
||||
self._is_inner = id < pcbnew.B_Cu
|
||||
elif self.layer.startswith("Inner"):
|
||||
# 3) Inner.N names
|
||||
m = match(r"^Inner\.([0-9]+)$", self.layer)
|
||||
if not m:
|
||||
raise KiPlotConfigurationError("Malformed inner layer name: `{}`, use Inner.N".format(self.layer))
|
||||
self._id = int(m.group(1))
|
||||
self._is_inner = True
|
||||
else:
|
||||
raise KiPlotConfigurationError("Unknown layer name: `{}`".format(self.layer))
|
||||
return self._id
|
||||
|
||||
def get_id(self, layer_cnt):
|
||||
""" Returns the ID, also checks this is a valid inner layer """
|
||||
# Check if the layer is in use
|
||||
if self._is_inner and (self._id < 1 or self._id >= layer_cnt - 1):
|
||||
raise PlotError("Inner layer `{}` is not valid for this board".format(self))
|
||||
return self._id
|
||||
|
||||
def __str__(self):
|
||||
return "{} ({} '{}' {})".format(self.layer, self._id, self.description, self.suffix)
|
||||
|
|
@ -11,16 +11,14 @@ def filter(v):
|
|||
|
||||
|
||||
class Optionable(object):
|
||||
""" A class to validate and hold configuration options.
|
||||
""" A class to validate and hold configuration outputs/options.
|
||||
Is configured from a dict and the collected values are stored in its attributes. """
|
||||
_str_values_re = compile(r"\[string=.*\] \[([^\]]+)\]")
|
||||
_num_range_re = compile(r"\[number=.*\] \[(-?\d+),(-?\d+)\]")
|
||||
|
||||
def __init__(self, name, description):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._name = name
|
||||
self._description = description
|
||||
self._unkown_is_error = True
|
||||
self._unkown_is_error = False
|
||||
|
||||
@staticmethod
|
||||
def _check_str(key, val, doc):
|
||||
|
|
@ -61,13 +59,13 @@ class Optionable(object):
|
|||
if isinstance(v, dict):
|
||||
return 'dict'
|
||||
if isinstance(v, list):
|
||||
return 'list'
|
||||
return 'list({})'.format(Optionable._typeof(v[0]))
|
||||
return 'None'
|
||||
|
||||
def _perform_config_mapping(self):
|
||||
""" Map the options to class attributes """
|
||||
attrs = Optionable.get_attrs_for(self)
|
||||
for k, v in self._options.items():
|
||||
attrs = self.get_attrs_for()
|
||||
for k, v in self._tree.items():
|
||||
# Map known attributes and avoid mapping private ones
|
||||
if (k[0] == '_') or (k not in attrs):
|
||||
if self._unkown_is_error:
|
||||
|
|
@ -105,24 +103,47 @@ class Optionable(object):
|
|||
# Dicts are solved using Optionable classes
|
||||
new_val = v
|
||||
# Create an object for the valid class
|
||||
v = cur_val(k, cur_doc)
|
||||
v = cur_val()
|
||||
# Delegate the validation to the object
|
||||
v.config(new_val)
|
||||
elif isinstance(v, list):
|
||||
new_val = []
|
||||
for element in v:
|
||||
e_type = 'list('+Optionable._typeof(element)+')'
|
||||
if e_type not in valid:
|
||||
raise KiPlotConfigurationError("Option `{}` must be any of {} not `{}`".
|
||||
format(element, valid, e_type))
|
||||
if isinstance(element, dict):
|
||||
nv = cur_val()
|
||||
nv.config(element)
|
||||
new_val.append(nv)
|
||||
else:
|
||||
new_val.append(element)
|
||||
v = new_val
|
||||
# Seems to be ok, map it
|
||||
setattr(self, k, v)
|
||||
|
||||
def config(self, options):
|
||||
self._options = options
|
||||
if options:
|
||||
def config(self, tree):
|
||||
self._tree = tree
|
||||
if tree:
|
||||
self._perform_config_mapping()
|
||||
|
||||
@staticmethod
|
||||
def get_attrs_for(obj):
|
||||
def get_attrs_for(self):
|
||||
""" Returns all attributes """
|
||||
return dict(inspect.getmembers(obj, filter))
|
||||
return dict(inspect.getmembers(self, filter))
|
||||
|
||||
@staticmethod
|
||||
def get_attrs_gen(obj):
|
||||
def get_attrs_gen(self):
|
||||
""" Returns a (key, val) iterator on public attributes """
|
||||
attrs = Optionable.get_attrs_for(obj)
|
||||
attrs = self.get_attrs_for()
|
||||
return ((k, v) for k, v in attrs.items() if k[0] != '_')
|
||||
|
||||
|
||||
class BaseOptions(Optionable):
|
||||
""" A class to validate and hold output options.
|
||||
Is configured from a dict and the collected values are stored in its attributes. """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def read_vals_from_po(self, po):
|
||||
""" Set attributes from a PCB_PLOT_PARAMS (plot options) """
|
||||
return
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import os
|
||||
from pcbnew import (PLOT_FORMAT_HPGL, PLOT_FORMAT_POST, PLOT_FORMAT_GERBER, PLOT_FORMAT_DXF, PLOT_FORMAT_SVG,
|
||||
PLOT_FORMAT_PDF, wxPoint)
|
||||
from .optionable import Optionable
|
||||
from .out_base import BaseOutput
|
||||
from .optionable import (Optionable, BaseOptions)
|
||||
from kiplot.macros import macros, document # noqa: F401
|
||||
from . import log
|
||||
|
||||
|
|
@ -10,24 +9,26 @@ logger = log.get_logger(__name__)
|
|||
|
||||
|
||||
class DrillMap(Optionable):
|
||||
def __init__(self, name, description):
|
||||
super().__init__(name, description)
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.type = 'pdf'
|
||||
""" [hpgl,ps,gerber,dxf,svg,pdf] format for a graphical drill map """
|
||||
""" [hpgl,ps,gerber,dxf,svg,pdf] format for a graphical drill map """ # pragma: no cover
|
||||
self._unkown_is_error = True
|
||||
|
||||
|
||||
class DrillReport(Optionable):
|
||||
def __init__(self, name, description):
|
||||
super().__init__(name, description)
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.filename = ''
|
||||
""" name of the drill report. Not generated unless a name is specified """
|
||||
""" name of the drill report. Not generated unless a name is specified """ # pragma: no cover
|
||||
self._unkown_is_error = True
|
||||
|
||||
|
||||
class AnyDrill(BaseOutput):
|
||||
def __init__(self, name, type, description):
|
||||
super().__init__(name, type, description)
|
||||
class AnyDrill(BaseOptions):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
# Options
|
||||
with document:
|
||||
self.use_aux_axis_as_origin = False
|
||||
|
|
@ -47,8 +48,8 @@ class AnyDrill(BaseOutput):
|
|||
'pdf': PLOT_FORMAT_PDF
|
||||
}
|
||||
|
||||
def config(self, outdir, options, layers):
|
||||
super().config(outdir, options, layers)
|
||||
def config(self, tree):
|
||||
super().config(tree)
|
||||
# Solve the map for both cases
|
||||
if isinstance(self.map, str):
|
||||
self.map = self._map_map[self.map]
|
||||
|
|
|
|||
|
|
@ -3,19 +3,18 @@ from pcbnew import (GERBER_JOBFILE_WRITER, PLOT_CONTROLLER, IsCopperLayer)
|
|||
from .out_base import (BaseOutput)
|
||||
from .error import (PlotError, KiPlotConfigurationError)
|
||||
from .gs import (GS)
|
||||
from .optionable import BaseOptions
|
||||
from .layer import Layer
|
||||
from kiplot.macros import macros, document # noqa: F401
|
||||
from . import log
|
||||
|
||||
logger = log.get_logger(__name__)
|
||||
|
||||
|
||||
class AnyLayer(BaseOutput):
|
||||
class AnyLayerOptions(BaseOptions):
|
||||
""" Base class for: DXF, Gerber, HPGL, PDF, PS and SVG """
|
||||
def __init__(self, name, type, description):
|
||||
super().__init__(name, type, description)
|
||||
# We need layers, so we define it
|
||||
self._layers = None
|
||||
# Options
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.exclude_edge_layer = True
|
||||
""" do not include the PCB edge layer """
|
||||
|
|
@ -32,12 +31,6 @@ class AnyLayer(BaseOutput):
|
|||
self.tent_vias = True
|
||||
""" cover the vias """ # pragma: no cover
|
||||
|
||||
def config(self, outdir, options, layers):
|
||||
super().config(outdir, options, layers)
|
||||
# We need layers
|
||||
if not self._layers:
|
||||
raise KiPlotConfigurationError("Missing `layers` list")
|
||||
|
||||
def _configure_plot_ctrl(self, po, output_dir):
|
||||
logger.debug("Configuring plot controller for output")
|
||||
po.SetOutputDirectory(output_dir)
|
||||
|
|
@ -53,15 +46,13 @@ class AnyLayer(BaseOutput):
|
|||
# We'll come back to this on a per-layer basis
|
||||
po.SetSkipPlotNPTH_Pads(False)
|
||||
|
||||
def run(self, output_dir, board):
|
||||
def run(self, output_dir, board, layers):
|
||||
# fresh plot controller
|
||||
plot_ctrl = PLOT_CONTROLLER(board)
|
||||
# set up plot options for the whole output
|
||||
po = plot_ctrl.GetPlotOptions()
|
||||
self._configure_plot_ctrl(po, output_dir)
|
||||
|
||||
layer_cnt = board.GetCopperLayerCount()
|
||||
|
||||
# Gerber Job files aren't automagically created
|
||||
# We need to assist KiCad
|
||||
create_job = po.GetCreateGerberJobFile()
|
||||
|
|
@ -70,11 +61,12 @@ class AnyLayer(BaseOutput):
|
|||
|
||||
plot_ctrl.SetColorMode(True)
|
||||
|
||||
layers = Layer.solve(layers)
|
||||
# plot every layer in the output
|
||||
for l in self._layers:
|
||||
for l in layers:
|
||||
suffix = l.suffix
|
||||
desc = l.desc
|
||||
id = l.get_layer_id_from_name(layer_cnt)
|
||||
desc = l.description
|
||||
id = l.id
|
||||
# Set current layer
|
||||
plot_ctrl.SetLayer(id)
|
||||
# Skipping NPTH is controlled by whether or not this is
|
||||
|
|
@ -116,3 +108,21 @@ class AnyLayer(BaseOutput):
|
|||
self.tent_vias = not po.GetPlotViaOnMaskLayer()
|
||||
# padsonsilk
|
||||
self.exclude_pads_from_silkscreen = not po.GetPlotPadsOnSilkLayer()
|
||||
|
||||
|
||||
class AnyLayer(BaseOutput):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.layers = Layer
|
||||
""" [list(dict)|list(string)|string] [all,selected,copper,technical,user]
|
||||
List of PCB layers to plot """ # pragma: no cover
|
||||
|
||||
def config(self, tree):
|
||||
super().config(tree)
|
||||
# We need layers
|
||||
if isinstance(self.layers, type):
|
||||
raise KiPlotConfigurationError("Missing `layers` list")
|
||||
|
||||
def run(self, output_dir, board):
|
||||
self.options.run(output_dir, board, self.layers)
|
||||
|
|
|
|||
|
|
@ -1,45 +1,33 @@
|
|||
from .optionable import Optionable
|
||||
from .reg_out import RegOutput
|
||||
from kiplot.macros import macros, document # noqa: F401
|
||||
from . import log
|
||||
|
||||
logger = log.get_logger(__name__)
|
||||
|
||||
|
||||
class BaseOutput(Optionable):
|
||||
class BaseOutput(RegOutput):
|
||||
_registered = {}
|
||||
|
||||
def __init__(self, name, type, description):
|
||||
super().__init__(name, description)
|
||||
self._type = type
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.name = ''
|
||||
""" Used to identify this particular output definition """
|
||||
self.type = ''
|
||||
""" Type of output """
|
||||
self.dir = '.'
|
||||
""" Output directory for the generated files """
|
||||
self.comment = ''
|
||||
""" A comment for documentation purposes """ # pragma: no cover
|
||||
self._sch_related = False
|
||||
self._unkown_is_error = False
|
||||
|
||||
def config(self, outdir, options, layers):
|
||||
self._outdir = outdir
|
||||
self._layers = layers
|
||||
super().config(options)
|
||||
self._unkown_is_error = True
|
||||
|
||||
@staticmethod
|
||||
def attr2longopt(attr):
|
||||
return '--'+attr.replace('_', '-')
|
||||
|
||||
@staticmethod
|
||||
def register(name, aclass):
|
||||
BaseOutput._registered[name] = aclass
|
||||
|
||||
@staticmethod
|
||||
def is_registered(name):
|
||||
return name in BaseOutput._registered
|
||||
|
||||
@staticmethod
|
||||
def get_class_for(name):
|
||||
return BaseOutput._registered[name]
|
||||
|
||||
@staticmethod
|
||||
def get_registered():
|
||||
return BaseOutput._registered
|
||||
|
||||
def __str__(self):
|
||||
return "'{}' ({}) [{}]".format(self._description, self._name, self._type)
|
||||
return "'{}' ({}) [{}]".format(self.comment, self.name, self.type)
|
||||
|
||||
def is_sch(self):
|
||||
""" True for outputs that works on the schematic """
|
||||
|
|
@ -49,17 +37,11 @@ class BaseOutput(Optionable):
|
|||
""" True for outputs that works on the PCB """
|
||||
return not self._sch_related
|
||||
|
||||
def read_vals_from_po(self, po):
|
||||
""" Set attributes from a PCB_PLOT_PARAMS (plot options) """
|
||||
return
|
||||
def config(self, tree):
|
||||
super().config(tree)
|
||||
if getattr(self, 'options', None) and isinstance(self.options, type):
|
||||
# No options, get the defaults
|
||||
self.options = self.options()
|
||||
|
||||
# These get_* aren't really needed.
|
||||
# _* members aren't supposed to be used by the user, not the code.
|
||||
def get_name(self):
|
||||
return self._name
|
||||
|
||||
def get_outdir(self):
|
||||
return self._outdir
|
||||
|
||||
def run(self, output_dir, board): # pragma: no cover
|
||||
logger.error("The run member for the class for the output type `{}` isn't implemented".format(self._type))
|
||||
def run(self, output_dir, board):
|
||||
self.options.run(output_dir, board)
|
||||
|
|
|
|||
|
|
@ -4,17 +4,9 @@ from kiplot.drill_marks import DrillMarks
|
|||
from kiplot.macros import macros, document, output_class # noqa: F401
|
||||
|
||||
|
||||
@output_class
|
||||
class DXF(AnyLayer, DrillMarks):
|
||||
"""
|
||||
DXF (Drawing Exchange Format)
|
||||
Exports the PCB to 2D mechanical EDA tools (like AutoCAD).
|
||||
This output is what you get from the File/Plot menu in pcbnew. """
|
||||
def __init__(self, name, type, description):
|
||||
AnyLayer.__init__(self, name, type, description)
|
||||
DrillMarks.__init__(self)
|
||||
self._plot_format = PLOT_FORMAT_DXF
|
||||
# Options
|
||||
class DXFOptions(DrillMarks):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.use_aux_axis_as_origin = False
|
||||
""" use the auxiliar axis as origin for coordinates """
|
||||
|
|
@ -24,14 +16,10 @@ class DXF(AnyLayer, DrillMarks):
|
|||
""" use mm instead of inches """
|
||||
self.sketch_plot = False
|
||||
""" don't fill objects, just draw the outline """ # pragma: no cover
|
||||
|
||||
def config(self, outdir, options, layers):
|
||||
AnyLayer.config(self, outdir, options, layers)
|
||||
DrillMarks.config(self)
|
||||
self._plot_format = PLOT_FORMAT_DXF
|
||||
|
||||
def _configure_plot_ctrl(self, po, output_dir):
|
||||
AnyLayer._configure_plot_ctrl(self, po, output_dir)
|
||||
DrillMarks._configure_plot_ctrl(self, po, output_dir)
|
||||
super()._configure_plot_ctrl(po, output_dir)
|
||||
po.SetDXFPlotPolygonMode(self.polygon_mode)
|
||||
# DXF_PLOTTER::DXF_UNITS isn't available
|
||||
# According to https://docs.kicad-pcb.org/doxygen/classDXF__PLOTTER.html 1 is mm
|
||||
|
|
@ -40,9 +28,21 @@ class DXF(AnyLayer, DrillMarks):
|
|||
po.SetUseAuxOrigin(self.use_aux_axis_as_origin)
|
||||
|
||||
def read_vals_from_po(self, po):
|
||||
AnyLayer.read_vals_from_po(self, po)
|
||||
DrillMarks.read_vals_from_po(self, po)
|
||||
super().read_vals_from_po(po)
|
||||
self.polygon_mode = po.GetDXFPlotPolygonMode()
|
||||
self.metric_units = po.GetDXFPlotUnits() == 1
|
||||
self.sketch_plot = po.GetPlotMode() == SKETCH
|
||||
self.use_aux_axis_as_origin = po.GetUseAuxOrigin()
|
||||
|
||||
|
||||
@output_class
|
||||
class DXF(AnyLayer):
|
||||
"""
|
||||
DXF (Drawing Exchange Format)
|
||||
Exports the PCB to 2D mechanical EDA tools (like AutoCAD).
|
||||
This output is what you get from the File/Plot menu in pcbnew. """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.options = DXFOptions
|
||||
""" [dict] Options for the `dxf` output """ # pragma: no cover
|
||||
|
|
|
|||
|
|
@ -1,16 +1,11 @@
|
|||
from pcbnew import (EXCELLON_WRITER)
|
||||
from .out_any_drill import (AnyDrill)
|
||||
from pcbnew import EXCELLON_WRITER
|
||||
from .out_any_drill import AnyDrill
|
||||
from kiplot.macros import macros, document, output_class # noqa: F401
|
||||
|
||||
|
||||
@output_class
|
||||
class Excellon(AnyDrill):
|
||||
""" Excellon drill format
|
||||
This is the main format for the drilling machine.
|
||||
You can create a map file for documentation purposes.
|
||||
This output is what you get from the 'File/Fabrication output/Drill Files' menu in pcbnew. """
|
||||
def __init__(self, name, type, description):
|
||||
super().__init__(name, type, description)
|
||||
class ExcellonOptions(AnyDrill):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.metric_units = True
|
||||
""" use metric units instead of inches """
|
||||
|
|
@ -26,3 +21,16 @@ class Excellon(AnyDrill):
|
|||
drill_writer.SetOptions(self.mirror_y_axis, self.minimal_header, offset, self.pth_and_npth_single_file)
|
||||
drill_writer.SetFormat(self.metric_units, EXCELLON_WRITER.DECIMAL_FORMAT)
|
||||
return drill_writer
|
||||
|
||||
|
||||
@output_class
|
||||
class Excellon(BaseOutput): # noqa: F821
|
||||
""" Excellon drill format
|
||||
This is the main format for the drilling machine.
|
||||
You can create a map file for documentation purposes.
|
||||
This output is what you get from the 'File/Fabrication output/Drill Files' menu in pcbnew. """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.options = ExcellonOptions
|
||||
""" [dict] Options for the `excellon` output """ # pragma: no cover
|
||||
|
|
|
|||
|
|
@ -1,16 +1,11 @@
|
|||
from pcbnew import (GERBER_WRITER)
|
||||
from .out_any_drill import (AnyDrill)
|
||||
from kiplot.macros import macros, output_class # noqa: F401
|
||||
from pcbnew import GERBER_WRITER
|
||||
from .out_any_drill import AnyDrill
|
||||
from kiplot.macros import macros, document, output_class # noqa: F401
|
||||
|
||||
|
||||
@output_class
|
||||
class Gerb_Drill(AnyDrill):
|
||||
""" Gerber drill format
|
||||
This is the information for the drilling machine in gerber format.
|
||||
You can create a map file for documentation purposes.
|
||||
This output is what you get from the 'File/Fabrication output/Drill Files' menu in pcbnew. """
|
||||
def __init__(self, name, type, description):
|
||||
super().__init__(name, type, description)
|
||||
class Gerb_DrillOptions(AnyDrill):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def _configure_writer(self, board, offset):
|
||||
drill_writer = GERBER_WRITER(board)
|
||||
|
|
@ -18,3 +13,16 @@ class Gerb_Drill(AnyDrill):
|
|||
drill_writer.SetFormat(5)
|
||||
drill_writer.SetOptions(offset)
|
||||
return drill_writer
|
||||
|
||||
|
||||
@output_class
|
||||
class Gerb_Drill(BaseOutput): # noqa: F821
|
||||
""" Gerber drill format
|
||||
This is the information for the drilling machine in gerber format.
|
||||
You can create a map file for documentation purposes.
|
||||
This output is what you get from the 'File/Fabrication output/Drill Files' menu in pcbnew. """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.options = Gerb_DrillOptions
|
||||
""" [dict] Options for the `gerb_drill` output """ # pragma: no cover
|
||||
|
|
|
|||
|
|
@ -1,18 +1,13 @@
|
|||
from pcbnew import (PLOT_FORMAT_GERBER, FromMM, ToMM)
|
||||
from .out_any_layer import (AnyLayer)
|
||||
from .out_any_layer import (AnyLayer, AnyLayerOptions)
|
||||
from .error import KiPlotConfigurationError
|
||||
from kiplot.macros import macros, document, output_class # noqa: F401
|
||||
|
||||
|
||||
@output_class
|
||||
class Gerber(AnyLayer):
|
||||
""" Gerber format
|
||||
This is the main fabrication format for the PCB.
|
||||
This output is what you get from the File/Plot menu in pcbnew. """
|
||||
def __init__(self, name, type, description):
|
||||
super().__init__(name, type, description)
|
||||
class GerberOptions(AnyLayerOptions):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._plot_format = PLOT_FORMAT_GERBER
|
||||
# Options
|
||||
with document:
|
||||
self.use_aux_axis_as_origin = False
|
||||
""" use the auxiliar axis as origin for coordinates """
|
||||
|
|
@ -71,3 +66,15 @@ class Gerber(AnyLayer):
|
|||
self.use_aux_axis_as_origin = po.GetUseAuxOrigin()
|
||||
# linewidth
|
||||
self.line_width = ToMM(po.GetLineWidth())
|
||||
|
||||
|
||||
@output_class
|
||||
class Gerber(AnyLayer):
|
||||
""" Gerber format
|
||||
This is the main fabrication format for the PCB.
|
||||
This output is what you get from the File/Plot menu in pcbnew. """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.options = GerberOptions
|
||||
""" [dict] Options for the `gerber` output """ # pragma: no cover
|
||||
|
|
|
|||
|
|
@ -5,15 +5,9 @@ from kiplot.drill_marks import DrillMarks
|
|||
from kiplot.macros import macros, document, output_class # noqa: F401
|
||||
|
||||
|
||||
@output_class
|
||||
class HPGL(AnyLayer, DrillMarks):
|
||||
""" HPGL (Hewlett & Packard Graphics Language)
|
||||
Exports the PCB for plotters and laser printers.
|
||||
This output is what you get from the File/Plot menu in pcbnew. """
|
||||
def __init__(self, name, type, description):
|
||||
super().__init__(name, type, description)
|
||||
self._plot_format = PLOT_FORMAT_HPGL
|
||||
# Options
|
||||
class HPGLOptions(DrillMarks):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.mirror_plot = False
|
||||
""" plot mirrored """
|
||||
|
|
@ -27,14 +21,10 @@ class HPGL(AnyLayer, DrillMarks):
|
|||
""" [1,99] pen speed """
|
||||
self.pen_width = 15
|
||||
""" [0,100] pen diameter in MILS, useful to fill areas. However, it is in mm in HPGL files """ # pragma: no cover
|
||||
|
||||
def config(self, outdir, options, layers):
|
||||
AnyLayer.config(self, outdir, options, layers)
|
||||
DrillMarks.config(self)
|
||||
self._plot_format = PLOT_FORMAT_HPGL
|
||||
|
||||
def _configure_plot_ctrl(self, po, output_dir):
|
||||
AnyLayer._configure_plot_ctrl(self, po, output_dir)
|
||||
DrillMarks._configure_plot_ctrl(self, po, output_dir)
|
||||
super()._configure_plot_ctrl(po, output_dir)
|
||||
po.SetHPGLPenDiameter(self.pen_width)
|
||||
po.SetHPGLPenNum(self.pen_number)
|
||||
po.SetHPGLPenSpeed(self.pen_speed)
|
||||
|
|
@ -49,8 +39,7 @@ class HPGL(AnyLayer, DrillMarks):
|
|||
po.SetScale(self.scaling)
|
||||
|
||||
def read_vals_from_po(self, po):
|
||||
AnyLayer.read_vals_from_po(self, po)
|
||||
DrillMarks.read_vals_from_po(self, po)
|
||||
super().read_vals_from_po(po)
|
||||
self.pen_width = po.GetHPGLPenDiameter()
|
||||
self.pen_number = po.GetHPGLPenNum()
|
||||
self.pen_speed = po.GetHPGLPenSpeed()
|
||||
|
|
@ -61,3 +50,15 @@ class HPGL(AnyLayer, DrillMarks):
|
|||
self.scaling = AUTO_SCALE
|
||||
else:
|
||||
self.scaling = po.GetScale()
|
||||
|
||||
|
||||
@output_class
|
||||
class HPGL(AnyLayer):
|
||||
""" HPGL (Hewlett & Packard Graphics Language)
|
||||
Exports the PCB for plotters and laser printers.
|
||||
This output is what you get from the File/Plot menu in pcbnew. """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.options = HPGLOptions
|
||||
""" [dict] Options for the `hpgl` output """ # pragma: no cover
|
||||
|
|
|
|||
|
|
@ -3,23 +3,16 @@ from subprocess import (check_output, STDOUT, CalledProcessError)
|
|||
from .misc import (CMD_IBOM, URL_IBOM, BOM_ERROR)
|
||||
from .gs import (GS)
|
||||
from .kiplot import (check_script)
|
||||
from .optionable import Optionable
|
||||
from .optionable import BaseOptions
|
||||
from kiplot.macros import macros, document, output_class # noqa: F401
|
||||
from . import log
|
||||
|
||||
logger = log.get_logger(__name__)
|
||||
|
||||
|
||||
@output_class
|
||||
class IBoM(BaseOutput): # noqa: F821
|
||||
""" IBoM (Interactive HTML BoM)
|
||||
Generates an interactive web page useful to identify the position of the components in the PCB.
|
||||
For more information: https://github.com/INTI-CMNB/InteractiveHtmlBom
|
||||
This output is what you get from the InteractiveHtmlBom plug-in (pcbnew). """
|
||||
def __init__(self, name, type, description):
|
||||
super().__init__(name, type, description)
|
||||
self._sch_related = True
|
||||
# Options
|
||||
class IBoMOptions(BaseOptions):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.dark_mode = False
|
||||
""" Default to dark mode """
|
||||
|
|
@ -85,7 +78,7 @@ class IBoM(BaseOutput): # noqa: F821
|
|||
os.environ['INTERACTIVE_HTML_BOM_NO_DISPLAY'] = ''
|
||||
cmd = [CMD_IBOM, GS.pcb_file, '--dest-dir', output_dir, '--no-browser', ]
|
||||
# Convert attributes into options
|
||||
for k, v in Optionable.get_attrs_gen(self):
|
||||
for k, v in self.get_attrs_gen():
|
||||
if not v:
|
||||
continue
|
||||
cmd.append(BaseOutput.attr2longopt(k)) # noqa: F821
|
||||
|
|
@ -101,3 +94,17 @@ class IBoM(BaseOutput): # noqa: F821
|
|||
logger.debug('Output from command: '+e.output.decode())
|
||||
exit(BOM_ERROR)
|
||||
logger.debug('Output from command:\n'+cmd_output.decode()+'\n')
|
||||
|
||||
|
||||
@output_class
|
||||
class IBoM(BaseOutput): # noqa: F821
|
||||
""" IBoM (Interactive HTML BoM)
|
||||
Generates an interactive web page useful to identify the position of the components in the PCB.
|
||||
For more information: https://github.com/INTI-CMNB/InteractiveHtmlBom
|
||||
This output is what you get from the InteractiveHtmlBom plug-in (pcbnew). """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.options = IBoMOptions
|
||||
""" [dict] Options for the `ibom` output """ # pragma: no cover
|
||||
self._sch_related = True
|
||||
|
|
|
|||
|
|
@ -4,22 +4,16 @@ from subprocess import (check_output, STDOUT, CalledProcessError)
|
|||
from .misc import (CMD_KIBOM, URL_KIBOM, BOM_ERROR)
|
||||
from .kiplot import (check_script)
|
||||
from .gs import (GS)
|
||||
from .optionable import BaseOptions
|
||||
from kiplot.macros import macros, document, output_class # noqa: F401
|
||||
from . import log
|
||||
|
||||
logger = log.get_logger(__name__)
|
||||
|
||||
|
||||
@output_class
|
||||
class KiBoM(BaseOutput): # noqa: F821
|
||||
""" KiBoM (KiCad Bill of Materials)
|
||||
Used to generate the BoM in HTML or CSV format using the KiBoM plug-in.
|
||||
For more information: https://github.com/INTI-CMNB/KiBoM
|
||||
This output is what you get from the 'Tools/Generate Bill of Materials' menu in eeschema. """
|
||||
def __init__(self, name, type, description):
|
||||
super().__init__(name, type, description)
|
||||
self._sch_related = True
|
||||
# Options
|
||||
class KiBoMOptions(BaseOptions):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.number = 1
|
||||
""" Number of boards to build (components multiplier) """
|
||||
|
|
@ -62,3 +56,17 @@ class KiBoM(BaseOutput): # noqa: F821
|
|||
for f in glob(os.path.join(output_dir, prj)+'*.tmp'):
|
||||
os.remove(f)
|
||||
logger.debug('Output from command:\n'+cmd_output.decode())
|
||||
|
||||
|
||||
@output_class
|
||||
class KiBoM(BaseOutput): # noqa: F821
|
||||
""" KiBoM (KiCad Bill of Materials)
|
||||
Used to generate the BoM in HTML or CSV format using the KiBoM plug-in.
|
||||
For more information: https://github.com/INTI-CMNB/KiBoM
|
||||
This output is what you get from the 'Tools/Generate Bill of Materials' menu in eeschema. """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.options = KiBoMOptions
|
||||
""" [dict] Options for the `kibom` output """ # pragma: no cover
|
||||
self._sch_related = True
|
||||
|
|
|
|||
|
|
@ -7,16 +7,9 @@ from . import log
|
|||
logger = log.get_logger(__name__)
|
||||
|
||||
|
||||
@output_class
|
||||
class PDF(AnyLayer, DrillMarks):
|
||||
""" PDF (Portable Document Format)
|
||||
Exports the PCB to the most common exhange format. Suitable for printing.
|
||||
Note that this output isn't the best for documating your project.
|
||||
This output is what you get from the File/Plot menu in pcbnew. """
|
||||
def __init__(self, name, type, description):
|
||||
super().__init__(name, type, description)
|
||||
self._plot_format = PLOT_FORMAT_PDF
|
||||
# Options
|
||||
class PDFOptions(DrillMarks):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.line_width = 0.1
|
||||
""" [0.02,2] for objects without width [mm] """
|
||||
|
|
@ -24,21 +17,29 @@ class PDF(AnyLayer, DrillMarks):
|
|||
""" plot mirrored """
|
||||
self.negative_plot = False
|
||||
""" invert black and white """ # pragma: no cover
|
||||
|
||||
def config(self, outdir, options, layers):
|
||||
AnyLayer.config(self, outdir, options, layers)
|
||||
DrillMarks.config(self)
|
||||
self._plot_format = PLOT_FORMAT_PDF
|
||||
|
||||
def _configure_plot_ctrl(self, po, output_dir):
|
||||
AnyLayer._configure_plot_ctrl(self, po, output_dir)
|
||||
DrillMarks._configure_plot_ctrl(self, po, output_dir)
|
||||
super()._configure_plot_ctrl(po, output_dir)
|
||||
po.SetMirror(self.mirror_plot)
|
||||
po.SetLineWidth(FromMM(self.line_width))
|
||||
po.SetNegative(self.negative_plot)
|
||||
|
||||
def read_vals_from_po(self, po):
|
||||
AnyLayer.read_vals_from_po(self, po)
|
||||
DrillMarks.read_vals_from_po(self, po)
|
||||
super().read_vals_from_po(po)
|
||||
self.mirror_plot = po.GetMirror()
|
||||
self.line_width = ToMM(po.GetLineWidth())
|
||||
self.negative_plot = po.GetNegative()
|
||||
|
||||
|
||||
@output_class
|
||||
class PDF(AnyLayer, DrillMarks):
|
||||
""" PDF (Portable Document Format)
|
||||
Exports the PCB to the most common exhange format. Suitable for printing.
|
||||
Note that this output isn't the best for documating your project.
|
||||
This output is what you get from the File/Plot menu in pcbnew. """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.options = PDFOptions
|
||||
""" [dict] Options for the `pdf` output """ # pragma: no cover
|
||||
|
|
|
|||
|
|
@ -5,39 +5,23 @@ from .error import (KiPlotConfigurationError)
|
|||
from .gs import (GS)
|
||||
from .kiplot import (check_script)
|
||||
from .misc import (CMD_PCBNEW_PRINT_LAYERS, URL_PCBNEW_PRINT_LAYERS, PDF_PCB_PRINT)
|
||||
from .optionable import BaseOptions
|
||||
from kiplot.macros import macros, document, output_class # noqa: F401
|
||||
from .layer import Layer
|
||||
from . import log
|
||||
|
||||
logger = log.get_logger(__name__)
|
||||
|
||||
|
||||
@output_class
|
||||
class PDF_Pcb_Print(BaseOutput): # noqa: F821
|
||||
""" PDF PCB Print (Portable Document Format)
|
||||
Exports the PCB to the most common exhange format. Suitable for printing.
|
||||
This is the main format to document your PCB.
|
||||
This output is what you get from the 'File/Print' menu in pcbnew. """
|
||||
def __init__(self, name, type, description):
|
||||
super().__init__(name, type, description)
|
||||
# We need layers, so we define it
|
||||
self._layers = None
|
||||
# Options
|
||||
class PDF_Pcb_PrintOptions(BaseOptions):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.output_name = ''
|
||||
""" filename for the output PDF (the name of the PCB if empty) """ # pragma: no cover
|
||||
|
||||
def config(self, outdir, options, layers):
|
||||
super().config(outdir, options, layers)
|
||||
# We need layers
|
||||
if not self._layers:
|
||||
raise KiPlotConfigurationError("Missing `layers` list")
|
||||
|
||||
def run(self, output_dir, board):
|
||||
def run(self, output_dir, board, layers):
|
||||
check_script(CMD_PCBNEW_PRINT_LAYERS, URL_PCBNEW_PRINT_LAYERS, '1.4.1')
|
||||
# Verify the inner layers
|
||||
layer_cnt = board.GetCopperLayerCount()
|
||||
for l in self._layers:
|
||||
l.get_layer_id_from_name(layer_cnt)
|
||||
# Output file name
|
||||
output = self.output_name
|
||||
if not output:
|
||||
|
|
@ -51,10 +35,36 @@ class PDF_Pcb_Print(BaseOutput): # noqa: F821
|
|||
cmd.insert(1, '-vv')
|
||||
cmd.insert(1, '-r')
|
||||
# Add the layers
|
||||
for l in self._layers:
|
||||
cmd.append(l.name)
|
||||
layers = Layer.solve(layers)
|
||||
cmd.extend([l.layer for l in layers])
|
||||
# Execute it
|
||||
logger.debug('Executing: '+str(cmd))
|
||||
ret = call(cmd)
|
||||
if ret:
|
||||
logger.error(CMD_PCBNEW_PRINT_LAYERS+' returned %d', ret)
|
||||
exit(PDF_PCB_PRINT)
|
||||
|
||||
|
||||
@output_class
|
||||
class PDF_Pcb_Print(BaseOutput): # noqa: F821
|
||||
""" PDF PCB Print (Portable Document Format)
|
||||
Exports the PCB to the most common exhange format. Suitable for printing.
|
||||
This is the main format to document your PCB.
|
||||
This output is what you get from the 'File/Print' menu in pcbnew. """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.options = PDF_Pcb_PrintOptions
|
||||
""" [dict] Options for the `pdf_pcb_print` output """
|
||||
self.layers = Layer
|
||||
""" [list(dict)|list(string)|string] [all,selected,copper,technical,user]
|
||||
List of PCB layers to include in the PDF """ # pragma: no cover
|
||||
|
||||
def config(self, tree):
|
||||
super().config(tree)
|
||||
# We need layers
|
||||
if isinstance(self.layers, type):
|
||||
raise KiPlotConfigurationError("Missing `layers` list")
|
||||
|
||||
def run(self, output_dir, board):
|
||||
self.options.run(output_dir, board, self.layers)
|
||||
|
|
|
|||
|
|
@ -3,22 +3,16 @@ from subprocess import (call)
|
|||
from .gs import (GS)
|
||||
from .kiplot import (check_eeschema_do)
|
||||
from .misc import (CMD_EESCHEMA_DO, PDF_SCH_PRINT)
|
||||
from .optionable import BaseOptions
|
||||
from kiplot.macros import macros, document, output_class # noqa: F401
|
||||
from . import log
|
||||
|
||||
logger = log.get_logger(__name__)
|
||||
|
||||
|
||||
@output_class
|
||||
class PDF_Sch_Print(BaseOutput): # noqa: F821
|
||||
""" PDF Schematic Print (Portable Document Format)
|
||||
Exports the PCB to the most common exhange format. Suitable for printing.
|
||||
This is the main format to document your schematic.
|
||||
This output is what you get from the 'File/Print' menu in eeschema. """
|
||||
def __init__(self, name, type, description):
|
||||
super().__init__(name, type, description)
|
||||
self._sch_related = True
|
||||
# Options
|
||||
class PDF_Sch_PrintOptions(BaseOptions):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.output = ''
|
||||
""" filename for the output PDF (the name of the schematic if empty) """ # pragma: no cover
|
||||
|
|
@ -39,3 +33,17 @@ class PDF_Sch_Print(BaseOutput): # noqa: F821
|
|||
new = os.path.abspath(os.path.join(output_dir, self.output))
|
||||
logger.debug('Moving '+cur+' -> '+new)
|
||||
os.rename(cur, new)
|
||||
|
||||
|
||||
@output_class
|
||||
class PDF_Sch_Print(BaseOutput): # noqa: F821
|
||||
""" PDF Schematic Print (Portable Document Format)
|
||||
Exports the PCB to the most common exhange format. Suitable for printing.
|
||||
This is the main format to document your schematic.
|
||||
This output is what you get from the 'File/Print' menu in eeschema. """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.options = PDF_Sch_PrintOptions
|
||||
""" [dict] Options for the `pdf_sch_print` output """ # pragma: no cover
|
||||
self._sch_related = True
|
||||
|
|
|
|||
|
|
@ -2,17 +2,13 @@ import os
|
|||
import operator
|
||||
from datetime import datetime
|
||||
from pcbnew import (IU_PER_MM, IU_PER_MILS)
|
||||
from .optionable import BaseOptions
|
||||
from kiplot.macros import macros, document, output_class # noqa: F401
|
||||
|
||||
|
||||
@output_class
|
||||
class Position(BaseOutput): # noqa: F821
|
||||
""" Pick & place
|
||||
Generates the file with position information for the PCB components, used by the pick and place machine.
|
||||
This output is what you get from the 'File/Fabrication output/Footprint poistion (.pos) file' menu in pcbnew. """
|
||||
def __init__(self, name, type, description):
|
||||
super().__init__(name, type, description)
|
||||
# Options
|
||||
class PositionOptions(BaseOptions):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.format = 'ASCII'
|
||||
""" [ASCII,CSV] format for the position file """
|
||||
|
|
@ -153,3 +149,15 @@ class Position(BaseOutput): # noqa: F821
|
|||
self._do_position_plot_ascii(board, output_dir, columns, modules, maxlengths)
|
||||
else: # if self.format == 'CSV':
|
||||
self._do_position_plot_csv(board, output_dir, columns, modules)
|
||||
|
||||
|
||||
@output_class
|
||||
class Position(BaseOutput): # noqa: F821
|
||||
""" Pick & place
|
||||
Generates the file with position information for the PCB components, used by the pick and place machine.
|
||||
This output is what you get from the 'File/Fabrication output/Footprint poistion (.pos) file' menu in pcbnew. """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.options = PositionOptions
|
||||
""" [dict] Options for the `position` output """ # pragma: no cover
|
||||
|
|
|
|||
|
|
@ -5,15 +5,9 @@ from kiplot.drill_marks import DrillMarks
|
|||
from kiplot.macros import macros, document, output_class # noqa: F401
|
||||
|
||||
|
||||
@output_class
|
||||
class PS(AnyLayer, DrillMarks):
|
||||
""" PS (Postscript)
|
||||
Exports the PCB to a format suitable for printing.
|
||||
This output is what you get from the File/Plot menu in pcbnew. """
|
||||
def __init__(self, name, type, description):
|
||||
super().__init__(name, type, description)
|
||||
self._plot_format = PLOT_FORMAT_POST
|
||||
# Options
|
||||
class PSOptions(DrillMarks):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.line_width = 0.15
|
||||
""" [0.02,2] for objects without width [mm] """
|
||||
|
|
@ -34,14 +28,10 @@ class PS(AnyLayer, DrillMarks):
|
|||
Only used to plot pads and tracks """
|
||||
self.a4_output = True
|
||||
""" force A4 paper size """ # pragma: no cover
|
||||
|
||||
def config(self, outdir, options, layers):
|
||||
AnyLayer.config(self, outdir, options, layers)
|
||||
DrillMarks.config(self)
|
||||
self._plot_format = PLOT_FORMAT_POST
|
||||
|
||||
def _configure_plot_ctrl(self, po, output_dir):
|
||||
AnyLayer._configure_plot_ctrl(self, po, output_dir)
|
||||
DrillMarks._configure_plot_ctrl(self, po, output_dir)
|
||||
super()._configure_plot_ctrl(po, output_dir)
|
||||
po.SetWidthAdjust(self.width_adjust)
|
||||
po.SetFineScaleAdjustX(self.scale_adjust_x)
|
||||
po.SetFineScaleAdjustX(self.scale_adjust_y)
|
||||
|
|
@ -59,8 +49,7 @@ class PS(AnyLayer, DrillMarks):
|
|||
po.SetScale(self.scaling)
|
||||
|
||||
def read_vals_from_po(self, po):
|
||||
AnyLayer.read_vals_from_po(self, po)
|
||||
DrillMarks.read_vals_from_po(self, po)
|
||||
super().read_vals_from_po(po)
|
||||
self.width_adjust = po.GetWidthAdjust()
|
||||
self.scale_adjust_x = po.GetFineScaleAdjustX()
|
||||
self.scale_adjust_y = po.GetFineScaleAdjustX()
|
||||
|
|
@ -74,3 +63,15 @@ class PS(AnyLayer, DrillMarks):
|
|||
self.scaling = AUTO_SCALE
|
||||
else:
|
||||
self.scaling = po.GetScale()
|
||||
|
||||
|
||||
@output_class
|
||||
class PS(AnyLayer):
|
||||
""" PS (Postscript)
|
||||
Exports the PCB to a format suitable for printing.
|
||||
This output is what you get from the File/Plot menu in pcbnew. """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.options = PSOptions
|
||||
""" [dict] Options for the `ps` output """ # pragma: no cover
|
||||
|
|
|
|||
|
|
@ -4,21 +4,16 @@ from subprocess import (check_output, STDOUT, CalledProcessError)
|
|||
from .error import KiPlotConfigurationError
|
||||
from .misc import (KICAD2STEP, KICAD2STEP_ERR)
|
||||
from .gs import (GS)
|
||||
from .optionable import BaseOptions
|
||||
from kiplot.macros import macros, document, output_class # noqa: F401
|
||||
from . import log
|
||||
|
||||
logger = log.get_logger(__name__)
|
||||
|
||||
|
||||
@output_class
|
||||
class STEP(BaseOutput): # noqa: F821
|
||||
""" STEP (ISO 10303-21 Clear Text Encoding of the Exchange Structure)
|
||||
Exports the PCB as a 3D model.
|
||||
This is the most common 3D format for exchange purposes.
|
||||
This output is what you get from the 'File/Export/STEP' menu in pcbnew. """
|
||||
def __init__(self, name, type, description):
|
||||
super().__init__(name, type, description)
|
||||
# Options
|
||||
class STEPOptions(BaseOptions):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.metric_units = True
|
||||
""" use metric units instead of inches. """
|
||||
|
|
@ -81,3 +76,16 @@ class STEP(BaseOutput): # noqa: F821
|
|||
logger.debug('Output from command: '+e.output.decode())
|
||||
exit(KICAD2STEP_ERR)
|
||||
logger.debug('Output from command:\n'+cmd_output.decode())
|
||||
|
||||
|
||||
@output_class
|
||||
class STEP(BaseOutput): # noqa: F821
|
||||
""" STEP (ISO 10303-21 Clear Text Encoding of the Exchange Structure)
|
||||
Exports the PCB as a 3D model.
|
||||
This is the most common 3D format for exchange purposes.
|
||||
This output is what you get from the 'File/Export/STEP' menu in pcbnew. """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.options = STEPOptions
|
||||
""" [dict] Options for the `step` output """ # pragma: no cover
|
||||
|
|
|
|||
|
|
@ -4,16 +4,9 @@ from kiplot.drill_marks import DrillMarks
|
|||
from kiplot.macros import macros, document, output_class # noqa: F401
|
||||
|
||||
|
||||
@output_class
|
||||
class SVG(AnyLayer, DrillMarks):
|
||||
""" SVG (Scalable Vector Graphics)
|
||||
Exports the PCB to a format suitable for 2D graphics software.
|
||||
Unlike bitmaps SVG drawings can be scaled without losing resolution.
|
||||
This output is what you get from the File/Plot menu in pcbnew. """
|
||||
def __init__(self, name, type, description):
|
||||
super().__init__(name, type, description)
|
||||
self._plot_format = PLOT_FORMAT_SVG
|
||||
# Options
|
||||
class SVGOptions(DrillMarks):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.line_width = 0.25
|
||||
""" [0.02,2] for objects without width [mm] """
|
||||
|
|
@ -21,21 +14,29 @@ class SVG(AnyLayer, DrillMarks):
|
|||
""" plot mirrored """
|
||||
self.negative_plot = False
|
||||
""" invert black and white """ # pragma: no cover
|
||||
|
||||
def config(self, outdir, options, layers):
|
||||
AnyLayer.config(self, outdir, options, layers)
|
||||
DrillMarks.config(self)
|
||||
self._plot_format = PLOT_FORMAT_SVG
|
||||
|
||||
def _configure_plot_ctrl(self, po, output_dir):
|
||||
AnyLayer._configure_plot_ctrl(self, po, output_dir)
|
||||
DrillMarks._configure_plot_ctrl(self, po, output_dir)
|
||||
super()._configure_plot_ctrl(po, output_dir)
|
||||
po.SetMirror(self.mirror_plot)
|
||||
po.SetLineWidth(FromMM(self.line_width))
|
||||
po.SetNegative(self.negative_plot)
|
||||
|
||||
def read_vals_from_po(self, po):
|
||||
AnyLayer.read_vals_from_po(self, po)
|
||||
DrillMarks.read_vals_from_po(self, po)
|
||||
super().read_vals_from_po(po)
|
||||
self.line_width = ToMM(po.GetLineWidth())
|
||||
self.negative_plot = po.GetNegative()
|
||||
self.mirror_plot = po.GetMirror()
|
||||
|
||||
|
||||
@output_class
|
||||
class SVG(AnyLayer):
|
||||
""" SVG (Scalable Vector Graphics)
|
||||
Exports the PCB to a format suitable for 2D graphics software.
|
||||
Unlike bitmaps SVG drawings can be scaled without losing resolution.
|
||||
This output is what you get from the File/Plot menu in pcbnew. """
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.options = SVGOptions
|
||||
""" [dict] Options for the `svg` output """ # pragma: no cover
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
from .optionable import Optionable
|
||||
|
||||
|
||||
class RegOutput(Optionable):
|
||||
""" This class adds the mechanism to register outputs """
|
||||
_registered = {}
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
@staticmethod
|
||||
def register(name, aclass):
|
||||
RegOutput._registered[name] = aclass
|
||||
|
||||
@staticmethod
|
||||
def is_registered(name):
|
||||
return name in RegOutput._registered
|
||||
|
||||
@staticmethod
|
||||
def get_class_for(name):
|
||||
return RegOutput._registered[name]
|
||||
|
||||
@staticmethod
|
||||
def get_registered():
|
||||
return RegOutput._registered
|
||||
|
|
@ -79,7 +79,7 @@
|
|||
(useauxorigin false)
|
||||
(hpglpennumber 1)
|
||||
(hpglpenspeed 20)
|
||||
(hpglpendiameter 15.000000)
|
||||
(hpglpendiameter 35.000000)
|
||||
(psnegative false)
|
||||
(psa4output false)
|
||||
(plotreference true)
|
||||
|
|
|
|||
|
|
@ -343,7 +343,6 @@ def test_example_1():
|
|||
ctx = context.TestContext('Example1', '3Rs', 'pre_and_position', '')
|
||||
ctx.run(extra=['--example'], no_verbose=True, no_yaml_file=True, no_board_file=True)
|
||||
assert ctx.expect_out_file(EXAMPLE_CFG)
|
||||
os.remove(ctx.get_out_path(EXAMPLE_CFG))
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
|
|
@ -351,8 +350,7 @@ def test_example_2():
|
|||
ctx = context.TestContext('Example2', 'good-project', 'pre_and_position', '')
|
||||
ctx.run(extra=['--example'], no_verbose=True, no_yaml_file=True)
|
||||
assert ctx.expect_out_file(EXAMPLE_CFG)
|
||||
ctx.search_in_file(EXAMPLE_CFG, ['GND.Cu'])
|
||||
os.remove(ctx.get_out_path(EXAMPLE_CFG))
|
||||
ctx.search_in_file(EXAMPLE_CFG, ['layers: all'])
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
|
|
@ -361,15 +359,21 @@ def test_example_3():
|
|||
ctx.run(extra=['--example'], no_verbose=True, no_yaml_file=True)
|
||||
assert ctx.expect_out_file(EXAMPLE_CFG)
|
||||
ctx.run(WONT_OVERWRITE, extra=['--example'], no_verbose=True, no_yaml_file=True)
|
||||
os.remove(ctx.get_out_path(EXAMPLE_CFG))
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_example_4():
|
||||
ctx = context.TestContext('Example4', 'good-project', 'pre_and_position', '')
|
||||
ctx.run(extra=['--example', '-P'], no_verbose=True, no_yaml_file=True)
|
||||
assert ctx.expect_out_file(EXAMPLE_CFG)
|
||||
ctx.search_in_file(EXAMPLE_CFG, ['GND.Cu', 'pen_width: 35.0'])
|
||||
ctx.search_not_in_file(EXAMPLE_CFG, ['F.Adhes'])
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_example_5():
|
||||
ctx = context.TestContext('Example5', 'good-project', 'pre_and_position', '')
|
||||
ctx.run(extra=['--example', '-p'], no_verbose=True, no_yaml_file=True)
|
||||
assert ctx.expect_out_file(EXAMPLE_CFG)
|
||||
ctx.search_in_file(EXAMPLE_CFG, ['GND.Cu'])
|
||||
ctx.search_not_in_file(EXAMPLE_CFG, ['F.Adhes'])
|
||||
os.remove(ctx.get_out_path(EXAMPLE_CFG))
|
||||
ctx.search_in_file(EXAMPLE_CFG, ['layers: selected', 'pen_width: 35.0'])
|
||||
ctx.clean_up()
|
||||
|
|
|
|||
|
|
@ -30,3 +30,200 @@ def test_svg():
|
|||
ctx.dont_expect_out_file(ctx.get_gerber_job_filename())
|
||||
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_svg_all():
|
||||
prj = 'simple_2layer'
|
||||
ctx = context.TestContext('SVGAll', prj, 'svg_all', PS_DIR)
|
||||
ctx.run()
|
||||
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('B_Adhes', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('B_CrtYd', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('B_Cu', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('B_Fab', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('B_Mask', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('B_Paste', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('B_SilkS', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Cmts_User', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Dwgs_User', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Eco1_User', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Eco2_User', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Edge_Cuts', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('F_Adhes', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('F_CrtYd', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('F_Cu', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('F_Fab', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('F_Mask', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('F_Paste', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('F_SilkS', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Margin', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_job_filename())
|
||||
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_svg_selected():
|
||||
prj = 'simple_2layer'
|
||||
ctx = context.TestContext('SVGSelected', prj, 'svg_selected', PS_DIR)
|
||||
ctx.run()
|
||||
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_Adhes', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_CrtYd', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('B_Cu', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_Fab', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('B_Mask', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('B_Paste', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('B_SilkS', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('Cmts_User', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('Dwgs_User', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('Eco1_User', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('Eco2_User', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Edge_Cuts', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_Adhes', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_CrtYd', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('F_Cu', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_Fab', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('F_Mask', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('F_Paste', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('F_SilkS', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('Margin', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_job_filename())
|
||||
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_svg_copper_and_user():
|
||||
prj = 'good-project'
|
||||
ctx = context.TestContext('SVGCopperUser', prj, 'svg_copper_and_user', PS_DIR)
|
||||
ctx.run()
|
||||
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('B_Cu', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Cmts_User', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Dwgs_User', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Eco1_User', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Eco2_User', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Edge_Cuts', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('F_Cu', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('GND_Cu', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Margin', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Power_Cu', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Signal1_Cu', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Signal2_Cu', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_Adhes', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_CrtYd', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_Fab', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_Mask', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_Paste', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_SilkS', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_Adhes', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_CrtYd', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_Fab', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_Mask', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_Paste', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_SilkS', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_job_filename())
|
||||
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_svg_copper_and_draw():
|
||||
prj = 'good-project'
|
||||
ctx = context.TestContext('SVGCopperDraw', prj, 'svg_copper_and_draw', PS_DIR)
|
||||
ctx.run()
|
||||
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('B_Cu', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Dwgs_User', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('F_Cu', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('GND_Cu', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Power_Cu', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Signal1_Cu', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Signal2_Cu', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('Margin', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('Eco1_User', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('Eco2_User', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('Edge_Cuts', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('Cmts_User', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_Adhes', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_CrtYd', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_Fab', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_Mask', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_Paste', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_SilkS', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_Adhes', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_CrtYd', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_Fab', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_Mask', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_Paste', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_SilkS', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_job_filename())
|
||||
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_svg_copper_and_cmt():
|
||||
prj = 'good-project'
|
||||
ctx = context.TestContext('SVGCopperCmt', prj, 'svg_copper_and_cmt', PS_DIR)
|
||||
ctx.run()
|
||||
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('B_Cu', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('F_Cu', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('GND_Cu', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Power_Cu', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Signal1_Cu', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Signal2_Cu', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Cmts_User', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('Dwgs_User', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('Margin', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('Eco1_User', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('Eco2_User', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('Edge_Cuts', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_Adhes', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_CrtYd', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_Fab', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_Mask', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_Paste', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_SilkS', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_Adhes', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_CrtYd', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_Fab', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_Mask', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_Paste', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_SilkS', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_job_filename())
|
||||
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_svg_anchor():
|
||||
prj = 'good-project'
|
||||
ctx = context.TestContext('SVGCopperCmt', prj, 'svg_anchor', PS_DIR)
|
||||
ctx.run(extra=['SVG'])
|
||||
|
||||
assert ctx.search_err(r"- 'SVG files' \(SVG\) \[svg\]")
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('B_Cu', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('F_Cu', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('GND_Cu', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Power_Cu', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Signal1_Cu', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Signal2_Cu', '.svg'))
|
||||
ctx.expect_out_file(ctx.get_gerber_filename('Cmts_User', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('Dwgs_User', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('Margin', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('Eco1_User', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('Eco2_User', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('Edge_Cuts', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_Adhes', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_CrtYd', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_Fab', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_Mask', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_Paste', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('B_SilkS', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_Adhes', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_CrtYd', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_Fab', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_Mask', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_Paste', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_filename('F_SilkS', '.svg'))
|
||||
ctx.dont_expect_out_file(ctx.get_gerber_job_filename())
|
||||
|
||||
ctx.clean_up()
|
||||
|
|
|
|||
|
|
@ -150,8 +150,8 @@ def test_drill_report_wrong_type_3():
|
|||
|
||||
def test_wrong_layer_1():
|
||||
ctx = context.TestContext('ErrorWrongLayer1', '3Rs', 'error_wrong_layer_1', None)
|
||||
ctx.run(PLOT_ERROR)
|
||||
assert ctx.search_err("Unknown layer name: F.Bogus")
|
||||
ctx.run(EXIT_BAD_CONFIG)
|
||||
assert ctx.search_err("Unknown layer name: .?F.Bogus.?")
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
|
|
@ -164,29 +164,45 @@ def test_wrong_layer_2():
|
|||
|
||||
def test_wrong_layer_3():
|
||||
ctx = context.TestContext('ErrorWrongLayer3', '3Rs', 'error_wrong_layer_3', None)
|
||||
ctx.run(PLOT_ERROR)
|
||||
assert ctx.search_err("Malformed inner layer name: Inner_1,")
|
||||
ctx.run(EXIT_BAD_CONFIG)
|
||||
assert ctx.search_err("Malformed inner layer name: .?Inner_1.?,")
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_wrong_layer_4():
|
||||
ctx = context.TestContext('ErrorWrongLayer4', '3Rs', 'error_wrong_layer_4', None)
|
||||
ctx.run(EXIT_BAD_CONFIG)
|
||||
assert ctx.search_err(".?layers.? must be a list")
|
||||
assert ctx.search_err(".?layers.? must be any of")
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_wrong_layer_5():
|
||||
ctx = context.TestContext('ErrorWrongLayer5', '3Rs', 'error_wrong_layer_5', None)
|
||||
ctx.run(EXIT_BAD_CONFIG)
|
||||
assert ctx.search_err("Unknown .?bogus.? attribute ")
|
||||
assert ctx.search_err("Unknown option .?bogus.?")
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_wrong_layer_6():
|
||||
ctx = context.TestContext('ErrorWrongLayer6', '3Rs', 'error_wrong_layer_6', None)
|
||||
ctx.run(EXIT_BAD_CONFIG)
|
||||
assert ctx.search_err("Missing .?layer.? attribute")
|
||||
assert ctx.search_err("Missing or empty .?layer.? attribute")
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_wrong_layer_7():
|
||||
""" List of numbers """
|
||||
ctx = context.TestContext('ErrorWrongLayer7', '3Rs', 'error_wrong_layer_7', None)
|
||||
ctx.run(EXIT_BAD_CONFIG)
|
||||
assert ctx.search_err(".?layers.? must be any of")
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_wrong_layer_8():
|
||||
""" List of strings, but number in middle """
|
||||
ctx = context.TestContext('ErrorWrongLayer8', '3Rs', 'error_wrong_layer_8', None)
|
||||
ctx.run(EXIT_BAD_CONFIG)
|
||||
assert ctx.search_err(".?4.? must be any of")
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
|
|
@ -207,7 +223,14 @@ def test_no_type():
|
|||
def test_out_unknown_attr():
|
||||
ctx = context.TestContext('ErrorUnkOutAttr', '3Rs', 'error_unk_attr', None)
|
||||
ctx.run(EXIT_BAD_CONFIG)
|
||||
assert ctx.search_err("Unknown key .?types.?")
|
||||
assert ctx.search_err("Unknown option .?directory.?")
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
def test_out_needs_type():
|
||||
ctx = context.TestContext('ErrorNeedsType', '3Rs', 'error_needs_type', None)
|
||||
ctx.run(EXIT_BAD_CONFIG)
|
||||
assert ctx.search_err("needs a type")
|
||||
ctx.clean_up()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
kiplot:
|
||||
version: 1
|
||||
|
||||
outputs:
|
||||
- name: PDF
|
||||
comment: "PDF files"
|
||||
types: pdf
|
||||
dir: PDF
|
||||
options:
|
||||
exclude_edge_layer: false
|
||||
exclude_pads_from_silkscreen: false
|
||||
use_aux_axis_as_origin: false
|
||||
plot_sheet_reference: false
|
||||
plot_footprint_refs: true
|
||||
plot_footprint_values: true
|
||||
force_plot_invisible_refs_vals: false
|
||||
tent_vias: true
|
||||
|
||||
# PDF options
|
||||
drill_marks: small
|
||||
mirror_plot: false
|
||||
negative_plot: false
|
||||
line_width: 0.01
|
||||
layers:
|
||||
- layer: F.Cu
|
||||
suffix: F_Cu
|
||||
- layer: F.Bogus
|
||||
suffix: B_Silks
|
||||
|
|
@ -4,8 +4,8 @@ kiplot:
|
|||
outputs:
|
||||
- name: PDF
|
||||
comment: "PDF files"
|
||||
types: pdf
|
||||
dir: PDF
|
||||
type: pdf
|
||||
directory: PDF
|
||||
options:
|
||||
exclude_edge_layer: false
|
||||
exclude_pads_from_silkscreen: false
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
kiplot:
|
||||
version: 1
|
||||
|
||||
outputs:
|
||||
- name: PDF
|
||||
comment: "PDF files"
|
||||
type: pdf
|
||||
dir: PDF
|
||||
options:
|
||||
exclude_edge_layer: false
|
||||
exclude_pads_from_silkscreen: false
|
||||
use_aux_axis_as_origin: false
|
||||
plot_sheet_reference: false
|
||||
plot_footprint_refs: true
|
||||
plot_footprint_values: true
|
||||
force_plot_invisible_refs_vals: false
|
||||
tent_vias: true
|
||||
|
||||
# PDF options
|
||||
drill_marks: small
|
||||
mirror_plot: false
|
||||
negative_plot: false
|
||||
line_width: 0.01
|
||||
layers:
|
||||
- 1
|
||||
- 2
|
||||
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
kiplot:
|
||||
version: 1
|
||||
|
||||
outputs:
|
||||
- name: PDF
|
||||
comment: "PDF files"
|
||||
type: pdf
|
||||
dir: PDF
|
||||
options:
|
||||
exclude_edge_layer: false
|
||||
exclude_pads_from_silkscreen: false
|
||||
use_aux_axis_as_origin: false
|
||||
plot_sheet_reference: false
|
||||
plot_footprint_refs: true
|
||||
plot_footprint_values: true
|
||||
force_plot_invisible_refs_vals: false
|
||||
tent_vias: true
|
||||
|
||||
# PDF options
|
||||
drill_marks: small
|
||||
mirror_plot: false
|
||||
negative_plot: false
|
||||
line_width: 0.01
|
||||
layers:
|
||||
- all
|
||||
- 4
|
||||
- user
|
||||
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
kiplot:
|
||||
version: 1
|
||||
|
||||
outputs:
|
||||
- name: SVG
|
||||
comment: "SVG files"
|
||||
type: svg
|
||||
dir: SVG
|
||||
options:
|
||||
exclude_edge_layer: false
|
||||
exclude_pads_from_silkscreen: false
|
||||
use_aux_axis_as_origin: false
|
||||
plot_sheet_reference: false
|
||||
plot_footprint_refs: true
|
||||
plot_footprint_values: true
|
||||
force_plot_invisible_refs_vals: false
|
||||
tent_vias: true
|
||||
|
||||
# SVG options
|
||||
line_width: 0.25
|
||||
drill_marks: full
|
||||
mirror_plot: true
|
||||
negative_plot: true
|
||||
layers: all
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
kiplot:
|
||||
version: 1
|
||||
|
||||
outputs:
|
||||
- name: postscript
|
||||
comment: "Postscript files"
|
||||
type: ps
|
||||
dir: PS
|
||||
layers: &copper_and_cmts
|
||||
- copper
|
||||
- 'Cmts.User'
|
||||
|
||||
- name: SVG
|
||||
comment: "SVG files"
|
||||
type: svg
|
||||
dir: SVG
|
||||
options:
|
||||
exclude_edge_layer: false
|
||||
exclude_pads_from_silkscreen: false
|
||||
use_aux_axis_as_origin: false
|
||||
plot_sheet_reference: false
|
||||
plot_footprint_refs: true
|
||||
plot_footprint_values: true
|
||||
force_plot_invisible_refs_vals: false
|
||||
tent_vias: true
|
||||
|
||||
# SVG options
|
||||
line_width: 0.25
|
||||
drill_marks: full
|
||||
mirror_plot: true
|
||||
negative_plot: true
|
||||
layers: *copper_and_cmts
|
||||
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
kiplot:
|
||||
version: 1
|
||||
|
||||
outputs:
|
||||
- name: SVG
|
||||
comment: "SVG files"
|
||||
type: svg
|
||||
dir: SVG
|
||||
options:
|
||||
exclude_edge_layer: false
|
||||
exclude_pads_from_silkscreen: false
|
||||
use_aux_axis_as_origin: false
|
||||
plot_sheet_reference: false
|
||||
plot_footprint_refs: true
|
||||
plot_footprint_values: true
|
||||
force_plot_invisible_refs_vals: false
|
||||
tent_vias: true
|
||||
|
||||
# SVG options
|
||||
line_width: 0.25
|
||||
drill_marks: full
|
||||
mirror_plot: true
|
||||
negative_plot: true
|
||||
layers:
|
||||
- copper
|
||||
- 'Cmts.User'
|
||||
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
kiplot:
|
||||
version: 1
|
||||
|
||||
outputs:
|
||||
- name: SVG
|
||||
comment: "SVG files"
|
||||
type: svg
|
||||
dir: SVG
|
||||
options:
|
||||
exclude_edge_layer: false
|
||||
exclude_pads_from_silkscreen: false
|
||||
use_aux_axis_as_origin: false
|
||||
plot_sheet_reference: false
|
||||
plot_footprint_refs: true
|
||||
plot_footprint_values: true
|
||||
force_plot_invisible_refs_vals: false
|
||||
tent_vias: true
|
||||
|
||||
# SVG options
|
||||
line_width: 0.25
|
||||
drill_marks: full
|
||||
mirror_plot: true
|
||||
negative_plot: true
|
||||
layers:
|
||||
- copper
|
||||
- layer: 'Dwgs.User'
|
||||
suffix: 'Dwgs_User'
|
||||
description: 'User drawings'
|
||||
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
kiplot:
|
||||
version: 1
|
||||
|
||||
outputs:
|
||||
- name: SVG
|
||||
comment: "SVG files"
|
||||
type: svg
|
||||
dir: SVG
|
||||
options:
|
||||
exclude_edge_layer: false
|
||||
exclude_pads_from_silkscreen: false
|
||||
use_aux_axis_as_origin: false
|
||||
plot_sheet_reference: false
|
||||
plot_footprint_refs: true
|
||||
plot_footprint_values: true
|
||||
force_plot_invisible_refs_vals: false
|
||||
tent_vias: true
|
||||
|
||||
# SVG options
|
||||
line_width: 0.25
|
||||
drill_marks: full
|
||||
mirror_plot: true
|
||||
negative_plot: true
|
||||
layers:
|
||||
- copper
|
||||
- user
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
kiplot:
|
||||
version: 1
|
||||
|
||||
outputs:
|
||||
- name: SVG
|
||||
comment: "SVG files"
|
||||
type: svg
|
||||
dir: SVG
|
||||
options:
|
||||
exclude_edge_layer: false
|
||||
exclude_pads_from_silkscreen: false
|
||||
use_aux_axis_as_origin: false
|
||||
plot_sheet_reference: false
|
||||
plot_footprint_refs: true
|
||||
plot_footprint_values: true
|
||||
force_plot_invisible_refs_vals: false
|
||||
tent_vias: true
|
||||
|
||||
# SVG options
|
||||
line_width: 0.25
|
||||
drill_marks: full
|
||||
mirror_plot: true
|
||||
negative_plot: true
|
||||
layers: selected
|
||||
Loading…
Reference in New Issue