KiBot/kiplot/kiplot.py

333 lines
10 KiB
Python

"""
Main Kiplot code
"""
import logging
import os
from . import plot_config as PCfg
from . import error
try:
import pcbnew
except ImportError:
logging.error("Failed to import pcbnew Python module."
" Do you need to add it to PYTHONPATH?")
raise
class PlotError(error.KiPlotError):
pass
class Plotter(object):
"""
Main Plotter class - this is what will perform the plotting
"""
def __init__(self, cfg):
self.cfg = cfg
def plot(self, brd_file):
logging.debug("Starting plot of board {}".format(brd_file))
board = pcbnew.LoadBoard(brd_file)
logging.debug("Board loaded")
self._preflight_checks(board)
for op in self.cfg.outputs:
logging.debug("Processing output: {}".format(op.name))
# fresh plot controller
pc = pcbnew.PLOT_CONTROLLER(board)
self._configure_output_dir(pc, op)
if self._output_is_layer(op):
self._do_layer_plot(board, pc, op)
elif self._output_is_drill(op):
self._do_drill_plot(board, pc, op)
else:
raise PlotError("Don't know how to plot type {}"
.format(op.options.type))
pc.ClosePlot()
def _preflight_checks(self, board):
logging.debug("Preflight checks")
if self.cfg.check_zone_fills:
raise PlotError("Not sure if Python scripts can do zone check!")
if self.cfg.run_drc:
raise PlotError("Not sure if Python scripts can run DRC!")
def _output_is_layer(self, output):
return output.options.type in [
PCfg.OutputOptions.GERBER,
PCfg.OutputOptions.POSTSCRIPT,
PCfg.OutputOptions.DXF,
PCfg.OutputOptions.SVG,
PCfg.OutputOptions.PDF,
PCfg.OutputOptions.HPGL,
]
def _output_is_drill(self, output):
return output.options.type in [
PCfg.OutputOptions.EXCELLON,
PCfg.OutputOptions.GERB_DRILL,
]
def _get_layer_plot_format(self, output):
"""
Gets the Pcbnew plot format for a given KiPlot output type
"""
mapping = {
PCfg.OutputOptions.GERBER: pcbnew.PLOT_FORMAT_GERBER,
PCfg.OutputOptions.POSTSCRIPT: pcbnew.PLOT_FORMAT_POST,
PCfg.OutputOptions.HPGL: pcbnew.PLOT_FORMAT_HPGL,
PCfg.OutputOptions.PDF: pcbnew.PLOT_FORMAT_PDF,
PCfg.OutputOptions.DXF: pcbnew.PLOT_FORMAT_DXF,
PCfg.OutputOptions.SVG: pcbnew.PLOT_FORMAT_SVG,
}
try:
return mapping[output.options.type]
except KeyError:
pass
raise ValueError("Don't know how to translate plot type: {}"
.format(output.options.type))
def _do_layer_plot(self, board, plot_ctrl, output):
# set up plot options for the whole output
self._configure_plot_ctrl(plot_ctrl, output)
po = plot_ctrl.GetPlotOptions()
layer_cnt = board.GetCopperLayerCount()
# plot every layer in the output
for l in output.layers:
layer = l.layer
suffix = l.suffix
desc = l.desc
# for inner layers, we can now check if the layer exists
if layer.is_inner:
if layer.layer < 1 or layer.layer >= layer_cnt - 1:
raise PlotError(
"Inner layer {} is not valid for this board"
.format(layer.layer))
# Set current layer
plot_ctrl.SetLayer(layer.layer)
# Skipping NPTH is controlled by whether or not this is
# a copper layer
is_cu = pcbnew.IsCopperLayer(layer.layer)
po.SetSkipPlotNPTH_Pads(is_cu)
plot_format = self._get_layer_plot_format(output)
# Plot single layer to file
logging.debug("Opening plot file for layer {} ({})"
.format(layer.layer, suffix))
plot_ctrl.OpenPlotfile(suffix, plot_format, desc)
logging.debug("Plotting layer {} to {}".format(
layer.layer, plot_ctrl.GetPlotFileName()))
plot_ctrl.PlotLayer()
def _configure_excellon_drill_writer(self, board, offset, options):
drill_writer = pcbnew.EXCELLON_WRITER(board)
to = options.type_options
mirror_y = to.mirror_y_axis
minimal_header = to.minimal_header
merge_npth = to.pth_and_npth_single_file
zeros_format = pcbnew.EXCELLON_WRITER.DECIMAL_FORMAT
drill_writer.SetOptions(mirror_y, minimal_header, offset, merge_npth)
drill_writer.SetFormat(to.metric_units, zeros_format)
return drill_writer
def _configure_gerber_drill_writer(self, board, offset, options):
drill_writer = pcbnew.GERBER_WRITER(board)
# hard coded in UI?
drill_writer.SetFormat(5)
drill_writer.SetOptions(offset)
return drill_writer
def _do_drill_plot(self, board, plot_ctrl, output):
to = output.options.type_options
outdir = plot_ctrl.GetPlotOptions().GetOutputDirectory()
# dialog_gendrill.cpp:357
if to.use_aux_axis_as_origin:
offset = board.GetAuxOrigin()
else:
offset = pcbnew.wxPoint(0, 0)
if output.options.type == PCfg.OutputOptions.EXCELLON:
drill_writer = self._configure_excellon_drill_writer(
board, offset, output.options)
elif output.options.type == PCfg.OutputOptions.GERB_DRILL:
drill_writer = self._configure_gerber_drill_writer(
board, offset, output.options)
else:
raise error.PlotError("Can't make a writer for type {}"
.format(output.options.type))
gen_drill = True
gen_map = to.generate_map
gen_report = to.generate_report
if gen_drill:
logging.debug("Generating drill files in {}"
.format(outdir))
if gen_map:
drill_writer.SetMapFileFormat(to.map_options.type)
logging.debug("Generating drill map type {} in {}"
.format(to.map_options.type, outdir))
drill_writer.CreateDrillandMapFilesSet(outdir, gen_drill, gen_map)
if gen_report:
drill_report_file = os.path.join(outdir,
to.report_options.filename)
logging.debug("Generating drill report: {}"
.format(drill_report_file))
drill_writer.GenDrillReportFile(drill_report_file)
def _configure_gerber_opts(self, po, output):
# true if gerber
po.SetUseGerberAttributes(True)
assert(output.options.type == PCfg.OutputOptions.GERBER)
gerb_opts = output.options.type_options
po.SetSubtractMaskFromSilk(gerb_opts.subtract_mask_from_silk)
po.SetUseGerberProtelExtensions(gerb_opts.use_protel_extensions)
po.SetGerberPrecision(gerb_opts.gerber_precision)
po.SetCreateGerberJobFile(gerb_opts.create_gerber_job_file)
po.SetUseGerberAttributes(gerb_opts.use_gerber_x2_attributes)
po.SetIncludeGerberNetlistInfo(gerb_opts.use_gerber_net_attributes)
def _configure_hpgl_opts(self, po, output):
assert(output.options.type == PCfg.OutputOptions.HPGL)
hpgl_opts = output.options.type_options
po.SetHPGLPenDiameter(hpgl_opts.pen_width)
def _configure_ps_opts(self, po, output):
assert(output.options.type == PCfg.OutputOptions.POSTSCRIPT)
ps_opts = output.options.type_options
po.SetWidthAdjust(ps_opts.width_adjust)
po.SetFineScaleAdjustX(ps_opts.scale_adjust_x)
po.SetFineScaleAdjustX(ps_opts.scale_adjust_y)
po.SetA4Output(ps_opts.a4_output)
def _configure_dxf_opts(self, po, output):
assert(output.options.type == PCfg.OutputOptions.DXF)
dxf_opts = output.options.type_options
po.SetDXFPlotPolygonMode(dxf_opts.polygon_mode)
def _configure_pdf_opts(self, po, output):
assert(output.options.type == PCfg.OutputOptions.PDF)
# pdf_opts = output.options.type_options
def _configure_svg_opts(self, po, output):
assert(output.options.type == PCfg.OutputOptions.SVG)
# pdf_opts = output.options.type_options
def _configure_output_dir(self, plot_ctrl, output):
po = plot_ctrl.GetPlotOptions()
# outdir is a combination of the config and output
outdir = os.path.join(self.cfg.outdir, output.outdir)
logging.debug("Output destination: {}".format(outdir))
po.SetOutputDirectory(outdir)
def _configure_plot_ctrl(self, plot_ctrl, output):
logging.debug("Configuring plot controller for output")
po = plot_ctrl.GetPlotOptions()
opts = output.options.type_options
po.SetLineWidth(opts.line_width)
po.SetAutoScale(opts.auto_scale)
po.SetScale(opts.scaling)
print opts.mirror_plot
po.SetMirror(opts.mirror_plot)
po.SetNegative(opts.negative_plot)
po.SetPlotFrameRef(opts.plot_sheet_reference)
po.SetPlotReference(opts.plot_footprint_refs)
po.SetPlotValue(opts.plot_footprint_values)
po.SetPlotInvisibleText(opts.force_plot_invisible_refs_vals)
po.SetExcludeEdgeLayer(opts.exclude_edge_layer)
po.SetPlotPadsOnSilkLayer(not opts.exclude_pads_from_silkscreen)
po.SetUseAuxOrigin(opts.use_aux_axis_as_origin)
po.SetPlotViaOnMaskLayer(not opts.tent_vias)
# in general, false, but gerber will set it back later
po.SetUseGerberAttributes(False)
if output.options.type == PCfg.OutputOptions.GERBER:
self._configure_gerber_opts(po, output)
elif output.options.type == PCfg.OutputOptions.POSTSCRIPT:
self._configure_ps_opts(po, output)
elif output.options.type == PCfg.OutputOptions.DXF:
self._configure_dxf_opts(po, output)
elif output.options.type == PCfg.OutputOptions.SVG:
self._configure_svg_opts(po, output)
elif output.options.type == PCfg.OutputOptions.PDF:
self._configure_pdf_opts(po, output)
elif output.options.type == PCfg.OutputOptions.HPGL:
self._configure_hpgl_opts(po, output)
po.SetDrillMarksType(opts.drill_marks)
# We'll come back to this on a per-layer basis
po.SetSkipPlotNPTH_Pads(False)