Moved sub-PCB specific stuff to var_base (from out_base)

- To allow more than one mechanism
- Also changed the order in which sub-PCBs are applied, will make
  things faster
- Also removed board as parameter for un/filter_pcb*
  Isn't really supported and the sub-PCBs clearly breaks it
This commit is contained in:
Salvador E. Tropea 2022-12-27 08:48:44 -03:00
parent bae5e91c7a
commit 1dbe58aab1
15 changed files with 116 additions and 94 deletions

View File

@ -159,7 +159,7 @@ class AnyDrill(VariantOptions):
def run(self, output_dir):
super().run(output_dir)
self.filter_pcb_components(GS.board)
self.filter_pcb_components()
if self.output:
output_dir = os.path.dirname(output_dir)
# dialog_gendrill.cpp:357
@ -187,7 +187,7 @@ class AnyDrill(VariantOptions):
drill_report_file = self.expand_filename(output_dir, self.report, 'drill_report', 'txt')
logger.debug("Generating drill report: "+drill_report_file)
drill_writer.GenDrillReportFile(drill_report_file)
self.unfilter_pcb_components(GS.board)
self.unfilter_pcb_components()
def get_targets(self, out_dir):
targets = []

View File

@ -117,7 +117,7 @@ class AnyLayerOptions(VariantOptions):
def run(self, output_dir, layers):
super().run(output_dir)
# Apply the variants and filters
exclude = self.filter_pcb_components(GS.board)
exclude = self.filter_pcb_components()
# fresh plot controller
plot_ctrl = PLOT_CONTROLLER(GS.board)
# set up plot options for the whole output
@ -185,7 +185,7 @@ class AnyLayerOptions(VariantOptions):
f.write(content)
# Restore the eliminated layers
if exclude:
self.unfilter_pcb_components(GS.board)
self.unfilter_pcb_components()
def solve_extension(self, layer):
if self._plot_format == PLOT_FORMAT_GERBER and self.use_protel_extensions:

View File

@ -57,12 +57,12 @@ class Any_PCB_PrintOptions(VariantOptions):
def filter_components(self):
if not self.will_filter_pcb_components() and self.title == '':
return GS.pcb_file, None
self.filter_pcb_components(GS.board)
self.filter_pcb_components()
self.set_title(self.title)
# Save the PCB to a temporal dir
fname, pcb_dir = self.save_tmp_dir_board('pdf_pcb_print')
self.restore_title()
self.unfilter_pcb_components(GS.board)
self.unfilter_pcb_components()
return fname, pcb_dir
def get_targets(self, out_dir):

View File

@ -105,14 +105,14 @@ class Stencil_Options(VariantOptions):
self.ensure_tool('Xvfb')
super().run(output)
# Apply variants and filters
filtered = self.filter_pcb_components(GS.board)
filtered = self.filter_pcb_components()
if self.side == 'auto':
detected_top, detected_bottom = self.detect_solder_paste(GS.board)
else:
detected_top = detected_bottom = False
fname = self.save_tmp_board() if filtered else GS.pcb_file
if filtered:
self.unfilter_pcb_components(GS.board)
self.unfilter_pcb_components()
# Avoid running the tool if we will generate useless models
if self.side == 'auto' and not detected_top and not detected_bottom:
logger.warning(W_AUTONONE+'No solder paste detected, skipping stencil generation')

View File

