From a6b89dbf3a35a78f2ecbe9322143dbb07479820a Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Sun, 6 Oct 2019 15:56:41 +0300 Subject: [PATCH] - moved back the ApertureMacro class to camlib for now and made some import changes in the new ParseGerber and ParseExcellon classes - some changes to the tests - perhaps I will try adding a few tests in the future --- FlatCAMApp.py | 10 +- README.md | 2 + camlib.py | 375 +++++++++++++++- flatcamParsers/ParseExcellon.py | 28 +- flatcamParsers/ParseGerber.py | 424 ++---------------- tests/frameless_window.py | 34 +- .../gerber_parsing_line_profile_1.py | 2 +- .../gerber_parsing_profile_1.py | 2 +- tests/new_window_test.py | 5 + tests/other/destructor_test.py | 8 +- tests/other/profile_gerber_parser.py | 2 +- tests/other/test_excellon_1.py | 11 +- tests/other/test_plotg.py | 3 +- tests/test_excellon.py | 51 +-- tests/test_excellon_flow.py | 34 +- tests/test_gerber_buffer.py | 7 +- tests/test_gerber_flow.py | 66 +-- tests/test_paint.py | 1 + tests/test_pathconnect.py | 3 +- tests/test_polygon_paint.py | 13 +- tests/test_svg_flow.py | 48 +- tests/test_tcl_shell.py | 79 ++-- 22 files changed, 646 insertions(+), 562 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 870d5d1b..1605f6fa 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -52,7 +52,7 @@ from flatcamEditors.FlatCAMTextEditor import TextEditor from FlatCAMProcess import * from FlatCAMWorkerStack import WorkerStack -from flatcamGUI.VisPyVisuals import Color +# from flatcamGUI.VisPyVisuals import Color from vispy.gloo.util import _screenshot from vispy.io import write_png @@ -71,12 +71,12 @@ fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: _ = gettext.gettext -# ######################################## -# # App ### -# ######################################## - class App(QtCore.QObject): + # ######################################## + # # App ### + # ######################################## + """ The main application class. The constructor starts the GUI. """ diff --git a/README.md b/README.md index 1c36314f..8f97cbd1 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,8 @@ CAD program, and create G-Code for Isolation routing. - made the Rules Check Tool document window Read Only - made Excellon and Gerber classes from camlib into their own files in the flatcamParser folder - moved the ApertureMacro class from camlib to ParseGerber file +- moved back the ApertureMacro class to camlib for now and made some import changes in the new ParseGerber and ParseExcellon classes +- some changes to the tests - perhaps I will try adding a few tests in the future 5.10.2019 diff --git a/camlib.py b/camlib.py index e4c7083d..6d49633b 100644 --- a/camlib.py +++ b/camlib.py @@ -49,7 +49,7 @@ import ezdxf # TODO: Commented for FlatCAM packaging with cx_freeze # from scipy.spatial import KDTree, Delaunay # from scipy.spatial import Delaunay -from flatcamParsers.ParseGerber import ApertureMacro + from flatcamParsers.ParseSVG import * from flatcamParsers.ParseDXF import * @@ -82,6 +82,379 @@ class ParseError(Exception): pass +class ApertureMacro: + """ + Syntax of aperture macros. + + : AM* + : {{*}{*}} + : $K= + : ,{,}| + : $M|< Arithmetic expression> + : 0 + """ + + # ## Regular expressions + am1_re = re.compile(r'^%AM([^\*]+)\*(.+)?(%)?$') + am2_re = re.compile(r'(.*)%$') + amcomm_re = re.compile(r'^0(.*)') + amprim_re = re.compile(r'^[1-9].*') + amvar_re = re.compile(r'^\$([0-9a-zA-z]+)=(.*)') + + def __init__(self, name=None): + self.name = name + self.raw = "" + + # ## These below are recomputed for every aperture + # ## definition, in other words, are temporary variables. + self.primitives = [] + self.locvars = {} + self.geometry = None + + def to_dict(self): + """ + Returns the object in a serializable form. Only the name and + raw are required. + + :return: Dictionary representing the object. JSON ready. + :rtype: dict + """ + + return { + 'name': self.name, + 'raw': self.raw + } + + def from_dict(self, d): + """ + Populates the object from a serial representation created + with ``self.to_dict()``. + + :param d: Serial representation of an ApertureMacro object. + :return: None + """ + for attr in ['name', 'raw']: + setattr(self, attr, d[attr]) + + def parse_content(self): + """ + Creates numerical lists for all primitives in the aperture + macro (in ``self.raw``) by replacing all variables by their + values iteratively and evaluating expressions. Results + are stored in ``self.primitives``. + + :return: None + """ + # Cleanup + self.raw = self.raw.replace('\n', '').replace('\r', '').strip(" *") + self.primitives = [] + + # Separate parts + parts = self.raw.split('*') + + # ### Every part in the macro #### + for part in parts: + # ## Comments. Ignored. + match = ApertureMacro.amcomm_re.search(part) + if match: + continue + + # ## Variables + # These are variables defined locally inside the macro. They can be + # numerical constant or defind in terms of previously define + # variables, which can be defined locally or in an aperture + # definition. All replacements ocurr here. + match = ApertureMacro.amvar_re.search(part) + if match: + var = match.group(1) + val = match.group(2) + + # Replace variables in value + for v in self.locvars: + # replaced the following line with the next to fix Mentor custom apertures not parsed OK + # val = re.sub((r'\$'+str(v)+r'(?![0-9a-zA-Z])'), str(self.locvars[v]), val) + val = val.replace('$' + str(v), str(self.locvars[v])) + + # Make all others 0 + val = re.sub(r'\$[0-9a-zA-Z](?![0-9a-zA-Z])', "0", val) + # Change x with * + val = re.sub(r'[xX]', "*", val) + + # Eval() and store. + self.locvars[var] = eval(val) + continue + + # ## Primitives + # Each is an array. The first identifies the primitive, while the + # rest depend on the primitive. All are strings representing a + # number and may contain variable definition. The values of these + # variables are defined in an aperture definition. + match = ApertureMacro.amprim_re.search(part) + if match: + # ## Replace all variables + for v in self.locvars: + # replaced the following line with the next to fix Mentor custom apertures not parsed OK + # part = re.sub(r'\$' + str(v) + r'(?![0-9a-zA-Z])', str(self.locvars[v]), part) + part = part.replace('$' + str(v), str(self.locvars[v])) + + # Make all others 0 + part = re.sub(r'\$[0-9a-zA-Z](?![0-9a-zA-Z])', "0", part) + + # Change x with * + part = re.sub(r'[xX]', "*", part) + + # ## Store + elements = part.split(",") + self.primitives.append([eval(x) for x in elements]) + continue + + log.warning("Unknown syntax of aperture macro part: %s" % str(part)) + + def append(self, data): + """ + Appends a string to the raw macro. + + :param data: Part of the macro. + :type data: str + :return: None + """ + self.raw += data + + @staticmethod + def default2zero(n, mods): + """ + Pads the ``mods`` list with zeros resulting in an + list of length n. + + :param n: Length of the resulting list. + :type n: int + :param mods: List to be padded. + :type mods: list + :return: Zero-padded list. + :rtype: list + """ + x = [0.0] * n + na = len(mods) + x[0:na] = mods + return x + + @staticmethod + def make_circle(mods): + """ + + :param mods: (Exposure 0/1, Diameter >=0, X-coord, Y-coord) + :return: + """ + + pol, dia, x, y = ApertureMacro.default2zero(4, mods) + + return {"pol": int(pol), "geometry": Point(x, y).buffer(dia/2)} + + @staticmethod + def make_vectorline(mods): + """ + + :param mods: (Exposure 0/1, Line width >= 0, X-start, Y-start, X-end, Y-end, + rotation angle around origin in degrees) + :return: + """ + pol, width, xs, ys, xe, ye, angle = ApertureMacro.default2zero(7, mods) + + line = LineString([(xs, ys), (xe, ye)]) + box = line.buffer(width/2, cap_style=2) + box_rotated = affinity.rotate(box, angle, origin=(0, 0)) + + return {"pol": int(pol), "geometry": box_rotated} + + @staticmethod + def make_centerline(mods): + """ + + :param mods: (Exposure 0/1, width >=0, height >=0, x-center, y-center, + rotation angle around origin in degrees) + :return: + """ + + pol, width, height, x, y, angle = ApertureMacro.default2zero(6, mods) + + box = shply_box(x-width/2, y-height/2, x+width/2, y+height/2) + box_rotated = affinity.rotate(box, angle, origin=(0, 0)) + + return {"pol": int(pol), "geometry": box_rotated} + + @staticmethod + def make_lowerleftline(mods): + """ + + :param mods: (exposure 0/1, width >=0, height >=0, x-lowerleft, y-lowerleft, + rotation angle around origin in degrees) + :return: + """ + + pol, width, height, x, y, angle = ApertureMacro.default2zero(6, mods) + + box = shply_box(x, y, x+width, y+height) + box_rotated = affinity.rotate(box, angle, origin=(0, 0)) + + return {"pol": int(pol), "geometry": box_rotated} + + @staticmethod + def make_outline(mods): + """ + + :param mods: + :return: + """ + + pol = mods[0] + n = mods[1] + points = [(0, 0)]*(n+1) + + for i in range(n+1): + points[i] = mods[2*i + 2:2*i + 4] + + angle = mods[2*n + 4] + + poly = Polygon(points) + poly_rotated = affinity.rotate(poly, angle, origin=(0, 0)) + + return {"pol": int(pol), "geometry": poly_rotated} + + @staticmethod + def make_polygon(mods): + """ + Note: Specs indicate that rotation is only allowed if the center + (x, y) == (0, 0). I will tolerate breaking this rule. + + :param mods: (exposure 0/1, n_verts 3<=n<=12, x-center, y-center, + diameter of circumscribed circle >=0, rotation angle around origin) + :return: + """ + + pol, nverts, x, y, dia, angle = ApertureMacro.default2zero(6, mods) + points = [(0, 0)]*nverts + + for i in range(nverts): + points[i] = (x + 0.5 * dia * cos(2*pi * i/nverts), + y + 0.5 * dia * sin(2*pi * i/nverts)) + + poly = Polygon(points) + poly_rotated = affinity.rotate(poly, angle, origin=(0, 0)) + + return {"pol": int(pol), "geometry": poly_rotated} + + @staticmethod + def make_moire(mods): + """ + Note: Specs indicate that rotation is only allowed if the center + (x, y) == (0, 0). I will tolerate breaking this rule. + + :param mods: (x-center, y-center, outer_dia_outer_ring, ring thickness, + gap, max_rings, crosshair_thickness, crosshair_len, rotation + angle around origin in degrees) + :return: + """ + + x, y, dia, thickness, gap, nrings, cross_th, cross_len, angle = ApertureMacro.default2zero(9, mods) + + r = dia/2 - thickness/2 + result = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0) + ring = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0) # Need a copy! + + i = 1 # Number of rings created so far + + # ## If the ring does not have an interior it means that it is + # ## a disk. Then stop. + while len(ring.interiors) > 0 and i < nrings: + r -= thickness + gap + if r <= 0: + break + ring = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0) + result = cascaded_union([result, ring]) + i += 1 + + # ## Crosshair + hor = LineString([(x - cross_len, y), (x + cross_len, y)]).buffer(cross_th/2.0, cap_style=2) + ver = LineString([(x, y-cross_len), (x, y + cross_len)]).buffer(cross_th/2.0, cap_style=2) + result = cascaded_union([result, hor, ver]) + + return {"pol": 1, "geometry": result} + + @staticmethod + def make_thermal(mods): + """ + Note: Specs indicate that rotation is only allowed if the center + (x, y) == (0, 0). I will tolerate breaking this rule. + + :param mods: [x-center, y-center, diameter-outside, diameter-inside, + gap-thickness, rotation angle around origin] + :return: + """ + + x, y, dout, din, t, angle = ApertureMacro.default2zero(6, mods) + + ring = Point((x, y)).buffer(dout/2.0).difference(Point((x, y)).buffer(din/2.0)) + hline = LineString([(x - dout/2.0, y), (x + dout/2.0, y)]).buffer(t/2.0, cap_style=3) + vline = LineString([(x, y - dout/2.0), (x, y + dout/2.0)]).buffer(t/2.0, cap_style=3) + thermal = ring.difference(hline.union(vline)) + + return {"pol": 1, "geometry": thermal} + + def make_geometry(self, modifiers): + """ + Runs the macro for the given modifiers and generates + the corresponding geometry. + + :param modifiers: Modifiers (parameters) for this macro + :type modifiers: list + :return: Shapely geometry + :rtype: shapely.geometry.polygon + """ + + # ## Primitive makers + makers = { + "1": ApertureMacro.make_circle, + "2": ApertureMacro.make_vectorline, + "20": ApertureMacro.make_vectorline, + "21": ApertureMacro.make_centerline, + "22": ApertureMacro.make_lowerleftline, + "4": ApertureMacro.make_outline, + "5": ApertureMacro.make_polygon, + "6": ApertureMacro.make_moire, + "7": ApertureMacro.make_thermal + } + + # ## Store modifiers as local variables + modifiers = modifiers or [] + modifiers = [float(m) for m in modifiers] + self.locvars = {} + for i in range(0, len(modifiers)): + self.locvars[str(i + 1)] = modifiers[i] + + # ## Parse + self.primitives = [] # Cleanup + self.geometry = Polygon() + self.parse_content() + + # ## Make the geometry + for primitive in self.primitives: + # Make the primitive + prim_geo = makers[str(int(primitive[0]))](primitive[1:]) + + # Add it (according to polarity) + # if self.geometry is None and prim_geo['pol'] == 1: + # self.geometry = prim_geo['geometry'] + # continue + if prim_geo['pol'] == 1: + self.geometry = self.geometry.union(prim_geo['geometry']) + continue + if prim_geo['pol'] == 0: + self.geometry = self.geometry.difference(prim_geo['geometry']) + continue + + return self.geometry + + class Geometry(object): """ Base geometry class. diff --git a/flatcamParsers/ParseExcellon.py b/flatcamParsers/ParseExcellon.py index c66140b7..457150be 100644 --- a/flatcamParsers/ParseExcellon.py +++ b/flatcamParsers/ParseExcellon.py @@ -1,8 +1,32 @@ -from camlib import * +from camlib import Geometry +import FlatCAMApp + +import FlatCAMTranslation as fcTranslate + +from shapely.geometry import Polygon, Point, LineString, MultiPolygon +from shapely.ops import cascaded_union +import shapely.affinity as affinity + +import re +import traceback +import gettext +import builtins +import numpy as np +from numpy import Inf + +import logging if '_' not in builtins.__dict__: _ = gettext.gettext +log = logging.getLogger('base2') +log.setLevel(logging.DEBUG) + +formatter = logging.Formatter('[%(levelname)s] %(message)s') +handler = logging.StreamHandler() +handler.setFormatter(formatter) +log.addHandler(handler) + class Excellon(Geometry): """ @@ -265,7 +289,7 @@ class Excellon(Geometry): line_units = '' - #### Parsing starts here ## ## + # ## Parsing starts here ## ## line_num = 0 # Line number eline = "" try: diff --git a/flatcamParsers/ParseGerber.py b/flatcamParsers/ParseGerber.py index bfa611ac..0012bf11 100644 --- a/flatcamParsers/ParseGerber.py +++ b/flatcamParsers/ParseGerber.py @@ -1,9 +1,35 @@ +from camlib import Geometry, ApertureMacro, parse_gerber_number, arc, arctan2, arc_angle +import FlatCAMApp +import FlatCAMTranslation as fcTranslate -from camlib import * +from shapely.geometry import Polygon, Point, LineString, MultiPolygon +from shapely.ops import cascaded_union +import shapely.affinity as affinity +from shapely.geometry import box as shply_box +import re +import traceback +from copy import deepcopy + +import gettext +import builtins + +import numpy as np +from numpy import Inf +from math import sqrt, pi, sin, cos +import sys + +import logging if '_' not in builtins.__dict__: _ = gettext.gettext +log = logging.getLogger('base2') +log.setLevel(logging.DEBUG) + +formatter = logging.Formatter('[%(levelname)s] %(message)s') +handler = logging.StreamHandler() +handler.setFormatter(formatter) +log.addHandler(handler) class Gerber(Geometry): """ @@ -1134,25 +1160,25 @@ class Gerber(Geometry): try: circular_x = parse_gerber_number(circular_x, self.int_digits, self.frac_digits, self.gerber_zeros) - except: + except Exception as e: circular_x = current_x try: circular_y = parse_gerber_number(circular_y, self.int_digits, self.frac_digits, self.gerber_zeros) - except: + except Exception as e: circular_y = current_y # According to Gerber specification i and j are not modal, which means that when i or j are missing, # they are to be interpreted as being zero try: i = parse_gerber_number(i, self.int_digits, self.frac_digits, self.gerber_zeros) - except: + except Exception as e: i = 0 try: j = parse_gerber_number(j, self.int_digits, self.frac_digits, self.gerber_zeros) - except: + except Exception as e: j = 0 if quadrant_mode is None: @@ -1598,6 +1624,7 @@ class Gerber(Geometry): :type xfactor: float :param yfactor: Number by which to scale on Y axis. :type yfactor: float + :param point: reference point for scaling operation :rtype : None """ log.debug("camlib.Gerber.scale()") @@ -1628,7 +1655,7 @@ class Gerber(Geometry): # variables to display the percentage of work done self.geo_len = 0 try: - for g in self.solid_geometry: + for __ in self.solid_geometry: self.geo_len += 1 except TypeError: self.geo_len = 1 @@ -1717,7 +1744,7 @@ class Gerber(Geometry): # variables to display the percentage of work done self.geo_len = 0 try: - for g in self.solid_geometry: + for __ in self.solid_geometry: self.geo_len += 1 except TypeError: self.geo_len = 1 @@ -1796,7 +1823,7 @@ class Gerber(Geometry): # variables to display the percentage of work done self.geo_len = 0 try: - for g in self.solid_geometry: + for __ in self.solid_geometry: self.geo_len += 1 except TypeError: self.geo_len = 1 @@ -1857,6 +1884,10 @@ class Gerber(Geometry): See shapely manual for more information: http://toblerity.org/shapely/manual.html#affine-transformations + :param angle_x: the angle on X axis for skewing + :param angle_y: the angle on Y axis for skewing + :param point: reference point for skewing operation + :return None """ log.debug("camlib.Gerber.skew()") @@ -1865,7 +1896,7 @@ class Gerber(Geometry): # variables to display the percentage of work done self.geo_len = 0 try: - for g in self.solid_geometry: + for __ in self.solid_geometry: self.geo_len += 1 except TypeError: self.geo_len = 1 @@ -1926,7 +1957,7 @@ class Gerber(Geometry): # variables to display the percentage of work done self.geo_len = 0 try: - for g in self.solid_geometry: + for __ in self.solid_geometry: self.geo_len += 1 except TypeError: self.geo_len = 1 @@ -1972,376 +2003,3 @@ class Gerber(Geometry): self.app.inform.emit('[success] %s' % _("Gerber Rotate done.")) self.app.proc_container.new_text = '' - - -class ApertureMacro: - """ - Syntax of aperture macros. - - : AM* - : {{*}{*}} - : $K= - : ,{,}| - : $M|< Arithmetic expression> - : 0 - """ - - # ## Regular expressions - am1_re = re.compile(r'^%AM([^\*]+)\*(.+)?(%)?$') - am2_re = re.compile(r'(.*)%$') - amcomm_re = re.compile(r'^0(.*)') - amprim_re = re.compile(r'^[1-9].*') - amvar_re = re.compile(r'^\$([0-9a-zA-z]+)=(.*)') - - def __init__(self, name=None): - self.name = name - self.raw = "" - - # ## These below are recomputed for every aperture - # ## definition, in other words, are temporary variables. - self.primitives = [] - self.locvars = {} - self.geometry = None - - def to_dict(self): - """ - Returns the object in a serializable form. Only the name and - raw are required. - - :return: Dictionary representing the object. JSON ready. - :rtype: dict - """ - - return { - 'name': self.name, - 'raw': self.raw - } - - def from_dict(self, d): - """ - Populates the object from a serial representation created - with ``self.to_dict()``. - - :param d: Serial representation of an ApertureMacro object. - :return: None - """ - for attr in ['name', 'raw']: - setattr(self, attr, d[attr]) - - def parse_content(self): - """ - Creates numerical lists for all primitives in the aperture - macro (in ``self.raw``) by replacing all variables by their - values iteratively and evaluating expressions. Results - are stored in ``self.primitives``. - - :return: None - """ - # Cleanup - self.raw = self.raw.replace('\n', '').replace('\r', '').strip(" *") - self.primitives = [] - - # Separate parts - parts = self.raw.split('*') - - # ### Every part in the macro #### - for part in parts: - # ## Comments. Ignored. - match = ApertureMacro.amcomm_re.search(part) - if match: - continue - - # ## Variables - # These are variables defined locally inside the macro. They can be - # numerical constant or defind in terms of previously define - # variables, which can be defined locally or in an aperture - # definition. All replacements ocurr here. - match = ApertureMacro.amvar_re.search(part) - if match: - var = match.group(1) - val = match.group(2) - - # Replace variables in value - for v in self.locvars: - # replaced the following line with the next to fix Mentor custom apertures not parsed OK - # val = re.sub((r'\$'+str(v)+r'(?![0-9a-zA-Z])'), str(self.locvars[v]), val) - val = val.replace('$' + str(v), str(self.locvars[v])) - - # Make all others 0 - val = re.sub(r'\$[0-9a-zA-Z](?![0-9a-zA-Z])', "0", val) - # Change x with * - val = re.sub(r'[xX]', "*", val) - - # Eval() and store. - self.locvars[var] = eval(val) - continue - - # ## Primitives - # Each is an array. The first identifies the primitive, while the - # rest depend on the primitive. All are strings representing a - # number and may contain variable definition. The values of these - # variables are defined in an aperture definition. - match = ApertureMacro.amprim_re.search(part) - if match: - # ## Replace all variables - for v in self.locvars: - # replaced the following line with the next to fix Mentor custom apertures not parsed OK - # part = re.sub(r'\$' + str(v) + r'(?![0-9a-zA-Z])', str(self.locvars[v]), part) - part = part.replace('$' + str(v), str(self.locvars[v])) - - # Make all others 0 - part = re.sub(r'\$[0-9a-zA-Z](?![0-9a-zA-Z])', "0", part) - - # Change x with * - part = re.sub(r'[xX]', "*", part) - - # ## Store - elements = part.split(",") - self.primitives.append([eval(x) for x in elements]) - continue - - log.warning("Unknown syntax of aperture macro part: %s" % str(part)) - - def append(self, data): - """ - Appends a string to the raw macro. - - :param data: Part of the macro. - :type data: str - :return: None - """ - self.raw += data - - @staticmethod - def default2zero(n, mods): - """ - Pads the ``mods`` list with zeros resulting in an - list of length n. - - :param n: Length of the resulting list. - :type n: int - :param mods: List to be padded. - :type mods: list - :return: Zero-padded list. - :rtype: list - """ - x = [0.0] * n - na = len(mods) - x[0:na] = mods - return x - - @staticmethod - def make_circle(mods): - """ - - :param mods: (Exposure 0/1, Diameter >=0, X-coord, Y-coord) - :return: - """ - - pol, dia, x, y = ApertureMacro.default2zero(4, mods) - - return {"pol": int(pol), "geometry": Point(x, y).buffer(dia/2)} - - @staticmethod - def make_vectorline(mods): - """ - - :param mods: (Exposure 0/1, Line width >= 0, X-start, Y-start, X-end, Y-end, - rotation angle around origin in degrees) - :return: - """ - pol, width, xs, ys, xe, ye, angle = ApertureMacro.default2zero(7, mods) - - line = LineString([(xs, ys), (xe, ye)]) - box = line.buffer(width/2, cap_style=2) - box_rotated = affinity.rotate(box, angle, origin=(0, 0)) - - return {"pol": int(pol), "geometry": box_rotated} - - @staticmethod - def make_centerline(mods): - """ - - :param mods: (Exposure 0/1, width >=0, height >=0, x-center, y-center, - rotation angle around origin in degrees) - :return: - """ - - pol, width, height, x, y, angle = ApertureMacro.default2zero(6, mods) - - box = shply_box(x-width/2, y-height/2, x+width/2, y+height/2) - box_rotated = affinity.rotate(box, angle, origin=(0, 0)) - - return {"pol": int(pol), "geometry": box_rotated} - - @staticmethod - def make_lowerleftline(mods): - """ - - :param mods: (exposure 0/1, width >=0, height >=0, x-lowerleft, y-lowerleft, - rotation angle around origin in degrees) - :return: - """ - - pol, width, height, x, y, angle = ApertureMacro.default2zero(6, mods) - - box = shply_box(x, y, x+width, y+height) - box_rotated = affinity.rotate(box, angle, origin=(0, 0)) - - return {"pol": int(pol), "geometry": box_rotated} - - @staticmethod - def make_outline(mods): - """ - - :param mods: - :return: - """ - - pol = mods[0] - n = mods[1] - points = [(0, 0)]*(n+1) - - for i in range(n+1): - points[i] = mods[2*i + 2:2*i + 4] - - angle = mods[2*n + 4] - - poly = Polygon(points) - poly_rotated = affinity.rotate(poly, angle, origin=(0, 0)) - - return {"pol": int(pol), "geometry": poly_rotated} - - @staticmethod - def make_polygon(mods): - """ - Note: Specs indicate that rotation is only allowed if the center - (x, y) == (0, 0). I will tolerate breaking this rule. - - :param mods: (exposure 0/1, n_verts 3<=n<=12, x-center, y-center, - diameter of circumscribed circle >=0, rotation angle around origin) - :return: - """ - - pol, nverts, x, y, dia, angle = ApertureMacro.default2zero(6, mods) - points = [(0, 0)]*nverts - - for i in range(nverts): - points[i] = (x + 0.5 * dia * cos(2*pi * i/nverts), - y + 0.5 * dia * sin(2*pi * i/nverts)) - - poly = Polygon(points) - poly_rotated = affinity.rotate(poly, angle, origin=(0, 0)) - - return {"pol": int(pol), "geometry": poly_rotated} - - @staticmethod - def make_moire(mods): - """ - Note: Specs indicate that rotation is only allowed if the center - (x, y) == (0, 0). I will tolerate breaking this rule. - - :param mods: (x-center, y-center, outer_dia_outer_ring, ring thickness, - gap, max_rings, crosshair_thickness, crosshair_len, rotation - angle around origin in degrees) - :return: - """ - - x, y, dia, thickness, gap, nrings, cross_th, cross_len, angle = ApertureMacro.default2zero(9, mods) - - r = dia/2 - thickness/2 - result = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0) - ring = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0) # Need a copy! - - i = 1 # Number of rings created so far - - # ## If the ring does not have an interior it means that it is - # ## a disk. Then stop. - while len(ring.interiors) > 0 and i < nrings: - r -= thickness + gap - if r <= 0: - break - ring = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0) - result = cascaded_union([result, ring]) - i += 1 - - # ## Crosshair - hor = LineString([(x - cross_len, y), (x + cross_len, y)]).buffer(cross_th/2.0, cap_style=2) - ver = LineString([(x, y-cross_len), (x, y + cross_len)]).buffer(cross_th/2.0, cap_style=2) - result = cascaded_union([result, hor, ver]) - - return {"pol": 1, "geometry": result} - - @staticmethod - def make_thermal(mods): - """ - Note: Specs indicate that rotation is only allowed if the center - (x, y) == (0, 0). I will tolerate breaking this rule. - - :param mods: [x-center, y-center, diameter-outside, diameter-inside, - gap-thickness, rotation angle around origin] - :return: - """ - - x, y, dout, din, t, angle = ApertureMacro.default2zero(6, mods) - - ring = Point((x, y)).buffer(dout/2.0).difference(Point((x, y)).buffer(din/2.0)) - hline = LineString([(x - dout/2.0, y), (x + dout/2.0, y)]).buffer(t/2.0, cap_style=3) - vline = LineString([(x, y - dout/2.0), (x, y + dout/2.0)]).buffer(t/2.0, cap_style=3) - thermal = ring.difference(hline.union(vline)) - - return {"pol": 1, "geometry": thermal} - - def make_geometry(self, modifiers): - """ - Runs the macro for the given modifiers and generates - the corresponding geometry. - - :param modifiers: Modifiers (parameters) for this macro - :type modifiers: list - :return: Shapely geometry - :rtype: shapely.geometry.polygon - """ - - # ## Primitive makers - makers = { - "1": ApertureMacro.make_circle, - "2": ApertureMacro.make_vectorline, - "20": ApertureMacro.make_vectorline, - "21": ApertureMacro.make_centerline, - "22": ApertureMacro.make_lowerleftline, - "4": ApertureMacro.make_outline, - "5": ApertureMacro.make_polygon, - "6": ApertureMacro.make_moire, - "7": ApertureMacro.make_thermal - } - - # ## Store modifiers as local variables - modifiers = modifiers or [] - modifiers = [float(m) for m in modifiers] - self.locvars = {} - for i in range(0, len(modifiers)): - self.locvars[str(i + 1)] = modifiers[i] - - # ## Parse - self.primitives = [] # Cleanup - self.geometry = Polygon() - self.parse_content() - - # ## Make the geometry - for primitive in self.primitives: - # Make the primitive - prim_geo = makers[str(int(primitive[0]))](primitive[1:]) - - # Add it (according to polarity) - # if self.geometry is None and prim_geo['pol'] == 1: - # self.geometry = prim_geo['geometry'] - # continue - if prim_geo['pol'] == 1: - self.geometry = self.geometry.union(prim_geo['geometry']) - continue - if prim_geo['pol'] == 0: - self.geometry = self.geometry.difference(prim_geo['geometry']) - continue - - return self.geometry diff --git a/tests/frameless_window.py b/tests/frameless_window.py index 75ae9cb7..47e28b31 100644 --- a/tests/frameless_window.py +++ b/tests/frameless_window.py @@ -3,10 +3,11 @@ from PyQt5 import QtGui, QtWidgets from PyQt5 import QtCore from PyQt5.QtCore import Qt + class TitleBar(QtWidgets.QDialog): def __init__(self, parent=None): QtWidgets.QWidget.__init__(self, parent) - self.setWindowFlags(Qt.FramelessWindowHint); + self.setWindowFlags(Qt.FramelessWindowHint) css = """ QWidget{ Background: #AA00AA; @@ -60,19 +61,19 @@ class TitleBar(QtWidgets.QDialog): self.maximize.clicked.connect(self.showMaxRestore) def showSmall(self): - box.showMinimized(); + box.showMinimized() def showMaxRestore(self): if(self.maxNormal): - box.showNormal(); - self.maxNormal= False; - self.maximize.setIcon(QtGui.QIcon('img/max.png')); + box.showNormal() + self.maxNormal= False + self.maximize.setIcon(QtGui.QIcon('img/max.png')) print(1) else: - box.showMaximized(); - self.maxNormal= True; + box.showMaximized() + self.maxNormal= True print(2) - self.maximize.setIcon(QtGui.QIcon('img/max2.png')); + self.maximize.setIcon(QtGui.QIcon('img/max2.png')) def close(self): box.close() @@ -88,7 +89,7 @@ class TitleBar(QtWidgets.QDialog): class Frame(QtWidgets.QFrame): def __init__(self, parent=None): QtWidgets.QFrame.__init__(self, parent) - self.m_mouse_down= False; + self.m_mouse_down= False self.setFrameShape(QtWidgets.QFrame.StyledPanel) css = """ QFrame{ @@ -122,25 +123,26 @@ class Frame(QtWidgets.QFrame): return self.m_titleBar def mousePressEvent(self,event): - self.m_old_pos = event.pos(); - self.m_mouse_down = event.button()== Qt.LeftButton; + self.m_old_pos = event.pos() + self.m_mouse_down = event.button()== Qt.LeftButton def mouseMoveEvent(self,event): x=event.x() y=event.y() def mouseReleaseEvent(self,event): - m_mouse_down=False; + m_mouse_down=False + if __name__ == '__main__': - app = QtWidgets.QApplication(sys.argv); + app = QtWidgets.QApplication(sys.argv) box = Frame() box.move(60,60) - l=QtWidgets.QVBoxLayout(box.contentWidget()); + l = QtWidgets.QVBoxLayout(box.contentWidget()) l.setContentsMargins(0, 0,0 ,0) - edit=QtWidgets.QLabel("""I would've did anything for you to show you how much I adored you + edit = QtWidgets.QLabel("""I would've did anything for you to show you how much I adored you But it's over now, it's too late to save our loveJust promise me you'll think of me -Every time you look up in the sky and see a star 'cuz I'm your star."""); +Every time you look up in the sky and see a star 'cuz I'm your star.""") l.addWidget(edit) box.show() app.exec_() \ No newline at end of file diff --git a/tests/gerber_parsing_profiling/gerber_parsing_line_profile_1.py b/tests/gerber_parsing_profiling/gerber_parsing_line_profile_1.py index 421714eb..1ff32fb0 100644 --- a/tests/gerber_parsing_profiling/gerber_parsing_line_profile_1.py +++ b/tests/gerber_parsing_profiling/gerber_parsing_line_profile_1.py @@ -4,7 +4,7 @@ import sys sys.path.append('../../') -from camlib import * +from flatcamParsers.ParseGerber import * log = logging.getLogger('base2') log.setLevel(logging.WARNING) diff --git a/tests/gerber_parsing_profiling/gerber_parsing_profile_1.py b/tests/gerber_parsing_profiling/gerber_parsing_profile_1.py index 0c3ff2d0..60e7d02a 100644 --- a/tests/gerber_parsing_profiling/gerber_parsing_profile_1.py +++ b/tests/gerber_parsing_profiling/gerber_parsing_profile_1.py @@ -3,7 +3,7 @@ import pstats import sys sys.path.append('../../') -from camlib import * +from flatcamParsers.ParseGerber import * log = logging.getLogger('base2') log.setLevel(logging.WARNING) diff --git a/tests/new_window_test.py b/tests/new_window_test.py index 442db9e7..9d0eb4d8 100644 --- a/tests/new_window_test.py +++ b/tests/new_window_test.py @@ -2,6 +2,7 @@ import sys from PyQt5.Qt import * from PyQt5 import QtGui, QtWidgets + class MyPopup(QWidget): def __init__(self): QWidget.__init__(self) @@ -30,6 +31,7 @@ class MyPopup(QWidget): # dc.drawLine(0, 0, 100, 100) # dc.drawLine(100, 0, 0, 100) + class MainWindow(QMainWindow): def __init__(self, *args): QtWidgets.QMainWindow.__init__(self, *args) @@ -46,6 +48,7 @@ class MainWindow(QMainWindow): self.w.setGeometry(QRect(100, 100, 400, 200)) self.w.show() + class App(QApplication): def __init__(self, *args): QtWidgets.QApplication.__init__(self, *args) @@ -56,10 +59,12 @@ class App(QApplication): def byebye(self): self.exit(0) + def main(args): global app app = App(args) app.exec_() + if __name__ == "__main__": main(sys.argv) \ No newline at end of file diff --git a/tests/other/destructor_test.py b/tests/other/destructor_test.py index 5ae57ce9..28950277 100644 --- a/tests/other/destructor_test.py +++ b/tests/other/destructor_test.py @@ -1,5 +1,5 @@ import sys -from PyQt4 import QtCore, QtGui +from PyQt5 import QtCore, QtGui, QtWidgets class MyObj(): @@ -16,12 +16,12 @@ def parse(): raise Exception("Intentional Exception") -class Example(QtGui.QWidget): +class Example(QtWidgets.QWidget): def __init__(self): super(Example, self).__init__() - qbtn = QtGui.QPushButton('Raise', self) + qbtn = QtWidgets.QPushButton('Raise', self) qbtn.clicked.connect(parse) self.setWindowTitle('Quit button') @@ -29,6 +29,6 @@ class Example(QtGui.QWidget): if __name__ == '__main__': - app = QtGui.QApplication(sys.argv) + app = QtWidgets.QApplication(sys.argv) ex = Example() sys.exit(app.exec_()) \ No newline at end of file diff --git a/tests/other/profile_gerber_parser.py b/tests/other/profile_gerber_parser.py index ddd1ae16..bdf671c2 100644 --- a/tests/other/profile_gerber_parser.py +++ b/tests/other/profile_gerber_parser.py @@ -1,7 +1,7 @@ import os os.chdir('../') -from camlib import * +from flatcamParsers.ParseGerber import * g = Gerber() g.parse_file(r'C:\Users\jpcaram\Dropbox\CNC\pcbcam\test_files\PlacaReles-F_Cu.gtl') diff --git a/tests/other/test_excellon_1.py b/tests/other/test_excellon_1.py index 5bf065f9..c3895bd7 100644 --- a/tests/other/test_excellon_1.py +++ b/tests/other/test_excellon_1.py @@ -8,12 +8,13 @@ Created on Sun Jan 05 13:30:47 2014 import os os.chdir('../') -from camlib import * -#from matplotlib.figure import Figure +from flatcamParsers.ParseGerber import * +from flatcamParsers.ParseExcellon import * + from matplotlib import pyplot # Gerber. To see if the Excellon is correct -project_dir = "tests/" +project_dir = "tests/gerber_files" gerber_filename = project_dir + "KiCad_Squarer-F_Cu.gtl" g = Gerber() g.parse_file(gerber_filename) @@ -32,7 +33,7 @@ ax.set_aspect(1) # Plot gerber for geo in g.solid_geometry: x, y = geo.exterior.coords.xy - plot(x, y, 'k-') + pyplot.plot(x, y, 'k-') for ints in geo.interiors: x, y = ints.coords.xy ax.plot(x, y, 'k-') @@ -40,7 +41,7 @@ for geo in g.solid_geometry: # Plot excellon for geo in ex.solid_geometry: x, y = geo.exterior.coords.xy - plot(x, y, 'r-') + pyplot.plot(x, y, 'r-') for ints in geo.interiors: x, y = ints.coords.xy ax.plot(x, y, 'g-') diff --git a/tests/other/test_plotg.py b/tests/other/test_plotg.py index 518517d1..3bc438e8 100644 --- a/tests/other/test_plotg.py +++ b/tests/other/test_plotg.py @@ -1,6 +1,7 @@ from shapely.geometry import LineString, Polygon from shapely.ops import cascaded_union, unary_union -from matplotlib.pyplot import plot, subplot, show +from matplotlib.pyplot import plot, subplot, show, axes +from matplotlib.axes import * from camlib import * diff --git a/tests/test_excellon.py b/tests/test_excellon.py index 1a422994..da7e0233 100644 --- a/tests/test_excellon.py +++ b/tests/test_excellon.py @@ -1,5 +1,6 @@ import unittest -import camlib +from flatcamParsers.ParseExcellon import Excellon +from flatcamParsers.ParseGerber import Gerber class ExcellonNumberParseTestInch(unittest.TestCase): @@ -16,39 +17,39 @@ class ExcellonNumberParseTestInch(unittest.TestCase): # of digits you typed and automatically fill in the missing zeros. def test_inch_leading_6digit(self): - excellon = camlib.Excellon() + excellon = Excellon() self.assertEqual(excellon.zeros, "L") self.assertEqual(excellon.parse_number("123456"), 12.3456) def test_inch_leading_5digit(self): - excellon = camlib.Excellon() + excellon = Excellon() self.assertEqual(excellon.parse_number("12345"), 12.345) def test_inch_leading_15digit(self): - excellon = camlib.Excellon() + excellon = Excellon() self.assertEqual(excellon.parse_number("012345"), 1.2345) def test_inch_leading_51digit(self): - excellon = camlib.Excellon() + excellon = Excellon() self.assertEqual(excellon.parse_number("123450"), 12.345) def test_inch_trailing_6digit(self): - excellon = camlib.Excellon() + excellon = Excellon() excellon.zeros = "T" self.assertEqual(excellon.parse_number("123456"), 12.3456) def test_inch_trailing_5digit(self): - excellon = camlib.Excellon() + excellon = Excellon() excellon.zeros = "T" self.assertEqual(excellon.parse_number("12345"), 1.2345) def test_inch_trailing_15digit(self): - excellon = camlib.Excellon() + excellon = Excellon() excellon.zeros = "T" self.assertEqual(excellon.parse_number("012345"), 1.2345) def test_inch_trailing_51digit(self): - excellon = camlib.Excellon() + excellon = Excellon() excellon.zeros = "T" self.assertEqual(excellon.parse_number("123450"), 12.345) @@ -67,45 +68,45 @@ class ExcellonNumberParseTestMetric(unittest.TestCase): # of digits you typed and automatically fill in the missing zeros. def test_inch_leading_6digit(self): - excellon = camlib.Excellon() + excellon = Excellon() excellon.units = "mm" self.assertEqual(excellon.parse_number("123456"), 123.456) def test_inch_leading_5digit(self): - excellon = camlib.Excellon() + excellon = Excellon() excellon.units = "mm" self.assertEqual(excellon.parse_number("12345"), 123.45) def test_inch_leading_15digit(self): - excellon = camlib.Excellon() + excellon = Excellon() excellon.units = "mm" self.assertEqual(excellon.parse_number("012345"), 12.345) def test_inch_leading_51digit(self): - excellon = camlib.Excellon() + excellon = Excellon() excellon.units = "mm" self.assertEqual(excellon.parse_number("123450"), 123.45) def test_inch_trailing_6digit(self): - excellon = camlib.Excellon() + excellon = Excellon() excellon.units = "mm" excellon.zeros = "T" self.assertEqual(excellon.parse_number("123456"), 123.456) def test_inch_trailing_5digit(self): - excellon = camlib.Excellon() + excellon = Excellon() excellon.units = "mm" excellon.zeros = "T" self.assertEqual(excellon.parse_number("12345"), 12.345) def test_inch_trailing_15digit(self): - excellon = camlib.Excellon() + excellon = Excellon() excellon.units = "mm" excellon.zeros = "T" self.assertEqual(excellon.parse_number("012345"), 12.345) def test_inch_trailing_51digit(self): - excellon = camlib.Excellon() + excellon = Excellon() excellon.units = "mm" excellon.zeros = "T" self.assertEqual(excellon.parse_number("123450"), 123.45) @@ -114,7 +115,7 @@ class ExcellonNumberParseTestMetric(unittest.TestCase): class ExcellonFormatM72Test(unittest.TestCase): def setUp(self): - self.excellon = camlib.Excellon() + self.excellon = Excellon() code = """ M48 M72 @@ -141,7 +142,7 @@ class ExcellonFormatM72Test(unittest.TestCase): class ExcellonFormatM71Test(unittest.TestCase): def setUp(self): - self.excellon = camlib.Excellon() + self.excellon = Excellon() code = """ M48 M71 @@ -168,7 +169,7 @@ class ExcellonFormatM71Test(unittest.TestCase): class ExcellonFormatINCHLZTest(unittest.TestCase): def setUp(self): - self.excellon = camlib.Excellon() + self.excellon = Excellon() code = """ M48 INCH,LZ @@ -195,7 +196,7 @@ class ExcellonFormatINCHLZTest(unittest.TestCase): class ExcellonFormatINCHTest(unittest.TestCase): def setUp(self): - self.excellon = camlib.Excellon() + self.excellon = Excellon() code = """ M48 INCH,LZ @@ -222,7 +223,7 @@ class ExcellonFormatINCHTest(unittest.TestCase): class ExcellonFormatINCHTZTest(unittest.TestCase): def setUp(self): - self.excellon = camlib.Excellon() + self.excellon = Excellon() code = """ M48 INCH,TZ @@ -249,7 +250,7 @@ class ExcellonFormatINCHTZTest(unittest.TestCase): class ExcellonFormatMETRICLZTest(unittest.TestCase): def setUp(self): - self.excellon = camlib.Excellon() + self.excellon = Excellon() code = """ M48 METRIC,LZ @@ -276,7 +277,7 @@ class ExcellonFormatMETRICLZTest(unittest.TestCase): class ExcellonFormatMETRICTest(unittest.TestCase): def setUp(self): - self.excellon = camlib.Excellon() + self.excellon = Excellon() code = """ M48 METRIC,LZ @@ -303,7 +304,7 @@ class ExcellonFormatMETRICTest(unittest.TestCase): class ExcellonFormatMETRICTZTest(unittest.TestCase): def setUp(self): - self.excellon = camlib.Excellon() + self.excellon = Excellon() code = """ M48 METRIC,TZ diff --git a/tests/test_excellon_flow.py b/tests/test_excellon_flow.py index 67069e1f..946c2df6 100644 --- a/tests/test_excellon_flow.py +++ b/tests/test_excellon_flow.py @@ -1,9 +1,9 @@ import unittest -from PyQt4 import QtGui +from PyQt5 import QtGui, QtWidgets import sys from FlatCAMApp import App from FlatCAMObj import FlatCAMExcellon, FlatCAMCNCjob -from ObjectUI import ExcellonObjectUI +from flatcamGUI.ObjectUI import ExcellonObjectUI import tempfile import os from time import sleep @@ -21,7 +21,7 @@ class ExcellonFlowTestCase(unittest.TestCase): filename = 'case1.drl' def setUp(self): - self.app = QtGui.QApplication(sys.argv) + self.app = QtWidgets.QApplication(sys.argv) # Create App, keep app defaults (do not load # user-defined defaults). @@ -79,19 +79,19 @@ class ExcellonFlowTestCase(unittest.TestCase): option, value, form_field.get_value() )) - #-------------------------------------------------- + # -------------------------------------------------- # Changes in the GUI should be read in when # running any process. Changing something here. - #-------------------------------------------------- + # -------------------------------------------------- form_field = excellon_obj.form_fields['feedrate'] value = form_field.get_value() form_field.set_value(value * 1.1) # Increase by 10% print(("'feedrate' == {}".format(value))) - #-------------------------------------------------- + # -------------------------------------------------- # Create GCode using all tools. - #-------------------------------------------------- + # -------------------------------------------------- assert isinstance(excellon_obj, FlatCAMExcellon) # Just for the IDE ui = excellon_obj.ui @@ -110,9 +110,9 @@ class ExcellonFlowTestCase(unittest.TestCase): sleep(0.1) self.app.processEvents() - #--------------------------------------------- + # --------------------------------------------- # Check that GUI has been read in. - #--------------------------------------------- + # --------------------------------------------- value = excellon_obj.options['feedrate'] form_value = form_field.get_value() @@ -121,33 +121,33 @@ class ExcellonFlowTestCase(unittest.TestCase): "which has {}".format('feedrate', form_value, value)) print(("'feedrate' == {}".format(value))) - #--------------------------------------------- + # --------------------------------------------- # Check that only 1 object has been created. - #--------------------------------------------- + # --------------------------------------------- names = self.fc.collection.get_names() self.assertEqual(len(names), 2, "Expected 2 objects, found %d" % len(names)) - #------------------------------------------------------- + # ------------------------------------------------------- # Make sure the CNCJob Object has the correct name - #------------------------------------------------------- + # ------------------------------------------------------- cncjob_name = excellon_name + "_cnc" self.assertTrue(cncjob_name in names, "Object named %s not found." % cncjob_name) - #------------------------------------------------------- + # ------------------------------------------------------- # Get the object make sure it's a cncjob object - #------------------------------------------------------- + # ------------------------------------------------------- cncjob_obj = self.fc.collection.get_by_name(cncjob_name) self.assertTrue(isinstance(cncjob_obj, FlatCAMCNCjob), "Expected a FlatCAMCNCjob, got %s" % type(cncjob_obj)) - #----------------------------------------- + # ----------------------------------------- # Export G-Code, check output - #----------------------------------------- + # ----------------------------------------- assert isinstance(cncjob_obj, FlatCAMCNCjob) # For IDE # get system temporary file(try create it and delete) diff --git a/tests/test_gerber_buffer.py b/tests/test_gerber_buffer.py index 7696b5b0..b79893eb 100644 --- a/tests/test_gerber_buffer.py +++ b/tests/test_gerber_buffer.py @@ -1,15 +1,16 @@ import unittest import camlib +from flatcamParsers.ParseGerber import Gerber class GerberBuffer(unittest.TestCase): def setUp(self): - self.gerber1 = camlib.Gerber() + self.gerber1 = Gerber() self.gerber1.use_buffer_for_union = True self.gerber1.parse_file("tests/gerber_files/STM32F4-spindle.cmp") geometry1 = self.gerber1.solid_geometry self.geometry1_area = self.compute_area(geometry1) - self.gerber2 = camlib.Gerber() + self.gerber2 = Gerber() self.gerber2.use_buffer_for_union = False self.gerber2.parse_file("tests/gerber_files/STM32F4-spindle.cmp") geometry2 = self.gerber2.solid_geometry @@ -21,7 +22,7 @@ class GerberBuffer(unittest.TestCase): for geo in geometry: area += geo.area - ## Not iterable, do the actual indexing and add. + # Not iterable, do the actual indexing and add. except TypeError: area = geometry.area return area diff --git a/tests/test_gerber_flow.py b/tests/test_gerber_flow.py index 15ca26d7..4549afc0 100644 --- a/tests/test_gerber_flow.py +++ b/tests/test_gerber_flow.py @@ -1,9 +1,9 @@ import sys import unittest -from PyQt4 import QtGui +from PyQt5 import QtGui, QtWidgets from FlatCAMApp import App, tclCommands from FlatCAMObj import FlatCAMGerber, FlatCAMGeometry, FlatCAMCNCjob -from ObjectUI import GerberObjectUI, GeometryObjectUI +from flatcamGUI.ObjectUI import GerberObjectUI, GeometryObjectUI from time import sleep import os import tempfile @@ -21,7 +21,7 @@ class GerberFlowTestCase(unittest.TestCase): filename = 'simple1.gbr' def setUp(self): - self.app = QtGui.QApplication(sys.argv) + self.app = QtWidgets.QApplication(sys.argv) # Create App, keep app defaults (do not load # user-defined defaults). @@ -38,30 +38,30 @@ class GerberFlowTestCase(unittest.TestCase): names = self.fc.collection.get_names() print(names) - #-------------------------------------- + # -------------------------------------- # Total of 1 objects. - #-------------------------------------- + # -------------------------------------- self.assertEqual(len(names), 1, "Expected 1 object, found %d" % len(names)) - #-------------------------------------- + # -------------------------------------- # Object's name matches the file name. - #-------------------------------------- + # -------------------------------------- self.assertEqual(names[0], self.filename, "Expected name == %s, got %s" % (self.filename, names[0])) - #--------------------------------------- + # --------------------------------------- # Get object by that name, make sure it's a FlatCAMGerber. - #--------------------------------------- + # --------------------------------------- gerber_name = names[0] gerber_obj = self.fc.collection.get_by_name(gerber_name) self.assertTrue(isinstance(gerber_obj, FlatCAMGerber), "Expected FlatCAMGerber, instead, %s is %s" % (gerber_name, type(gerber_obj))) - #---------------------------------------- + # ---------------------------------------- # Object's GUI matches Object's options - #---------------------------------------- + # ---------------------------------------- # TODO: Open GUI with double-click on object. # Opens the Object's GUI, populates it. gerber_obj.build_ui() @@ -79,20 +79,20 @@ class GerberFlowTestCase(unittest.TestCase): option, value, form_field.get_value() )) - #-------------------------------------------------- + # -------------------------------------------------- # Changes in the GUI should be read in when # running any process. Changing something here. - #-------------------------------------------------- + # -------------------------------------------------- form_field = gerber_obj.form_fields['isotooldia'] value = form_field.get_value() form_field.set_value(value * 1.1) # Increase by 10% print(("'isotooldia' == {}".format(value))) - #-------------------------------------------------- + # -------------------------------------------------- # Create isolation routing using default values # and by clicking on the button. - #-------------------------------------------------- + # -------------------------------------------------- # Get the object's GUI and click on "Generate Geometry" under # "Isolation Routing" assert isinstance(gerber_obj, FlatCAMGerber) # Just for the IDE @@ -102,9 +102,9 @@ class GerberFlowTestCase(unittest.TestCase): assert isinstance(ui, GerberObjectUI) ui.generate_iso_button.click() # Click - #--------------------------------------------- + # --------------------------------------------- # Check that GUI has been read in. - #--------------------------------------------- + # --------------------------------------------- value = gerber_obj.options['isotooldia'] form_value = form_field.get_value() self.assertEqual(value, form_value, @@ -112,30 +112,30 @@ class GerberFlowTestCase(unittest.TestCase): "which has {}".format('isotooldia', form_value, value)) print(("'isotooldia' == {}".format(value))) - #--------------------------------------------- + # --------------------------------------------- # Check that only 1 object has been created. - #--------------------------------------------- + # --------------------------------------------- names = self.fc.collection.get_names() self.assertEqual(len(names), 2, "Expected 2 objects, found %d" % len(names)) - #------------------------------------------------------- + # ------------------------------------------------------- # Make sure the Geometry Object has the correct name - #------------------------------------------------------- + # ------------------------------------------------------- geo_name = gerber_name + "_iso" self.assertTrue(geo_name in names, "Object named %s not found." % geo_name) - #------------------------------------------------------- + # ------------------------------------------------------- # Get the object make sure it's a geometry object - #------------------------------------------------------- + # ------------------------------------------------------- geo_obj = self.fc.collection.get_by_name(geo_name) self.assertTrue(isinstance(geo_obj, FlatCAMGeometry), "Expected a FlatCAMGeometry, got %s" % type(geo_obj)) - #------------------------------------ + # ------------------------------------ # Open the UI, make CNCObject - #------------------------------------ + # ------------------------------------ geo_obj.build_ui() ui = geo_obj.ui assert isinstance(ui, GeometryObjectUI) # Just for the IDE @@ -152,30 +152,30 @@ class GerberFlowTestCase(unittest.TestCase): sleep(0.1) self.app.processEvents() - #--------------------------------------------- + # --------------------------------------------- # Check that only 1 object has been created. - #--------------------------------------------- + # --------------------------------------------- names = self.fc.collection.get_names() self.assertEqual(len(names), 3, "Expected 3 objects, found %d" % len(names)) - #------------------------------------------------------- + # ------------------------------------------------------- # Make sure the CNC Job Object has the correct name - #------------------------------------------------------- + # ------------------------------------------------------- cnc_name = geo_name + "_cnc" self.assertTrue(cnc_name in names, "Object named %s not found." % geo_name) - #------------------------------------------------------- + # ------------------------------------------------------- # Get the object make sure it's a CNC Job object - #------------------------------------------------------- + # ------------------------------------------------------- cnc_obj = self.fc.collection.get_by_name(cnc_name) self.assertTrue(isinstance(cnc_obj, FlatCAMCNCjob), "Expected a FlatCAMCNCJob, got %s" % type(geo_obj)) - #----------------------------------------- + # ----------------------------------------- # Export G-Code, check output - #----------------------------------------- + # ----------------------------------------- assert isinstance(cnc_obj, FlatCAMCNCjob) output_filename = "" # get system temporary file(try create it and delete also) diff --git a/tests/test_paint.py b/tests/test_paint.py index 62115642..85820301 100644 --- a/tests/test_paint.py +++ b/tests/test_paint.py @@ -3,6 +3,7 @@ import unittest from shapely.geometry import LineString, Polygon from shapely.ops import cascaded_union, unary_union from matplotlib.pyplot import plot, subplot, show, cla, clf, xlim, ylim, title +from matplotlib.axes import * from camlib import * from copy import deepcopy diff --git a/tests/test_pathconnect.py b/tests/test_pathconnect.py index 74dbfb22..56fc1fb9 100644 --- a/tests/test_pathconnect.py +++ b/tests/test_pathconnect.py @@ -84,5 +84,6 @@ class PathConnectTest1(unittest.TestCase): matches = [p for p in result if p.equals(LineString([[0, 0], [1, 1], [2, 1]]))] self.assertEqual(len(matches), 1) + if __name__ == "__main__": - unittest.main() \ No newline at end of file + unittest.main() diff --git a/tests/test_polygon_paint.py b/tests/test_polygon_paint.py index edaab0ee..34707fa2 100644 --- a/tests/test_polygon_paint.py +++ b/tests/test_polygon_paint.py @@ -1,9 +1,9 @@ import sys import unittest -from PyQt4 import QtGui +from PyQt5 import QtGui, QtWidgets from FlatCAMApp import App from FlatCAMObj import FlatCAMGeometry, FlatCAMCNCjob -from ObjectUI import GerberObjectUI, GeometryObjectUI +from flatcamGUI.ObjectUI import GerberObjectUI, GeometryObjectUI from time import sleep import os import tempfile @@ -13,7 +13,7 @@ from shapely.geometry import LineString, LinearRing, Polygon, MultiPolygon class PolyPaintTestCase(unittest.TestCase): def setUp(self): - self.app = QtGui.QApplication(sys.argv) + self.app = QtWidgets.QApplication(sys.argv) # Create App, keep app defaults (do not load # user-defined defaults). @@ -218,10 +218,3 @@ class PolyPaintTestCase(unittest.TestCase): self.assertTrue(isinstance(geo, LineString)) # Lots of points (Should be 1000s) self.assertGreater(len(geo.coords), 2) - - - - - - - diff --git a/tests/test_svg_flow.py b/tests/test_svg_flow.py index 630a989e..7fbd6658 100644 --- a/tests/test_svg_flow.py +++ b/tests/test_svg_flow.py @@ -1,9 +1,9 @@ import sys import unittest -from PyQt4 import QtGui +from PyQt5 import QtWidgets from FlatCAMApp import App from FlatCAMObj import FlatCAMGeometry, FlatCAMCNCjob -from ObjectUI import GerberObjectUI, GeometryObjectUI +from flatcamGUI.ObjectUI import GerberObjectUI, GeometryObjectUI from time import sleep import os import tempfile @@ -12,7 +12,7 @@ import tempfile class SVGFlowTestCase(unittest.TestCase): def setUp(self): - self.app = QtGui.QApplication(sys.argv) + self.app = QtWidgets.QApplication(sys.argv) # Create App, keep app defaults (do not load # user-defined defaults). @@ -31,30 +31,28 @@ class SVGFlowTestCase(unittest.TestCase): names = self.fc.collection.get_names() print(names) - #-------------------------------------- + # -------------------------------------- # Total of 1 objects. - #-------------------------------------- - self.assertEqual(len(names), 1, - "Expected 1 object, found %d" % len(names)) + # -------------------------------------- + self.assertEqual(len(names), 1, "Expected 1 object, found %d" % len(names)) - #-------------------------------------- + # -------------------------------------- # Object's name matches the file name. - #-------------------------------------- - self.assertEqual(names[0], self.filename, - "Expected name == %s, got %s" % (self.filename, names[0])) + # -------------------------------------- + self.assertEqual(names[0], self.filename, "Expected name == %s, got %s" % (self.filename, names[0])) - #--------------------------------------- + # --------------------------------------- # Get object by that name, make sure it's a FlatCAMGerber. - #--------------------------------------- + # --------------------------------------- geo_name = names[0] geo_obj = self.fc.collection.get_by_name(geo_name) self.assertTrue(isinstance(geo_obj, FlatCAMGeometry), "Expected FlatCAMGeometry, instead, %s is %s" % (geo_name, type(geo_obj))) - #---------------------------------------- + # ---------------------------------------- # Object's GUI matches Object's options - #---------------------------------------- + # ---------------------------------------- # TODO: Open GUI with double-click on object. # Opens the Object's GUI, populates it. geo_obj.build_ui() @@ -72,9 +70,9 @@ class SVGFlowTestCase(unittest.TestCase): option, value, form_field.get_value() )) - #------------------------------------ + # ------------------------------------ # Open the UI, make CNCObject - #------------------------------------ + # ------------------------------------ geo_obj.build_ui() ui = geo_obj.ui assert isinstance(ui, GeometryObjectUI) # Just for the IDE @@ -91,30 +89,30 @@ class SVGFlowTestCase(unittest.TestCase): sleep(0.1) self.app.processEvents() - #--------------------------------------------- + # --------------------------------------------- # Check that only 1 object has been created. - #--------------------------------------------- + # --------------------------------------------- names = self.fc.collection.get_names() self.assertEqual(len(names), 2, "Expected 2 objects, found %d" % len(names)) - #------------------------------------------------------- + # ------------------------------------------------------- # Make sure the CNC Job Object has the correct name - #------------------------------------------------------- + # ------------------------------------------------------- cnc_name = geo_name + "_cnc" self.assertTrue(cnc_name in names, "Object named %s not found." % geo_name) - #------------------------------------------------------- + # ------------------------------------------------------- # Get the object make sure it's a CNC Job object - #------------------------------------------------------- + # ------------------------------------------------------- cnc_obj = self.fc.collection.get_by_name(cnc_name) self.assertTrue(isinstance(cnc_obj, FlatCAMCNCjob), "Expected a FlatCAMCNCJob, got %s" % type(geo_obj)) - #----------------------------------------- + # ----------------------------------------- # Export G-Code, check output - #----------------------------------------- + # ----------------------------------------- assert isinstance(cnc_obj, FlatCAMCNCjob) output_filename = "" # get system temporary file(try create it and delete also) diff --git a/tests/test_tcl_shell.py b/tests/test_tcl_shell.py index bcf7bed2..0933f66b 100644 --- a/tests/test_tcl_shell.py +++ b/tests/test_tcl_shell.py @@ -1,13 +1,13 @@ import sys import unittest -from PyQt4 import QtGui -from PyQt4.QtCore import QThread +from PyQt5 import QtWidgets, QtGui +from PyQt5.QtCore import QThread from FlatCAMApp import App from os import listdir from os.path import isfile from FlatCAMObj import FlatCAMGerber, FlatCAMGeometry, FlatCAMCNCjob, FlatCAMExcellon -from ObjectUI import GerberObjectUI, GeometryObjectUI +from flatcamGUI.ObjectUI import GerberObjectUI, GeometryObjectUI from time import sleep import os import tempfile @@ -36,13 +36,13 @@ class TclShellTest(unittest.TestCase): # reason for this is reuse one test window only, # CANNOT DO THIS HERE!!! - #from tests.test_tclCommands import * + # from tests.test_tclCommands import * @classmethod def setUpClass(cls): cls.setup = True - cls.app = QtGui.QApplication(sys.argv) + cls.app = QtWidgets.QApplication(sys.argv) # Create App, keep app defaults (do not load # user-defined defaults). @@ -78,9 +78,9 @@ class TclShellTest(unittest.TestCase): self.fc.exec_command_test('set_sys units IN') self.fc.exec_command_test('new') - #---------------------------------------- + # ---------------------------------------- # Units must be IN - #---------------------------------------- + # ---------------------------------------- units = self.fc.exec_command_test('get_sys units') self.assertEqual(units, "IN") @@ -88,9 +88,9 @@ class TclShellTest(unittest.TestCase): self.fc.exec_command_test('set_sys units MM') self.fc.exec_command_test('new') - #---------------------------------------- + # ---------------------------------------- # Units must be MM - #---------------------------------------- + # ---------------------------------------- units = self.fc.exec_command_test('get_sys units') self.assertEqual(units, "MM") @@ -103,9 +103,9 @@ class TclShellTest(unittest.TestCase): gbr_cmd = 'open_gerber {path}/{filename} -outname {outname}' - #----------------------------------------- + # ----------------------------------------- # Open top layer and check for object type - #----------------------------------------- + # ----------------------------------------- cmd = gbr_cmd.format( path=self.gerber_files, filename=self.copper_top_filename, @@ -116,9 +116,9 @@ class TclShellTest(unittest.TestCase): "Expected FlatCAMGerber, instead, %s is %s" % (self.gerber_top_name, type(gerber_top_obj))) - #-------------------------------------------- + # -------------------------------------------- # Open bottom layer and check for object type - #-------------------------------------------- + # -------------------------------------------- cmd = gbr_cmd.format( path=self.gerber_files, filename=self.copper_bottom_filename, @@ -129,9 +129,9 @@ class TclShellTest(unittest.TestCase): "Expected FlatCAMGerber, instead, %s is %s" % (self.gerber_bottom_name, type(gerber_bottom_obj))) - #-------------------------------------------- + # -------------------------------------------- # Open cutout layer and check for object type - #-------------------------------------------- + # -------------------------------------------- cmd = gbr_cmd.format( path=self.gerber_files, filename=self.cutout_filename, @@ -160,9 +160,9 @@ class TclShellTest(unittest.TestCase): # TODO: Check deleteb object is gone. - #-------------------------------------------- + # -------------------------------------------- # Exteriors of cutout layer, check type - #-------------------------------------------- + # -------------------------------------------- obj = self.fc.collection.get_by_name(self.gerber_cutout_name + '_iso_exterior') self.assertTrue(isinstance(obj, FlatCAMGeometry), "Expected FlatCAMGeometry, instead, %s is %s" % @@ -173,8 +173,14 @@ class TclShellTest(unittest.TestCase): self.fc.exec_command_test('mirror %s -box %s -axis X' % (self.gerber_cutout_name, self.gerber_cutout_name)) # exteriors delete and join geometries for bottom layer - self.fc.exec_command_test('isolate %s -dia %f -outname %s' % (self.gerber_cutout_name, self.engraver_diameter, self.gerber_cutout_name + '_bottom_iso')) - self.fc.exec_command_test('exteriors %s -outname %s' % (self.gerber_cutout_name + '_bottom_iso', self.gerber_cutout_name + '_bottom_iso_exterior')) + self.fc.exec_command_test( + 'isolate %s -dia %f -outname %s' % + (self.gerber_cutout_name, self.engraver_diameter, self.gerber_cutout_name + '_bottom_iso') + ) + self.fc.exec_command_test( + 'exteriors %s -outname %s' % + (self.gerber_cutout_name + '_bottom_iso', self.gerber_cutout_name + '_bottom_iso_exterior') + ) self.fc.exec_command_test('delete %s' % (self.gerber_cutout_name + '_bottom_iso')) obj = self.fc.collection.get_by_name(self.gerber_cutout_name + '_bottom_iso_exterior') self.assertTrue(isinstance(obj, FlatCAMGeometry), @@ -187,12 +193,20 @@ class TclShellTest(unittest.TestCase): "Expected 5 objects, found %d" % len(names)) # isolate traces - self.fc.exec_command_test('isolate %s -dia %f' % (self.gerber_top_name, self.engraver_diameter)) - self.fc.exec_command_test('isolate %s -dia %f' % (self.gerber_bottom_name, self.engraver_diameter)) + self.fc.exec_command_test('isolate %s -dia %f' % (self.gerber_top_name, self.engraver_diameter)) + self.fc.exec_command_test('isolate %s -dia %f' % (self.gerber_bottom_name, self.engraver_diameter)) # join isolated geometries for top and bottom - self.fc.exec_command_test('join_geometries %s %s %s' % (self.gerber_top_name + '_join_iso', self.gerber_top_name + '_iso', self.gerber_cutout_name + '_iso_exterior')) - self.fc.exec_command_test('join_geometries %s %s %s' % (self.gerber_bottom_name + '_join_iso', self.gerber_bottom_name + '_iso', self.gerber_cutout_name + '_bottom_iso_exterior')) + self.fc.exec_command_test( + 'join_geometries %s %s %s' % + (self.gerber_top_name + '_join_iso', self.gerber_top_name + '_iso', + self.gerber_cutout_name + '_iso_exterior') + ) + self.fc.exec_command_test( + 'join_geometries %s %s %s' % + (self.gerber_bottom_name + '_join_iso', self.gerber_bottom_name + '_iso', + self.gerber_cutout_name + '_bottom_iso_exterior') + ) # at this stage we should have 9 objects names = self.fc.collection.get_names() @@ -211,14 +225,21 @@ class TclShellTest(unittest.TestCase): "Expected 5 objects, found %d" % len(names)) # geocutout bottom test (it cuts to same object) - self.fc.exec_command_test('isolate %s -dia %f -outname %s' % (self.gerber_cutout_name, self.cutout_diameter, self.gerber_cutout_name + '_bottom_iso')) - self.fc.exec_command_test('exteriors %s -outname %s' % (self.gerber_cutout_name + '_bottom_iso', self.gerber_cutout_name + '_bottom_iso_exterior')) + self.fc.exec_command_test( + 'isolate %s -dia %f -outname %s' % + (self.gerber_cutout_name, self.cutout_diameter, self.gerber_cutout_name + '_bottom_iso') + ) + self.fc.exec_command_test( + 'exteriors %s -outname %s' % + (self.gerber_cutout_name + '_bottom_iso', self.gerber_cutout_name + '_bottom_iso_exterior') + ) self.fc.exec_command_test('delete %s' % (self.gerber_cutout_name + '_bottom_iso')) obj = self.fc.collection.get_by_name(self.gerber_cutout_name + '_bottom_iso_exterior') self.assertTrue(isinstance(obj, FlatCAMGeometry), "Expected FlatCAMGeometry, instead, %s is %s" % (self.gerber_cutout_name + '_bottom_iso_exterior', type(obj))) - self.fc.exec_command_test('geocutout %s -dia %f -gapsize 0.3 -gaps 4' % (self.gerber_cutout_name + '_bottom_iso_exterior', self.cutout_diameter)) + self.fc.exec_command_test('geocutout %s -dia %f -gapsize 0.3 -gaps 4' % + (self.gerber_cutout_name + '_bottom_iso_exterior', self.cutout_diameter)) # at this stage we should have 6 objects names = self.fc.collection.get_names() @@ -229,7 +250,8 @@ class TclShellTest(unittest.TestCase): def test_open_gerber(self): - self.fc.exec_command_test('open_gerber %s/%s -outname %s' % (self.gerber_files, self.copper_top_filename, self.gerber_top_name)) + self.fc.exec_command_test('open_gerber %s/%s -outname %s' % + (self.gerber_files, self.copper_top_filename, self.gerber_top_name)) gerber_top_obj = self.fc.collection.get_by_name(self.gerber_top_name) self.assertTrue(isinstance(gerber_top_obj, FlatCAMGerber), "Expected FlatCAMGerber, instead, %s is %s" % @@ -237,7 +259,8 @@ class TclShellTest(unittest.TestCase): def test_excellon_flow(self): - self.fc.exec_command_test('open_excellon %s/%s -outname %s' % (self.gerber_files, self.excellon_filename, self.excellon_name)) + self.fc.exec_command_test('open_excellon %s/%s -outname %s' % + (self.gerber_files, self.excellon_filename, self.excellon_name)) excellon_obj = self.fc.collection.get_by_name(self.excellon_name) self.assertTrue(isinstance(excellon_obj, FlatCAMExcellon), "Expected FlatCAMExcellon, instead, %s is %s" %