From 452f95b9648fecae124c990f046a77cc98273834 Mon Sep 17 00:00:00 2001 From: Ryan Jarvis Date: Tue, 15 Aug 2017 23:47:46 -0700 Subject: [PATCH] Convert to absolute_imports in preparation for Python3. add object to a couple of old-style classes. Whitespace fixes. --- pcbmode/pcbmode.py | 176 +++++----- pcbmode/utils/__init__.py | 1 + pcbmode/utils/board.py | 13 +- pcbmode/utils/bom.py | 143 ++++----- pcbmode/utils/component.py | 89 +++--- pcbmode/utils/coord_file.py | 45 ++- pcbmode/utils/excellon.py | 70 ++-- pcbmode/utils/extract.py | 187 +++++------ pcbmode/utils/footprint.py | 76 ++--- pcbmode/utils/gerber.py | 586 +++++++++++++++------------------ pcbmode/utils/messages.py | 10 +- pcbmode/utils/module.py | 395 +++++++++-------------- pcbmode/utils/place.py | 69 ++-- pcbmode/utils/point.py | 20 +- pcbmode/utils/shape.py | 84 ++--- pcbmode/utils/style.py | 19 +- pcbmode/utils/svg.py | 599 +++++++++++++++------------------- pcbmode/utils/svgpath.py | 622 ++++++++++++++++-------------------- pcbmode/utils/utils.py | 255 +++++---------- setup.py | 26 +- 20 files changed, 1441 insertions(+), 2044 deletions(-) diff --git a/pcbmode/pcbmode.py b/pcbmode/pcbmode.py index 2f4c5d1f..5cc57f93 100644 --- a/pcbmode/pcbmode.py +++ b/pcbmode/pcbmode.py @@ -1,27 +1,23 @@ #!/usr/bin/python +# coding=utf-8 +from __future__ import absolute_import -import os -import json import argparse +import json +import os + +from pkg_resources import resource_exists, resource_filename + +# PCBmodE modules +from pcbmode import config +from pcbmode.utils import bom, coord_file, excellon, extract, gerber, messages as msg, utils +from pcbmode.utils.board import Board try: from os import getcwdu as getcwd except: from os import getcwd as getcwd -from pkg_resources import resource_filename, resource_exists - -# PCBmodE modules -from . import config -from .utils import utils -from .utils import gerber -from .utils import extract -from .utils import excellon -from .utils import messages as msg -from .utils import bom -from .utils import coord_file -from .utils.board import Board - def cmdArgSetup(pcbmode_version): """ @@ -32,31 +28,31 @@ def cmdArgSetup(pcbmode_version): epilog = """ """ - + # commandline argument settings and parsing - argp = argparse.ArgumentParser(description=description, - add_help=True, epilog=epilog) - + argp = argparse.ArgumentParser(description=description, + add_help=True, epilog=epilog) + argp.add_argument('-b', '--board-name', dest='boards', required=True, nargs=1, help='The name of the board. The location of the files should be specified in the configuration file, otherwise defaults are used') - + argp.add_argument('-f', '--filein', required=False, dest='filein', help='Input file name') - + argp.add_argument('-o', '--fileout', dest='fileout', help='Output file name') - + argp.add_argument('-c', '--config-file', default='pcbmode_config.json', dest='config_file', help='Configuration file name (default=pcbmode_config.json)') - + argp.add_argument('-m', '--make-board', action='store_true', dest='make', default=False, help="Create SVG for the board specified with the '-b'/'--board_name' switch. The output's location can be specified in the configuration file") - + argp.add_argument('-e', '--extract', action='store_true', dest='extract', default=False, help="Extract routing and component placement from board's SVG") @@ -64,7 +60,7 @@ def cmdArgSetup(pcbmode_version): argp.add_argument('--extract-refdefs', action='store_true', dest='extract_refdefs', default=False, help="Extract components' reference designator location and rotation from board's SVG") - + argp.add_argument('--fab', nargs='?', dest='fab', default=False, help='Generate manufacturing files (Gerbers, Excellon, etc.) An optional argument specifies the fab for custom filenames') @@ -98,21 +94,16 @@ def cmdArgSetup(pcbmode_version): help="Create a simple placement coordinate CSV file") argp.add_argument('--make-bom', nargs='?', - dest='make_bom', default=False, + dest='make_bom', default=False, help='Create a bill of materials') argp.add_argument('--sig-dig', nargs=1, dest='sig_dig', default=False, help="Number of significant digits to use when generating the board's SVG. Valid values are between 2 and 8.") - return argp - - - - def makeConfig(name, version, cmdline_args): """ """ @@ -147,8 +138,8 @@ def makeConfig(name, version, cmdline_args): # Read in the board's configuration data msg.info("Processing board's configuration file") - filename = os.path.join(config.cfg['locations']['boards'], - config.cfg['name'], + filename = os.path.join(config.cfg['locations']['boards'], + config.cfg['name'], config.cfg['name'] + '.json') config.brd = utils.dictFromJsonFile(filename) @@ -161,10 +152,9 @@ def makeConfig(name, version, cmdline_args): config.brd['config']['units'] = 'mm' config.brd['config']['style-layout'] = 'default' - - #================================= + # ================================= # Style - #================================= + # ================================= # Get style file; search for it in the project directory and # where the script it @@ -172,7 +162,7 @@ def makeConfig(name, version, cmdline_args): layout_style_filename = 'layout.json' paths = [os.path.join(config.cfg['base-dir'], config.cfg['locations']['styles'], - layout_style, layout_style_filename)] # project dir + layout_style, layout_style_filename)] # project dir style_resource = (__name__, '/'.join(['styles', layout_style, layout_style_filename])) if resource_exists(*style_resource): @@ -189,15 +179,15 @@ def makeConfig(name, version, cmdline_args): if not 'layout' in config.stl or config.stl['layout'] == {}: msg.error("Couldn't find style file %s. Looked for it here:\n%s" % (layout_style_filename, filenames)) - #------------------------------------------------------------- + # ------------------------------------------------------------- # Stackup - #------------------------------------------------------------- + # ------------------------------------------------------------- try: stackup_filename = config.brd['stackup']['name'] + '.json' except: stackup_filename = 'two-layer.json' - paths = [os.path.join(config.cfg['base-dir'], config.cfg['locations']['stackups'], stackup_filename)] # project dir + paths = [os.path.join(config.cfg['base-dir'], config.cfg['locations']['stackups'], stackup_filename)] # project dir stackup_resource = (__name__, '/'.join(['stackups', stackup_filename])) if resource_exists(*stackup_resource): @@ -220,10 +210,10 @@ def makeConfig(name, version, cmdline_args): config.stk['surface-layer-names'] = [config.stk['layer-names'][0], config.stk['layer-names'][-1]] config.stk['internal-layer-names'] = config.stk['layer-names'][1:-1] - #--------------------------------------------------------------- + # --------------------------------------------------------------- # Path database - #--------------------------------------------------------------- - filename = os.path.join(config.cfg['locations']['boards'], + # --------------------------------------------------------------- + filename = os.path.join(config.cfg['locations']['boards'], config.cfg['name'], config.cfg['locations']['build'], 'paths_db.json') @@ -233,11 +223,10 @@ def makeConfig(name, version, cmdline_args): if os.path.isfile(filename): config.pth = utils.dictFromJsonFile(filename) - - #---------------------------------------------------------------- + # ---------------------------------------------------------------- # Routing - #---------------------------------------------------------------- - filename = os.path.join(config.cfg['base-dir'], + # ---------------------------------------------------------------- + filename = os.path.join(config.cfg['base-dir'], config.brd['files'].get('routing-json') or config.cfg['name'] + '_routing.json') # Open database file. If it doesn't exist, leave the database in @@ -247,19 +236,18 @@ def makeConfig(name, version, cmdline_args): else: config.rte = {} - # namespace URLs config.cfg['ns'] = { - None : "http://www.w3.org/2000/svg", - "dc" : "http://purl.org/dc/elements/1.1/", - "cc" : "http://creativecommons.org/ns#", - "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - "svg" : "http://www.w3.org/2000/svg", - "sodipodi" : "http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd", - "inkscape" : "http://www.inkscape.org/namespaces/inkscape", + None: "http://www.w3.org/2000/svg", + "dc": "http://purl.org/dc/elements/1.1/", + "cc": "http://creativecommons.org/ns#", + "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "svg": "http://www.w3.org/2000/svg", + "sodipodi": "http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd", + "inkscape": "http://www.inkscape.org/namespaces/inkscape", # Namespace URI are strings; they don't need to be URLs. See: # http://en.wikipedia.org/wiki/XML_namespace - "pcbmode" : "pcbmode" + "pcbmode": "pcbmode" } config.cfg['namespace'] = config.cfg['ns'] @@ -280,13 +268,12 @@ def makeConfig(name, version, cmdline_args): # the style for masks used for copper pours config.cfg['mask-style'] = "fill:#000;stroke:#000;stroke-linejoin:round;stroke-width:%s;" - - #------------------------------------------------------------------ + # ------------------------------------------------------------------ # Distances - #------------------------------------------------------------------ + # ------------------------------------------------------------------ # If any of the distance definitions are missing from the board's # configuration file, use PCBmodE's defaults - #------------------------------------------------------------------ + # ------------------------------------------------------------------ config_distances_dict = config.cfg['distances'] try: board_distances_dict = config.brd.get('distances') @@ -302,17 +289,17 @@ def makeConfig(name, version, cmdline_args): except: board_distances_dict[dk] = {} board_dict = board_distances_dict[dk] - + for k in config_dict.keys(): board_dict[k] = (board_dict.get(k) or config_dict[k]) - #----------------------------------------------------------------- + # ----------------------------------------------------------------- # Commandline overrides - #----------------------------------------------------------------- + # ----------------------------------------------------------------- # These are stored in a temporary dictionary so that they are not # written to the config file when the board's configuration is # dumped, with extraction, for example - #----------------------------------------------------------------- + # ----------------------------------------------------------------- config.tmp = {} config.tmp['no-layer-index'] = (cmdline_args.no_layer_index or config.brd['config'].get('no-layer-index') or @@ -327,13 +314,12 @@ def makeConfig(name, version, cmdline_args): config.brd['config'].get('no-drill-index') or False) - # Define Gerber setting from board's config or defaults try: tmp = config.brd['gerber'] except: config.brd['gerber'] = {} - gd = config.brd['gerber'] + gd = config.brd['gerber'] gd['decimals'] = config.brd['gerber'].get('decimals') or 6 gd['digits'] = config.brd['gerber'].get('digits') or 6 gd['steps-per-segment'] = config.brd['gerber'].get('steps-per-segment') or 100 @@ -345,31 +331,30 @@ def makeConfig(name, version, cmdline_args): # control if things change. config.cfg['invert-y'] = -1 - - #----------------------------------------------------------------- + # ----------------------------------------------------------------- # Commandline overrides - #----------------------------------------------------------------- + # ----------------------------------------------------------------- # Controls the visibility of layers and whether they are locked by # default. This is the "master" control; settings in the board's # config file will override these settings - #----------------------------------------------------------------- + # ----------------------------------------------------------------- layer_control_default = { - "conductor": { - "place": True, "hide": False, "lock": False, - "pours": { "place": True, "hide": False, "lock": True }, - "pads": { "place": True, "hide": False, "lock": False }, - "routing": { "place": True, "hide": False, "lock": False } - }, - "soldermask": { "place": True, "hide": False, "lock": False }, - "solderpaste": { "place": True, "hide": True, "lock": True }, - "silkscreen": { "place": True, "hide": False, "lock": False }, - "assembly": { "place": True, "hide": False, "lock": False }, - "documentation": { "place": True, "hide": False, "lock": False }, - "dimensions": { "place": True, "hide": False, "lock": True }, - "origin": { "place": True, "hide": False, "lock": True }, - "drills": { "place": True, "hide": False, "lock": False }, - "placement": { "place": True, "hide": False, "lock": False }, - "outline": { "place": True, "hide": False, "lock": True } + "conductor": { + "place": True, "hide": False, "lock": False, + "pours": {"place": True, "hide": False, "lock": True}, + "pads": {"place": True, "hide": False, "lock": False}, + "routing": {"place": True, "hide": False, "lock": False} + }, + "soldermask": {"place": True, "hide": False, "lock": False}, + "solderpaste": {"place": True, "hide": True, "lock": True}, + "silkscreen": {"place": True, "hide": False, "lock": False}, + "assembly": {"place": True, "hide": False, "lock": False}, + "documentation": {"place": True, "hide": False, "lock": False}, + "dimensions": {"place": True, "hide": False, "lock": True}, + "origin": {"place": True, "hide": False, "lock": True}, + "drills": {"place": True, "hide": False, "lock": False}, + "placement": {"place": True, "hide": False, "lock": False}, + "outline": {"place": True, "hide": False, "lock": True} } # Get overrides @@ -380,15 +365,10 @@ def makeConfig(name, version, cmdline_args): else: config.brd['layer-control'] = layer_control_default - return - - - def main(): - # Get PCBmodE version version = utils.get_git_revision() @@ -411,7 +391,7 @@ def main(): if cmdline_args.renumber is None: order = 'top-to-bottom' else: - order = cmdline_args.renumber.lower() + order = cmdline_args.renumber.lower() utils.renumberRefdefs(order) @@ -439,19 +419,18 @@ def main(): manufacturer = 'default' else: manufacturer = cmdline_args.fab.lower() - + msg.info("Creating Gerbers") gerber.gerberise(manufacturer) msg.info("Creating excellon drill file") excellon.makeExcellon(manufacturer) - + if cmdline_args.pngs is True: msg.info("Creating PNGs") utils.makePngs() - - - filename = os.path.join(config.cfg['locations']['boards'], + + filename = os.path.join(config.cfg['locations']['boards'], config.cfg['name'], config.cfg['locations']['build'], 'paths_db.json') @@ -460,13 +439,12 @@ def main(): f = open(filename, 'w') except IOError as e: print("I/O error({0}): {1}".format(e.errno, e.strerror)) - + json.dump(config.pth, f, sort_keys=True, indent=2) f.close() msg.info("Done!") - if __name__ == "__main__": main() diff --git a/pcbmode/utils/__init__.py b/pcbmode/utils/__init__.py index e69de29b..9bad5790 100644 --- a/pcbmode/utils/__init__.py +++ b/pcbmode/utils/__init__.py @@ -0,0 +1 @@ +# coding=utf-8 diff --git a/pcbmode/utils/board.py b/pcbmode/utils/board.py index 974a7e88..3291be17 100644 --- a/pcbmode/utils/board.py +++ b/pcbmode/utils/board.py @@ -1,21 +1,18 @@ #!/usr/bin/python +# coding=utf-8 -import pcbmode.config as config -from . import messages as msg -from .module import Module +from __future__ import absolute_import +import pcbmode.config as config +from pcbmode.utils.module import Module -class Board(): +class Board(object): """ """ def __init__(self): - self._module_dict = config.brd self._module_routing = config.rte module = Module(self._module_dict, self._module_routing) - - - diff --git a/pcbmode/utils/bom.py b/pcbmode/utils/bom.py index 07b2c2a7..929a58cd 100644 --- a/pcbmode/utils/bom.py +++ b/pcbmode/utils/bom.py @@ -1,13 +1,12 @@ #!/usr/bin/python +# coding=utf-8 +from __future__ import absolute_import -#import json import os import re -import pcbmode.config as config - -from . import utils -from . import messages as msg +import pcbmode.config as config +from pcbmode.utils import messages as msg, utils def make_bom(quantity=None): @@ -19,13 +18,11 @@ def natural_key(string_): """See http://www.codinghorror.com/blog/archives/001018.html""" return [int(s) if s.isdigit() else s for s in re.split(r'(\d+)', string_)] - - dnp_text = 'Do not populate' uncateg_text = 'Uncategorised' components_dict = config.brd['components'] - + bom_dict = {} for refdef in components_dict: @@ -50,26 +47,25 @@ def natural_key(string_): footprint_name = components_dict[refdef]['footprint'] except: msg.error("Cannot find a 'footprint' name for refdef %s." % refdef) - + # Open footprint file fname = os.path.join(config.cfg['base-dir'], config.cfg['locations']['components'], footprint_name + '.json') footprint_dict = utils.dictFromJsonFile(fname) - + info_dict = footprint_dict.get('info') or {} - - try: + + try: comp_bom_dict = components_dict[refdef]['bom'] except: comp_bom_dict = {} - - try: + + try: fp_bom_dict = footprint_dict['info'] except: fp_bom_dict = {} - - + # Override component BoM info on top of footprint info for key in comp_bom_dict: fp_bom_dict[key] = comp_bom_dict[key] @@ -79,78 +75,75 @@ def natural_key(string_): try: dnp = components_dict[refdef]['bom']['dnp'] except: - dnp = False + dnp = False if dnp == True: description = dnp_text - + if description not in bom_dict: bom_dict[description] = fp_bom_dict bom_dict[description]['refdefs'] = [] bom_dict[description]['refdefs'].append(refdef) - try: bom_content = config.brd['bom'] except: bom_content = [ { - "field": "line-item", - "text": "#" + "field": "line-item", + "text": "#" }, { - "field": "quantity", - "text": "Qty" + "field": "quantity", + "text": "Qty" }, { - "field": "designators", - "text": "Designators" - }, + "field": "designators", + "text": "Designators" + }, { - "field": "description", - "text": "Description" - }, + "field": "description", + "text": "Description" + }, { - "field": "package", - "text": "Package" - }, + "field": "package", + "text": "Package" + }, { - "field": "manufacturer", - "text": "Manufacturer" - }, + "field": "manufacturer", + "text": "Manufacturer" + }, { - "field": "part-number", - "text": "Part #" + "field": "part-number", + "text": "Part #" }, { - "field": "suppliers", - "text": "Suppliers", - "suppliers": - [ - { - "field": "farnell", - "text": "Farnell #", - "search-url": "http://uk.farnell.com/catalog/Search?st=" - }, - { - "field": "mouser", - "text": "Mouser #", - "search-url": "http://uk.mouser.com/Search/Refine.aspx?Keyword=" - }, - { - "field": "octopart", - "text": "Octopart", - "search-url": "https://octopart.com/search?q=" - } - ] + "field": "suppliers", + "text": "Suppliers", + "suppliers": + [ + { + "field": "farnell", + "text": "Farnell #", + "search-url": "http://uk.farnell.com/catalog/Search?st=" + }, + { + "field": "mouser", + "text": "Mouser #", + "search-url": "http://uk.mouser.com/Search/Refine.aspx?Keyword=" + }, + { + "field": "octopart", + "text": "Octopart", + "search-url": "https://octopart.com/search?q=" + } + ] }, { - "field": "notes", - "text": "Notes" + "field": "notes", + "text": "Notes" } - ] - - + ] # Set up the BoM file name bom_path = os.path.join(config.cfg['base-dir'], @@ -163,14 +156,12 @@ def natural_key(string_): board_revision = config.brd['config'].get('rev') base_name = "%s_rev_%s" % (board_name, board_revision) - bom_html = os.path.join(bom_path, base_name + '_%s.html'% 'bom') - bom_csv = os.path.join(bom_path, base_name + '_%s.csv'% 'bom') - + bom_html = os.path.join(bom_path, base_name + '_%s.html' % 'bom') + bom_csv = os.path.join(bom_path, base_name + '_%s.csv' % 'bom') html = [] csv = [] - html.append('') html.append('