@ -8,9 +8,9 @@ from glob import glob
import math
import os
import re
from tempfile import NamedTemporaryFile, mkdtemp, TemporaryDirectory
from tempfile import NamedTemporaryFile, mkdtemp
from .gs import GS
from .kiplot import load_sch, get_board_comps_data, load_board
from .kiplot import load_sch, get_board_comps_data
from .misc import Rect, W_WRONGPASTE, DISABLE_3D_MODEL_TEXT, W_NOCRTYD
if not GS.kicad_version_n:
# When running the regression tests we need it
@ -366,6 +366,7 @@ class VariantOptions(BaseOptions):
""" Remove from solder paste layers the filtered components. """
if comps_hash is None or not (GS.global_remove_solder_paste_for_dnp or GS.global_remove_adhesive_for_dnp):
return
logger.debug('Removing paste and glue')
exclude = LSET()
fpaste = board.GetLayerID('F.Paste')
bpaste = board.GetLayerID('B.Paste')
@ -398,16 +399,22 @@ class VariantOptions(BaseOptions):
logger.warning(W_WRONGPASTE+'Pad with solder paste, but no copper or solder mask aperture in '+ref)
p.SetLayerSet(pad_layers)
old_layers.append(old_c_layers)
logger.debugl(3, '- Removed paste from '+ref)
# Remove any graphical item in the *.Adhes layers
if GS.global_remove_adhesive_for_dnp:
found = False
for gi in m.GraphicalItems():
l_gi = gi.GetLayer()
if l_gi == fadhes:
gi.SetLayer(rescue)
old_fadhes.append(gi)
found = True
if l_gi == badhes:
gi.SetLayer(rescue)
old_badhes.append(gi)
found = True
if found:
logger.debugl(3, '- Removed adhesive from '+ref)
# Store the data to undo the above actions
self.old_layers = old_layers
self.old_fadhes = old_fadhes
@ -419,11 +426,13 @@ class VariantOptions(BaseOptions):
def restore_paste_and_glue(self, board, comps_hash):
if comps_hash is None:
return
logger.debug('Restoring paste and glue')
if GS.global_remove_solder_paste_for_dnp:
for m in GS.get_modules_board(board):
ref = m.GetReference()
c = comps_hash.get(ref, None)
if c and c.included and not c.fitted:
logger.debugl(3, '- Restoring paste for '+ref)
restore = self.old_layers.pop(0)
for p in m.Pads():
pad_layers = p.GetLayerSet()
@ -714,91 +723,54 @@ class VariantOptions(BaseOptions):
m.Models().pop()
self._highlighted_3D_components = None
def apply_sub_pcb(self):
with TemporaryDirectory(prefix='kibot-separate') as d:
dest = os.path.join(d, os.path.basename(GS.pcb_file))
# Save the current PCB, with any changes applied
with NamedTemporaryFile(mode='w', suffix='.kicad_pcb', delete=False) as f:
pcb_file = f.name
GS.board.Save(pcb_file)
if self._comps:
# Memorize the used modules
old_modules = {m.GetReference() for m in GS.get_modules()}
# Now do the separation
self._sub_pcb.load_board(pcb_file, dest)
# Remove the temporal PCB
os.remove(pcb_file)
# Compute the modules we removed
self._excl_by_sub_pcb = set()
# Now reflect it in the list of components
if self._comps:
logger.debug('Removing components outside the sub-PCB')
# Memorize the used modules
new_modules = {m.GetReference() for m in GS.get_modules()}
diff = old_modules - new_modules
logger.debugl(3, diff)
# Exclude them from _comps
for c in diff:
cmp = self.comps_hash[c]
if cmp.included:
cmp.included = False
self._excl_by_sub_pcb.add(c)
logger.debugl(2, '- Removing '+c)
def will_filter_pcb_components(self):
""" True if we will apply filters/variants """
return self._comps or self._sub_pcb
def filter_pcb_components(self, board, do_3D=False, do_2D=True, highlight=None):
def filter_pcb_components(self, do_3D=False, do_2D=True, highlight=None):
if not self.will_filter_pcb_components():
return False
self.comps_hash = self.get_refs_hash()
if self._sub_pcb:
self._sub_pcb.apply(self.comps_hash)
if self._comps:
self.comps_hash = self.get_refs_hash()
if do_2D:
self.cross_modules(board, self.comps_hash)
self.remove_paste_and_glue(board, self.comps_hash)
self.cross_modules(GS.board, self.comps_hash)
self.remove_paste_and_glue(GS.board, self.comps_hash)
if hasattr(self, 'hide_excluded') and self.hide_excluded:
self.remove_fab(board, self.comps_hash)
self.remove_fab(GS.board, self.comps_hash)
# Copy any change in the schematic fields to the PCB properties
# I.e. the value of a component so it gets updated in the *.Fab layer
# Also useful for iBoM that can read the sch fields from the PCB
self.sch_fields_to_pcb(board, self.comps_hash)
self.sch_fields_to_pcb(GS.board, self.comps_hash)
if do_3D:
# Disable the models that aren't for this variant
self.apply_3D_variant_aspect(board)
self.apply_3D_variant_aspect(GS.board)
# Remove the 3D models for not fitted components (also rename)
self.remove_3D_models(board, self.comps_hash)
self.remove_3D_models(GS.board, self.comps_hash)
# Highlight selected components
self.highlight_3D_models(board, highlight)
if self._sub_pcb:
# Current implementation isn't efficient
self.apply_sub_pcb()
self.highlight_3D_models(GS.board, highlight)
return True
def unfilter_pcb_components(self, board, do_3D=False, do_2D=True):
def unfilter_pcb_components(self, do_3D=False, do_2D=True):
if not self.will_filter_pcb_components():
return
if self._sub_pcb:
# Undo the sub-PCB: just reload the PCB
GS.board = None
load_board()
for c in self._excl_by_sub_pcb:
self.comps_hash[c].included = True
return
if do_2D:
self.uncross_modules(board, self.comps_hash)
self.restore_paste_and_glue(board, self.comps_hash)
if do_2D and self.comps_hash:
self.uncross_modules(GS.board, self.comps_hash)
self.restore_paste_and_glue(GS.board, self.comps_hash)
if hasattr(self, 'hide_excluded') and self.hide_excluded:
self.restore_fab(board, self.comps_hash)
self.restore_fab(GS.board, self.comps_hash)
# Restore the PCB properties and values
self.restore_sch_fields_to_pcb(board)
if do_3D:
self.restore_sch_fields_to_pcb(GS.board)
if do_3D and self.comps_hash:
# Undo the removing (also rename)
self.restore_3D_models(board, self.comps_hash)
self.restore_3D_models(GS.board, self.comps_hash)
# Re-enable the modules that aren't for this variant
self.apply_3D_variant_aspect(board, enable=True)
self.apply_3D_variant_aspect(GS.board, enable=True)
# Remove the highlight 3D object
self.unhighlight_3D_models(board)
self.unhighlight_3D_models(GS.board)
if self._sub_pcb:
self._sub_pcb.revert(self.comps_hash)
def remove_highlight_3D_file(self):
# Remove the highlight 3D file if it was created
@ -894,11 +866,11 @@ class VariantOptions(BaseOptions):
if not self.will_filter_pcb_components() and not new_title:
return GS.pcb_file
logger.debug('Creating modified PCB')
self.filter_pcb_components(GS.board, do_3D=do_3D)
self.filter_pcb_components(do_3D=do_3D)
self.set_title(new_title)
fname = self.save_tmp_board()
self.restore_title()
self.unfilter_pcb_components(GS.board, do_3D=do_3D)
self.unfilter_pcb_components(do_3D=do_3D)
to_remove.extend(GS.get_pcb_and_pro_names(fname))
logger.debug('- Modified PCB: '+fname)
return fname

