Added --example/-x option to generate a configuration example.
The example contains all the available preflights and outputs. If the user specifies a PCB the names of the layers are from the provided PCB
This commit is contained in:
parent
f579d648bf
commit
4ae54f3ded
|
|
@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
### Added
|
||||
- -e/--schematic option to specify any schematic (not just derived from the PCB
|
||||
name.
|
||||
- -x/--example option to generate a complete configuration example.
|
||||
- Help for the supported outputs (--help-list-outputs, --help-outputs and
|
||||
--help-output)
|
||||
- Help for the supported preflights (--help-preflights)
|
||||
|
|
|
|||
19
README.md
19
README.md
|
|
@ -427,6 +427,21 @@ Most options are the same you'll find in the KiCad dialogs.
|
|||
|
||||
## Using KiPlot
|
||||
|
||||
If you need a template for the configuration file try:
|
||||
|
||||
```
|
||||
kiplot --example
|
||||
```
|
||||
|
||||
This will generate a file named `example.kiplot.yaml` containing all the available options and comments about them.
|
||||
You can use it to create your own configuration file.
|
||||
|
||||
If you want to use the layers of a particular PCB in the example use:
|
||||
|
||||
```
|
||||
kiplot -b PCB_FILE --example
|
||||
```
|
||||
|
||||
If the current directory contains only one PCB file and only one configuration file (named *.kiplot.yaml)
|
||||
you can just call `kiplot`. No arguments needed. The tool will figure out which files to use.
|
||||
|
||||
|
|
@ -495,6 +510,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] --example
|
||||
kiplot --help-list-outputs
|
||||
kiplot --help-output=HELP_OUTPUT
|
||||
kiplot --help-outputs
|
||||
|
|
@ -520,7 +536,8 @@ Options:
|
|||
-q, --quiet Remove information logs
|
||||
-s PRE, --skip-pre PRE Skip preflights, comma separated or `all`
|
||||
-v, --verbose Show debugging information
|
||||
--version, -V Show program's version number and exit
|
||||
-V, --version Show program's version number and exit
|
||||
-x, --example Create an example configuration file.
|
||||
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
#!/usr/bin/make
|
||||
|
||||
all: ../README.md
|
||||
all: ../README.md samples/generic_plot.kiplot.yaml
|
||||
|
||||
../README.md: README.in replace_tags.pl ../kiplot/out_*.py ../kiplot/pre_*.py ../kiplot/__main__.py
|
||||
cat README.in | perl replace_tags.pl > ../README.md
|
||||
|
||||
samples/generic_plot.kiplot.yaml: ../kiplot/out_*.py ../kiplot/pre_*.py ../kiplot/config_reader.py
|
||||
../src/kiplot --example
|
||||
mv example.kiplot.yaml $@
|
||||
|
|
|
|||
|
|
@ -196,6 +196,21 @@ Most options are the same you'll find in the KiCad dialogs.
|
|||
|
||||
## Using KiPlot
|
||||
|
||||
If you need a template for the configuration file try:
|
||||
|
||||
```
|
||||
kiplot --example
|
||||
```
|
||||
|
||||
This will generate a file named `example.kiplot.yaml` containing all the available options and comments about them.
|
||||
You can use it to create your own configuration file.
|
||||
|
||||
If you want to use the layers of a particular PCB in the example use:
|
||||
|
||||
```
|
||||
kiplot -b PCB_FILE --example
|
||||
```
|
||||
|
||||
If the current directory contains only one PCB file and only one configuration file (named *.kiplot.yaml)
|
||||
you can just call `kiplot`. No arguments needed. The tool will figure out which files to use.
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -5,6 +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] --example
|
||||
kiplot --help-list-outputs
|
||||
kiplot --help-output=HELP_OUTPUT
|
||||
kiplot --help-outputs
|
||||
|
|
@ -30,7 +31,8 @@ Options:
|
|||
-q, --quiet Remove information logs
|
||||
-s PRE, --skip-pre PRE Skip preflights, comma separated or `all`
|
||||
-v, --verbose Show debugging information
|
||||
--version, -V Show program's version number and exit
|
||||
-V, --version Show program's version number and exit
|
||||
-x, --example Create an example configuration file.
|
||||
|
||||
"""
|
||||
__author__ = 'John Beard, Salvador E. Tropea'
|
||||
|
|
@ -52,7 +54,7 @@ log.set_domain('kiplot')
|
|||
from .gs import (GS)
|
||||
from .kiplot import (generate_outputs)
|
||||
from .pre_base import (BasePreFlight)
|
||||
from .config_reader import (CfgYamlReader, print_outputs_help, print_output_help, print_preflights_help)
|
||||
from .config_reader import (CfgYamlReader, print_outputs_help, print_output_help, print_preflights_help, create_example)
|
||||
from .misc import (NO_PCB_FILE, NO_SCH_FILE, EXIT_BAD_ARGS)
|
||||
from .docopt import docopt
|
||||
from .__version__ import __version__
|
||||
|
|
@ -122,6 +124,12 @@ def solve_config(a_plot_config):
|
|||
return plot_config
|
||||
|
||||
|
||||
def check_board_file(board_file):
|
||||
if board_file and not os.path.isfile(board_file):
|
||||
logger.error("Board file not found: "+board_file)
|
||||
sys.exit(NO_PCB_FILE)
|
||||
|
||||
|
||||
def solve_board_file(schematic, a_board_file):
|
||||
board_file = a_board_file
|
||||
if not board_file and schematic:
|
||||
|
|
@ -137,9 +145,7 @@ def solve_board_file(schematic, a_board_file):
|
|||
board_file = board_files[0]
|
||||
logger.warning('More than one PCB file found in current directory.\n'
|
||||
' Using '+board_file+' if you want to use another use -b option.')
|
||||
if board_file and not os.path.isfile(board_file):
|
||||
logger.error("Board file not found: "+board_file)
|
||||
sys.exit(NO_PCB_FILE)
|
||||
check_board_file(board_file)
|
||||
return board_file
|
||||
|
||||
|
||||
|
|
@ -164,6 +170,10 @@ def main():
|
|||
if args.help_preflights:
|
||||
print_preflights_help()
|
||||
sys.exit(0)
|
||||
if args.example:
|
||||
check_board_file(args.board_file)
|
||||
create_example(args.board_file)
|
||||
sys.exit(0)
|
||||
|
||||
# Determine the YAML file
|
||||
plot_config = solve_config(args.plot_config)
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@
|
|||
Class to read KiPlot config files
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
from sys import (exit, maxsize)
|
||||
from collections import OrderedDict
|
||||
|
||||
from .error import (KiPlotConfigurationError)
|
||||
from .kiplot import (Layer)
|
||||
from .misc import (NO_YAML_MODULE, EXIT_BAD_CONFIG, EXIT_BAD_ARGS)
|
||||
from .kiplot import (Layer, load_board)
|
||||
from .misc import (NO_YAML_MODULE, EXIT_BAD_CONFIG, EXIT_BAD_ARGS, EXAMPLE_CFG, WONT_OVERWRITE)
|
||||
from mcpy import activate # noqa: F401
|
||||
# Output classes
|
||||
from .out_base import BaseOutput
|
||||
|
|
@ -43,12 +44,12 @@ try:
|
|||
except ImportError: # pragma: no cover
|
||||
log.init(False, False)
|
||||
logger.error('No yaml module for Python, install python3-yaml')
|
||||
sys.exit(NO_YAML_MODULE)
|
||||
exit(NO_YAML_MODULE)
|
||||
|
||||
|
||||
def config_error(msg):
|
||||
logger.error(msg)
|
||||
sys.exit(EXIT_BAD_CONFIG)
|
||||
exit(EXIT_BAD_CONFIG)
|
||||
|
||||
|
||||
class CfgYamlReader(object):
|
||||
|
|
@ -228,14 +229,14 @@ def trim(docstring):
|
|||
# and split into a list of lines:
|
||||
lines = docstring.expandtabs().splitlines()
|
||||
# Determine minimum indentation (first line doesn't count):
|
||||
indent = sys.maxsize
|
||||
indent = maxsize
|
||||
for line in lines[1:]:
|
||||
stripped = line.lstrip()
|
||||
if stripped:
|
||||
indent = min(indent, len(line) - len(stripped))
|
||||
# Remove indentation (first line is special):
|
||||
trimmed = [lines[0].strip()]
|
||||
if indent < sys.maxsize:
|
||||
if indent < maxsize:
|
||||
for line in lines[1:]:
|
||||
trimmed.append(line[indent:].rstrip())
|
||||
# Strip off trailing and leading blank lines:
|
||||
|
|
@ -287,7 +288,7 @@ def print_outputs_help(details=False):
|
|||
def print_output_help(name):
|
||||
if not BaseOutput.is_registered(name):
|
||||
logger.error('Unknown output type `{}`, try --help-list-outputs'.format(name))
|
||||
sys.exit(EXIT_BAD_ARGS)
|
||||
exit(EXIT_BAD_ARGS)
|
||||
print_one_out_help(True, name, BaseOutput.get_class_for(name))
|
||||
|
||||
|
||||
|
|
@ -300,3 +301,63 @@ def print_preflights_help():
|
|||
if help is None:
|
||||
help = 'Undocumented'
|
||||
print('- {}: {}.'.format(n, help.rstrip()))
|
||||
|
||||
|
||||
def create_example(pcb_file):
|
||||
if os.path.isfile(EXAMPLE_CFG):
|
||||
logger.error(EXAMPLE_CFG+" already exists, won't overwrite")
|
||||
exit(WONT_OVERWRITE)
|
||||
with open(EXAMPLE_CFG, 'w') as f:
|
||||
logger.info('Creating {} example configuration'.format(EXAMPLE_CFG))
|
||||
f.write('kiplot:\n version: 1\n')
|
||||
# Preflights
|
||||
f.write('\npreflight:\n')
|
||||
pres = BasePreFlight.get_registered()
|
||||
for n, o in pres.items():
|
||||
if o.__doc__:
|
||||
f.write(' #'+o.__doc__.rstrip()+'\n')
|
||||
f.write(' {}: {}\n'.format(n, o.get_example()))
|
||||
# Outputs
|
||||
outs = BaseOutput.get_registered()
|
||||
f.write('\noutputs:\n')
|
||||
# List of layers
|
||||
if pcb_file:
|
||||
# We have a PCB to take as reference
|
||||
load_board(pcb_file)
|
||||
layers = Layer.get_pcb_layers()
|
||||
else:
|
||||
# Use the default list of layers
|
||||
layers = Layer.get_default_layers()
|
||||
for n, o in OrderedDict(sorted(outs.items())).items():
|
||||
lines = trim(o.__doc__)
|
||||
if len(lines) == 0:
|
||||
lines = ['Undocumented', 'No description']
|
||||
f.write(' # '+lines[0].rstrip()+':\n')
|
||||
for ln in range(2, len(lines)):
|
||||
f.write(' # '+lines[ln].rstrip()+'\n')
|
||||
f.write(" - name: '{}_example'\n".format(n))
|
||||
f.write(" comment: '{}'\n".format(lines[1]))
|
||||
f.write(" type: '{}'\n".format(n))
|
||||
f.write(" dir: 'Example/{}_dir'\n".format(n))
|
||||
f.write(" options:\n")
|
||||
obj = o('', n, '')
|
||||
for k, v in BaseOutput.get_attrs_gen(obj):
|
||||
help = getattr(obj, '_help_'+k)
|
||||
if help:
|
||||
help_lines = help.split('\n')
|
||||
for hl in help_lines:
|
||||
f.write(' # '+hl.strip()+'\n')
|
||||
val = getattr(obj, k)
|
||||
if isinstance(val, str):
|
||||
val = "'{}'".format(val)
|
||||
elif isinstance(val, bool):
|
||||
val = str(val).lower()
|
||||
f.write(' {}: {}\n'.format(k, val))
|
||||
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))
|
||||
f.write('\n')
|
||||
|
|
|
|||
|
|
@ -53,6 +53,29 @@ class Layer(object):
|
|||
'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 = {}
|
||||
|
||||
|
|
@ -61,13 +84,24 @@ class Layer(object):
|
|||
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 range(pcbnew.PCBNEW_LAYER_ID_START, pcbnew.PCB_LAYER_ID_COUNT):
|
||||
for id in board.GetEnabledLayers().Seq():
|
||||
Layer.pcb_layers[board.GetLayerName(id)] = id
|
||||
|
||||
@staticmethod
|
||||
def get_pcb_layers():
|
||||
layers = []
|
||||
for n, id in Layer.pcb_layers.items():
|
||||
s = n.replace('.', '_')
|
||||
d = Layer.DEFAULT_LAYER_DESC.get(n)
|
||||
layers.append(Layer(n, s, d))
|
||||
return layers
|
||||
|
||||
def get_layer_id_from_name(self, layer_cnt):
|
||||
""" Get the pcbnew layer from the string provided in the config """
|
||||
# Priority
|
||||
|
|
@ -94,6 +128,14 @@ class Layer(object):
|
|||
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)
|
||||
|
||||
|
|
@ -130,10 +172,12 @@ def check_eeschema_do():
|
|||
exit(NO_SCH_FILE)
|
||||
|
||||
|
||||
def load_board():
|
||||
GS.check_pcb()
|
||||
def load_board(pcb_file=None):
|
||||
if not pcb_file:
|
||||
GS.check_pcb()
|
||||
pcb_file = GS.pcb_file
|
||||
try:
|
||||
board = pcbnew.LoadBoard(GS.pcb_file)
|
||||
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
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ NO_YAML_MODULE = 15
|
|||
NO_PCBNEW_MODULE = 16
|
||||
CORRUPTED_PCB = 17
|
||||
KICAD2STEP_ERR = 18
|
||||
WONT_OVERWRITE = 19
|
||||
|
||||
CMD_EESCHEMA_DO = 'eeschema_do'
|
||||
URL_EESCHEMA_DO = 'https://github.com/INTI-CMNB/kicad-automation-scripts'
|
||||
|
|
@ -32,3 +33,4 @@ URL_KIBOM = 'https://github.com/INTI-CMNB/KiBoM'
|
|||
CMD_IBOM = 'generate_interactive_bom.py'
|
||||
URL_IBOM = 'https://github.com/INTI-CMNB/InteractiveHtmlBom'
|
||||
KICAD2STEP = 'kicad2step'
|
||||
EXAMPLE_CFG = 'example.kiplot.yaml'
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ AUTO_SCALE = 0
|
|||
class AnyLayer(BaseOutput):
|
||||
def __init__(self, name, type, description):
|
||||
super(AnyLayer, self).__init__(name, type, description)
|
||||
# We need layers, so we define it
|
||||
self._layers = None
|
||||
# Options
|
||||
with document:
|
||||
self.exclude_edge_layer = True
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ class PDF_Pcb_Print(BaseOutput): # noqa: F821
|
|||
This output is what you get from the 'File/Print' menu in pcbnew. """
|
||||
def __init__(self, name, type, description):
|
||||
super(PDF_Pcb_Print, self).__init__(name, type, description)
|
||||
# We need layers, so we define it
|
||||
self._layers = None
|
||||
# Options
|
||||
with document:
|
||||
self.output_name = ''
|
||||
|
|
|
|||
|
|
@ -84,6 +84,10 @@ class BasePreFlight(object):
|
|||
""" True for preflights that needs the PCB """
|
||||
return self._pcb_related
|
||||
|
||||
def get_example():
|
||||
""" Returns a YAML value for the example config """
|
||||
return 'true'
|
||||
|
||||
def run(self, brd_file): # pragma: no cover
|
||||
logger.error("The run method for the preflight class name `{}` isn't implemented".format(self._name))
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@ class Filters(BasePreFlight): # noqa: F821
|
|||
def __init__(self, name, value):
|
||||
super().__init__(name, value)
|
||||
|
||||
def get_example():
|
||||
""" Returns a YAML value for the example config """
|
||||
return "\n - filter: 'Filter description'\n number: 10\n regex: 'Regular expression to match'"
|
||||
|
||||
def run(self):
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@ class Ignore_Unconnected(BasePreFlight): # noqa: F821
|
|||
raise KiPlotConfigurationError('must be boolean')
|
||||
self._enabled = value
|
||||
|
||||
def get_example():
|
||||
""" Returns a YAML value for the example config """
|
||||
return 'false'
|
||||
|
||||
def run(self, brd_file):
|
||||
pass
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue