KiBot/kiplot/plot_config.py

532 lines
12 KiB
Python

import pcbnew
from . import error
from . import log
logger = log.get_logger(__name__)
class KiPlotConfigurationError(error.KiPlotError):
pass
class TypeOptions(object):
def validate(self):
"""
Return list of invalid settings
"""
return []
class LayerOptions(TypeOptions):
"""
Common options that all layer outputs have
"""
AUTO_SCALE = 0
def __init__(self):
super(LayerOptions, self).__init__()
self.exclude_edge_layer = False
self.exclude_pads_from_silkscreen = False
self.plot_sheet_reference = False
self._supports_line_width = False
self._line_width = 0
self._supports_aux_axis_origin = False
self._use_aux_axis_as_origin = False
# override for scalable formats
self._supports_scaling = False
self._auto_scale = False
self._scaling = 1
self._supports_mirror = False
self._mirror_plot = False
self._supports_negative = False
self._negative_plot = False
self._supports_drill_marks = False
self._drill_marks = pcbnew.PCB_PLOT_PARAMS.NO_DRILL_SHAPE
self._support_sketch_mode = False
self._sketch_mode = False
@property
def line_width(self):
return self._line_width
@line_width.setter
def line_width(self, value):
"""
Set the line width, in mm
"""
if self._supports_line_width:
self._line_width = pcbnew.FromMM(value)
else:
raise KiPlotConfigurationError(
"This output doesn't support setting line width")
@property
def auto_scale(self):
return self._auto_scale
@property
def scaling(self):
return self._scaling
@scaling.setter
def scaling(self, val):
"""
Set scaling, if possible. AUTO_SCALE to set auto scaling
"""
if self._supports_scaling:
if val == self.AUTO_SCALE:
self._scaling = 1
self._auto_scale = True
else:
self._scaling = val
self._auto_scale = False
else:
raise KiPlotConfigurationError(
"This Layer output does not support scaling")
@property
def mirror_plot(self):
return self._mirror_plot
@mirror_plot.setter
def mirror_plot(self, val):
if self._supports_mirror:
self._mirror_plot = val
else:
raise KiPlotConfigurationError(
"This Layer output does not support mirror plotting")
@property
def negative_plot(self):
return self._negative_plot
@negative_plot.setter
def negative_plot(self, val):
if self._supports_mirror:
self._negative_plot = val
else:
raise KiPlotConfigurationError(
"This Layer output does not support negative plotting")
@property
def drill_marks(self):
return self._drill_marks
@drill_marks.setter
def drill_marks(self, val):
if self._supports_drill_marks:
try:
drill_mark = {
'none': pcbnew.PCB_PLOT_PARAMS.NO_DRILL_SHAPE,
'small': pcbnew.PCB_PLOT_PARAMS.SMALL_DRILL_SHAPE,
'full': pcbnew.PCB_PLOT_PARAMS.FULL_DRILL_SHAPE,
}[val]
except KeyError:
raise KiPlotConfigurationError(
"Unknown drill mark type: {}".format(val))
self._drill_marks = drill_mark
else:
raise KiPlotConfigurationError(
"This Layer output does not support drill marks")
@property
def use_aux_axis_as_origin(self):
return self._use_aux_axis_as_origin
@use_aux_axis_as_origin.setter
def use_aux_axis_as_origin(self, val):
if self._supports_aux_axis_origin:
self._use_aux_axis_as_origin = val
else:
raise KiPlotConfigurationError(
"This Layer output does not support using the auxiliary"
" axis as the origin")
@property
def sketch_mode(self):
return self._sketch_mode
@sketch_mode.setter
def sketch_mode(self, val):
if self._supports_sketch_mode:
self._sketch_mode = val
else:
raise KiPlotConfigurationError(
"This Layer output does not support sketch mode")
class GerberOptions(LayerOptions):
def __init__(self):
super(GerberOptions, self).__init__()
self._supports_line_width = True
self._supports_aux_axis_origin = True
self.subtract_mask_from_silk = False
self.use_protel_extensions = False
self.create_gerber_job_file = False
self.use_gerber_x2_attributes = False
self.use_gerber_net_attributes = False
# either 5 or 6
self._gerber_precision = None
def validate(self):
errs = super(GerberOptions, self).validate()
if (not self.use_gerber_x2_attributes and
self.use_gerber_net_attributes):
errs.append("Must set Gerber X2 attributes to use net attributes")
return errs
@property
def gerber_precision(self):
return self._gerber_precision
@gerber_precision.setter
def gerber_precision(self, val):
"""
Set gerber precision: either 4.5 or 4.6
"""
if val == 4.5:
self._gerber_precision = 5
elif val == 4.6:
self._gerber_precision = 6
else:
raise KiPlotConfigurationError(
"Bad Gerber precision : {}".format(val))
class HpglOptions(LayerOptions):
def __init__(self):
super(HpglOptions, self).__init__()
self._supports_sketch_mode = True
self._supports_mirror = True
self._supports_scaling = True
self._supports_drill_marks = True
self._pen_width = None
@property
def pen_width(self):
return self._pen_width
@pen_width.setter
def pen_width(self, pw_mm):
self._pen_width = pcbnew.FromMM(pw_mm)
class PsOptions(LayerOptions):
def __init__(self):
super(PsOptions, self).__init__()
self._supports_mirror = True
self._supports_negative = True
self._supports_scaling = True
self._supports_drill_marks = True
self._supports_line_width = True
self._supports_sketch_mode = True
self.scale_adjust_x = 1.0
self.scale_adjust_y = 1.0
self._width_adjust = 0
self.a4_output = False
@property
def width_adjust(self):
return self._width_adjust
@width_adjust.setter
def width_adjust(self, width_adjust_mm):
self._width_adjust = pcbnew.FromMM(width_adjust_mm)
class SvgOptions(LayerOptions):
def __init__(self):
super(SvgOptions, self).__init__()
self._supports_line_width = True
self._supports_mirror = True
self._supports_negative = True
self._supports_drill_marks = True
class PdfOptions(LayerOptions):
def __init__(self):
super(PdfOptions, self).__init__()
self._supports_line_width = True
self._supports_mirror = True
self._supports_negative = True
self._supports_drill_marks = True
class DxfOptions(LayerOptions):
def __init__(self):
super(DxfOptions, self).__init__()
self._supports_aux_axis_origin = True
self._supports_drill_marks = True
self.polygon_mode = False
class DrillOptions(TypeOptions):
def __init__(self):
super(DrillOptions, self).__init__()
self.use_aux_axis_as_origin = False
self.map_options = None
self.report_options = None
@property
def generate_map(self):
return self.map_options is not None
@property
def generate_report(self):
return self.report_options is not None
class ExcellonOptions(DrillOptions):
def __init__(self):
super(ExcellonOptions, self).__init__()
self.metric_units = True
self.minimal_header = False
self.mirror_y_axis = False
class GerberDrillOptions(DrillOptions):
def __init__(self):
super(GerberDrillOptions, self).__init__()
class DrillReportOptions(object):
def __init__(self):
self.filename = None
class DrillMapOptions(object):
def __init__(self):
self.type = None
class PositionOptions(TypeOptions):
def __init__(self):
self.format = None
self.units = None
self.separate_files_for_front_and_back = None
def validate(self):
errs = []
if self.format not in ["ASCII", "CSV"]:
errs.append("Format must be either ASCII or CSV")
if self.units not in ["millimeters", "inches"]:
errs.append("Units must be either millimeters or inches")
return errs
class KiBoMOptions(TypeOptions):
def __init__(self):
self.format = None
def validate(self):
errs = []
if self.format not in ["HTML", "CSV"]:
errs.append("Format must be either HTML or CSV")
return errs
class IBoMOptions(TypeOptions):
def __init__(self):
self.blacklist = None
self.name_format = None
class SchPrintOptions(TypeOptions):
def __init__(self):
self.output = None
class PcbPrintOptions(TypeOptions):
def __init__(self):
self.output = None
class OutputOptions(object):
GERBER = 'gerber'
POSTSCRIPT = 'ps'
HPGL = 'hpgl'
SVG = 'svg'
PDF = 'pdf'
DXF = 'dxf'
EXCELLON = 'excellon'
GERB_DRILL = 'gerb_drill'
POSITION = 'position'
KIBOM = 'kibom'
IBOM = 'ibom'
PDF_SCH_PRINT = 'pdf_sch_print'
PDF_PCB_PRINT = 'pdf_pcb_print'
def __init__(self, otype):
self.type = otype
if otype == self.GERBER:
self.type_options = GerberOptions()
elif otype == self.POSTSCRIPT:
self.type_options = PsOptions()
elif otype == self.HPGL:
self.type_options = HpglOptions()
elif otype == self.SVG:
self.type_options = SvgOptions()
elif otype == self.DXF:
self.type_options = DxfOptions()
elif otype == self.PDF:
self.type_options = PdfOptions()
elif otype == self.EXCELLON:
self.type_options = ExcellonOptions()
elif otype == self.GERB_DRILL:
self.type_options = GerberDrillOptions()
elif otype == self.POSITION:
self.type_options = PositionOptions()
elif otype == self.KIBOM:
self.type_options = KiBoMOptions()
elif otype == self.IBOM:
self.type_options = IBoMOptions()
elif otype == self.PDF_SCH_PRINT:
self.type_options = SchPrintOptions()
elif otype == self.PDF_PCB_PRINT:
self.type_options = PcbPrintOptions()
else: # pragma: no cover
# If we get here it means the above if is incomplete
raise KiPlotConfigurationError("Output options not implemented for "+otype)
def validate(self):
return self.type_options.validate()
class LayerInfo(object):
def __init__(self, layer, is_inner, name):
self.layer = layer
self.is_inner = is_inner
self.name = name
class LayerConfig(object):
def __init__(self, layer):
# the Pcbnew layer
self.layer = layer
self.suffix = ""
self.desc = "desc"
class PlotOutput(object):
def __init__(self, name, description, otype, options):
self.name = name
self.description = description
self.outdir = None
self.options = options
self.layers = []
def validate(self):
return self.options.validate()
class PlotConfig(object):
def __init__(self):
self._outputs = []
self.outdir = None
self.check_zone_fills = False
self.run_drc = False
self.update_xml = False
self.ignore_unconnected = False
self.run_erc = False
self.filters = None
def add_output(self, new_op):
self._outputs.append(new_op)
def add_filter(self, comment, number, regex):
logger.debug("Adding DRC/ERC filter '{}','{}','{}'".format(comment, number, regex))
if self.filters is None:
self.filters = ''
if comment:
self.filters += '# '+comment+'\n'
self.filters += '{},{}\n'.format(number, regex)
def validate(self):
errs = []
for o in self._outputs:
errs += o.validate()
return errs
@property
def outputs(self):
return self._outputs