View File

@ -201,10 +201,10 @@ class Base3DOptions(VariantOptions):
self.undo_3d_models_rename(GS.board)
return ret
return GS.pcb_file
self.filter_pcb_components(GS.board, do_3D=True, do_2D=True, highlight=highlight)
self.filter_pcb_components(do_3D=True, do_2D=True, highlight=highlight)
self.download_models()
fname = self.save_tmp_board()
self.unfilter_pcb_components(GS.board, do_3D=True, do_2D=True)
self.unfilter_pcb_components(do_3D=True, do_2D=True)
return fname
def get_targets(self, out_dir):

View File

@ -162,10 +162,10 @@ class BoardViewOptions(VariantOptions):
def run(self, output):
super().run(output)
self.filter_pcb_components(GS.board)
self.filter_pcb_components()
with open(output, 'wt') as f:
convert(GS.board, f)
self.unfilter_pcb_components(GS.board)
self.unfilter_pcb_components()
def get_targets(self, out_dir):
return [self._parent.expand_filename(out_dir, self.output)]

View File

@ -126,7 +126,7 @@ class Copy_FilesOptions(Base3DOptions):
dest_dir = dest_dir[:-1]
f.output_dir = dest_dir
# Apply any variant
self.filter_pcb_components(GS.board, do_3D=True, do_2D=True)
self.filter_pcb_components(do_3D=True, do_2D=True)
# Download missing models and rename all collect 3D models (renamed)
f.rel_dirs = self.rel_dirs
files_list = self.download_models(rename_filter=f.source, rename_function=FilesList.apply_rename, rename_data=f)
@ -139,7 +139,7 @@ class Copy_FilesOptions(Base3DOptions):
# We must undo the download/rename
self.undo_3d_models_rename(GS.board)
else:
self.unfilter_pcb_components(GS.board, do_3D=True, do_2D=True)
self.unfilter_pcb_components(do_3D=True, do_2D=True)
# Also include the step/wrl counterpart
new_list = []
for fn in files_list:

View File

@ -184,9 +184,9 @@ class IBoMOptions(VariantOptions):
with open(netlist, 'wb') as f:
GS.sch.save_netlist(f, self._comps)
# Write a board with the filtered values applied
self.filter_pcb_components(GS.board)
self.filter_pcb_components()
pcb_name, _ = self.save_tmp_dir_board('ibom', force_dir=net_dir)
self.unfilter_pcb_components(GS.board)
self.unfilter_pcb_components()
else:
# Check if the user wants extra_fields but there is no data about them (#68)
if self.need_extra_fields() and not os.path.isfile(self.extra_data_file):

View File

@ -1142,9 +1142,9 @@ class PCB_PrintOptions(VariantOptions):
svgutils = importlib.import_module('.svgutils.transform', package=__package__)
global kicad_worksheet
kicad_worksheet = importlib.import_module('.kicad.worksheet', package=__package__)
self.filter_pcb_components(GS.board)
self.filter_pcb_components()
self.generate_output(output)
self.unfilter_pcb_components(GS.board)
self.unfilter_pcb_components()
@output_class

View File

@ -33,14 +33,14 @@ class PCB_Variant_Options(VariantOptions):
def run(self, output):
super().run(output)
self.filter_pcb_components(GS.board, do_3D=True)
self.filter_pcb_components(do_3D=True)
self.set_title(self.title)
logger.debug('Saving PCB to '+output)
GS.board.Save(output)
if self.copy_project:
GS.copy_project(output)
self.restore_title()
self.unfilter_pcb_components(GS.board, do_3D=True)
self.unfilter_pcb_components(do_3D=True)
@output_class

View File

@ -521,11 +521,11 @@ class PcbDrawOptions(VariantOptions):
def run(self, name):
super().run(name)
# Apply any variant
self.filter_pcb_components(GS.board, do_3D=True)
self.filter_pcb_components(do_3D=True)
# Create the image
self.create_image(name, GS.board)
# Undo the variant
self.unfilter_pcb_components(GS.board, do_3D=True)
self.unfilter_pcb_components(do_3D=True)
@output_class

View File

@ -222,7 +222,7 @@ class PositionOptions(VariantOptions):
def run(self, fname):
super().run(fname)
self.filter_pcb_components(GS.board)
self.filter_pcb_components()
output_dir = os.path.dirname(fname)
columns = self.columns.values()
conv = GS.unit_name_to_scale_factor(self.units)
@ -304,7 +304,7 @@ class PositionOptions(VariantOptions):
self._do_position_plot_ascii(output_dir, columns, modules, maxlengths, modules_side)
else: # if self.format == 'CSV':
self._do_position_plot_csv(output_dir, columns, modules, modules_side)
self.unfilter_pcb_components(GS.board)
self.unfilter_pcb_components()
@output_class

View File

@ -3,6 +3,8 @@
# Copyright (c) 2020-2022 Instituto Nacional de Tecnología Industrial
# License: GPL-3.0
# Project: KiBot (formerly KiPlot)
import os
from tempfile import NamedTemporaryFile, TemporaryDirectory
from .registrable import RegVariant
from .optionable import Optionable, PanelOptions
from .fil_base import apply_exclude_filter, apply_fitted_filter, apply_fixed_filter, apply_pre_transform
@ -11,6 +13,9 @@ from .misc import KIKIT_UNIT_ALIASES
from .gs import GS
from .kiplot import load_board, run_command
from .macros import macros, document # noqa: F401
from . import log
logger = log.get_logger()
class SubPCBOptions(PanelOptions):
@ -66,15 +71,59 @@ class SubPCBOptions(PanelOptions):
return "annotation; ref: {}".format(self.reference)
return "rectangle; tlx: {}; tly: {}; brx: {}; bry: {}".format(self.tlx, self.tly, self.brx, self.bry)
def load_board(self, pcb_file, dest):
def load_board(self, comps_hash):
""" Apply the sub-PCB using an external tool and load it into memory """
# Make sure kikit is available
command = GS.ensure_tool('global', 'KiKit')
# Execute the separate
cmd = [command, 'separate', '-s', self.get_separate_source(), pcb_file, dest]
run_command(cmd)
# Load this board
with TemporaryDirectory(prefix='kibot-separate') as d:
dest = os.path.join(d, os.path.basename(GS.pcb_file))
# Save the current PCB, with any changes applied
with NamedTemporaryFile(mode='w', suffix='.kicad_pcb', delete=False) as f:
pcb_file = f.name
GS.board.Save(pcb_file)
if comps_hash:
# Memorize the used modules
old_modules = {m.GetReference() for m in GS.get_modules()}
# Now do the separation
cmd = [command, 'separate', '-s', self.get_separate_source(), pcb_file, dest]
# Execute the separate
run_command(cmd)
# Load this board
GS.board = None
load_board(dest)
# Remove the temporal PCB
os.remove(pcb_file)
self._excl_by_sub_pcb = set()
# Now reflect the changes in the list of components
if comps_hash:
logger.debug('Removing components outside the sub-PCB')
# Memorize the used modules
new_modules = {m.GetReference() for m in GS.get_modules()}
# Compute the modules we removed
diff = old_modules - new_modules
logger.debugl(3, diff)
# Exclude them from _comps
for c in diff:
cmp = comps_hash[c]
if cmp.included:
cmp.included = False
self._excl_by_sub_pcb.add(c)
logger.debugl(2, '- Removing '+c)
def apply(self, comps_hash):
if True:
self.load_board(comps_hash)
def unload_board(self, comps_hash):
# Undo the sub-PCB: just reload the PCB
GS.board = None
load_board(dest)
load_board()
for c in self._excl_by_sub_pcb:
comps_hash[c].included = True
def revert(self, comps_hash):
if True:
self.unload_board(comps_hash)
class BaseVariant(RegVariant):

View File

@ -71,4 +71,5 @@ outputs:
dir: PopulateWithFilter
options:
renderer: KiCad_3D
# renderer: PcbDraw
input: tests/data/with_filter_html.md