diff --git a/FlatCAMObj.py b/FlatCAMObj.py index f9348675..d760d812 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -5370,9 +5370,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): try: xfactor = float(xfactor) - except Exception as e: - self.app.inform.emit('[ERROR_NOTCL] %s' % - _("Scale factor has to be a number: integer or float.")) + except Exception: + self.app.inform.emit('[ERROR_NOTCL] %s' % _("Scale factor has to be a number: integer or float.")) return if yfactor is None: @@ -5380,29 +5379,19 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): else: try: yfactor = float(yfactor) - except Exception as e: - self.app.inform.emit('[ERROR_NOTCL] %s' % - _("Scale factor has to be a number: integer or float.")) + except Exception: + self.app.inform.emit('[ERROR_NOTCL] %s' % _("Scale factor has to be a number: integer or float.")) return + if xfactor == 1 and yfactor == 1: + return + if point is None: px = 0 py = 0 else: px, py = point - # if type(self.solid_geometry) == list: - # geo_list = self.flatten(self.solid_geometry) - # self.solid_geometry = [] - # # for g in geo_list: - # # self.solid_geometry.append(affinity.scale(g, xfactor, yfactor, origin=(px, py))) - # self.solid_geometry = [affinity.scale(g, xfactor, yfactor, origin=(px, py)) - # for g in geo_list] - # else: - # self.solid_geometry = affinity.scale(self.solid_geometry, xfactor, yfactor, - # origin=(px, py)) - # self.app.inform.emit("[success] Geometry Scale done.") - self.geo_len = 0 self.old_disp_number = 0 self.el_count = 0 @@ -5438,25 +5427,24 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): self.el_count = 0 self.tools[tool]['solid_geometry'] = scale_recursion(self.tools[tool]['solid_geometry']) - else: - try: - # variables to display the percentage of work done - self.geo_len = 0 - try: - self.geo_len = len(self.solid_geometry) - except TypeError: - self.geo_len = 1 - self.old_disp_number = 0 - self.el_count = 0 - self.solid_geometry = scale_recursion(self.solid_geometry) - except AttributeError: - self.solid_geometry = [] - return + try: + # variables to display the percentage of work done + self.geo_len = 0 + try: + self.geo_len = len(self.solid_geometry) + except TypeError: + self.geo_len = 1 + self.old_disp_number = 0 + self.el_count = 0 + + self.solid_geometry = scale_recursion(self.solid_geometry) + except AttributeError: + self.solid_geometry = [] + return self.app.proc_container.new_text = '' - if xfactor != 1 and yfactor != 1: - self.app.inform.emit('[success] %s' % _("Geometry Scale done.")) + self.app.inform.emit('[success] %s' % _("Geometry Scale done.")) def offset(self, vect): """ @@ -5478,6 +5466,9 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): ) return + if dx == 0 and dy == 0: + return + self.geo_len = 0 self.old_disp_number = 0 self.el_count = 0 @@ -5513,18 +5504,18 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): self.el_count = 0 self.tools[tool]['solid_geometry'] = translate_recursion(self.tools[tool]['solid_geometry']) - else: - # variables to display the percentage of work done - self.geo_len = 0 - try: - for g in self.solid_geometry: - self.geo_len += 1 - except TypeError: - self.geo_len = 1 - self.old_disp_number = 0 - self.el_count = 0 - self.solid_geometry = translate_recursion(self.solid_geometry) + # variables to display the percentage of work done + self.geo_len = 0 + try: + for g in self.solid_geometry: + self.geo_len += 1 + except TypeError: + self.geo_len = 1 + self.old_disp_number = 0 + self.el_count = 0 + + self.solid_geometry = translate_recursion(self.solid_geometry) self.app.proc_container.new_text = '' self.app.inform.emit('[success] %s' % _("Geometry Offset done.")) diff --git a/README.md b/README.md index d137bf79..298c8038 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,8 @@ CAD program, and create G-Code for Isolation routing. - reverted this change: "selected object in Project used to ask twice for UI build" because it will not build the UI when a tab is closed for Document object and the object is selected - fixed issue after Geometry object edit; the GCode made from and edited object did not reflect the changes in the object - in Object UI, the Scale FCDoubleSpinner will no longer work for Return key press due of issues of unwanted scaling on focusOut event +- in FlatCAMGeometry fixed the scale and offset methods to always process the self.solid_geometry +- Calibration Tool - finished the calibrated object creation method 8.12.2019 diff --git a/flatcamGUI/GUIElements.py b/flatcamGUI/GUIElements.py index d960d024..133b39e0 100644 --- a/flatcamGUI/GUIElements.py +++ b/flatcamGUI/GUIElements.py @@ -531,6 +531,7 @@ class FCSpinner(QtWidgets.QSpinBox): def __init__(self, suffix=None, alignment=None, parent=None): super(FCSpinner, self).__init__(parent) self.readyToEdit = True + self.editingFinished.connect(self.on_edit_finished) self.lineEdit().installEventFilter(self) @@ -588,6 +589,7 @@ class FCSpinner(QtWidgets.QSpinBox): super(FCSpinner, self).focusOutEvent(e) # required to remove cursor on focusOut self.lineEdit().deselect() self.readyToEdit = True + self.prev_readyToEdit = True def get_value(self): return int(self.value()) @@ -652,7 +654,6 @@ class FCDoubleSpinner(QtWidgets.QDoubleSpinBox): self.readyToEdit = False else: self.lineEdit().deselect() - return True return False diff --git a/flatcamParsers/ParseExcellon.py b/flatcamParsers/ParseExcellon.py index c6eb50aa..558a2cfa 100644 --- a/flatcamParsers/ParseExcellon.py +++ b/flatcamParsers/ParseExcellon.py @@ -1112,6 +1112,9 @@ class Excellon(Geometry): else: px, py = point + if xfactor == 0 and yfactor == 0: + return + def scale_geom(obj): if type(obj) is list: new_obj = [] @@ -1168,6 +1171,9 @@ class Excellon(Geometry): dx, dy = vect + if dx == 0 and dy == 0: + return + def offset_geom(obj): if type(obj) is list: new_obj = [] @@ -1297,6 +1303,9 @@ class Excellon(Geometry): if angle_y is None: angle_y = 0.0 + if angle_x == 0 and angle_y == 0: + return + def skew_geom(obj): if type(obj) is list: new_obj = [] @@ -1375,6 +1384,9 @@ class Excellon(Geometry): """ log.debug("flatcamParsers.ParseExcellon.Excellon.rotate()") + if angle == 0: + return + def rotate_geom(obj, origin=None): if type(obj) is list: new_obj = [] diff --git a/flatcamParsers/ParseGerber.py b/flatcamParsers/ParseGerber.py index 1092bf9c..3b75d2d7 100644 --- a/flatcamParsers/ParseGerber.py +++ b/flatcamParsers/ParseGerber.py @@ -1760,6 +1760,9 @@ class Gerber(Geometry): _("Scale factor has to be a number: integer or float.")) return + if xfactor == 0 and yfactor == 0: + return + if point is None: px = 0 py = 0 @@ -1769,8 +1772,7 @@ class Gerber(Geometry): # variables to display the percentage of work done self.geo_len = 0 try: - for __ in self.solid_geometry: - self.geo_len += 1 + self.geo_len = len(self.solid_geometry) except TypeError: self.geo_len = 1 @@ -1835,8 +1837,7 @@ class Gerber(Geometry): log.debug('camlib.Gerber.scale() Exception --> %s' % str(e)) return 'fail' - self.app.inform.emit('[success] %s' % - _("Gerber Scale done.")) + self.app.inform.emit('[success] %s' % _("Gerber Scale done.")) self.app.proc_container.new_text = '' # ## solid_geometry ??? @@ -1876,6 +1877,9 @@ class Gerber(Geometry): "Probable you entered only one value in the Offset field.")) return + if dx == 0 and dy == 0: + return + # variables to display the percentage of work done self.geo_len = 0 try: @@ -2028,11 +2032,13 @@ class Gerber(Geometry): px, py = point + if angle_x == 0 and angle_y == 0: + return + # variables to display the percentage of work done self.geo_len = 0 try: - for __ in self.solid_geometry: - self.geo_len += 1 + self.geo_len = len(self.solid_geometry) except TypeError: self.geo_len = 1 @@ -2089,6 +2095,9 @@ class Gerber(Geometry): px, py = point + if angle == 0: + return + # variables to display the percentage of work done self.geo_len = 0 try: diff --git a/flatcamTools/ToolCalibration.py b/flatcamTools/ToolCalibration.py index 928aee75..555adafb 100644 --- a/flatcamTools/ToolCalibration.py +++ b/flatcamTools/ToolCalibration.py @@ -18,6 +18,7 @@ from shapely.geometry.base import * import math from datetime import datetime import logging +from copy import deepcopy import gettext import FlatCAMTranslation as fcTranslate @@ -388,6 +389,21 @@ class ToolCalibration(FlatCAMTool): """) grid_lay.addWidget(self.generate_factors_button, 20, 0, 1, 3) + separator_line1 = QtWidgets.QFrame() + separator_line1.setFrameShape(QtWidgets.QFrame.HLine) + separator_line1.setFrameShadow(QtWidgets.QFrame.Sunken) + grid_lay.addWidget(separator_line1, 21, 0, 1, 3) + + grid_lay.addWidget(QtWidgets.QLabel(''), 22, 0, 1, 3) + + # STEP 4 # + step_4 = QtWidgets.QLabel('%s' % _("STEP 4: Adjusted GCode")) + step_4.setToolTip( + _("Generate verification GCode file adjusted with\n" + "the factors above.") + ) + grid_lay.addWidget(step_4, 23, 0, 1, 3) + self.scalex_label = QtWidgets.QLabel(_("Scale Factor X:")) self.scalex_label.setToolTip( _("Factor for Scale action over X axis.") @@ -397,8 +413,8 @@ class ToolCalibration(FlatCAMTool): self.scalex_entry.set_precision(self.decimals) self.scalex_entry.setSingleStep(0.1) - grid_lay.addWidget(self.scalex_label, 21, 0) - grid_lay.addWidget(self.scalex_entry, 21, 1, 1, 2) + grid_lay.addWidget(self.scalex_label, 24, 0) + grid_lay.addWidget(self.scalex_entry, 24, 1, 1, 2) self.scaley_label = QtWidgets.QLabel(_("Scale Factor Y:")) self.scaley_label.setToolTip( @@ -409,20 +425,20 @@ class ToolCalibration(FlatCAMTool): self.scaley_entry.set_precision(self.decimals) self.scaley_entry.setSingleStep(0.1) - grid_lay.addWidget(self.scaley_label, 22, 0) - grid_lay.addWidget(self.scaley_entry, 22, 1, 1, 2) + grid_lay.addWidget(self.scaley_label, 25, 0) + grid_lay.addWidget(self.scaley_entry, 25, 1, 1, 2) self.scale_button = QtWidgets.QPushButton(_("Apply Scale Factors")) self.scale_button.setToolTip( _("Apply Scale factors on the calibration points.") ) self.scale_button.setStyleSheet(""" - QPushButton - { - font-weight: bold; - } - """) - grid_lay.addWidget(self.scale_button, 23, 0, 1, 3) + QPushButton + { + font-weight: bold; + } + """) + grid_lay.addWidget(self.scale_button, 26, 0, 1, 3) self.skewx_label = QtWidgets.QLabel(_("Skew Angle X:")) self.skewx_label.setToolTip( @@ -434,8 +450,8 @@ class ToolCalibration(FlatCAMTool): self.skewx_entry.set_precision(self.decimals) self.skewx_entry.setSingleStep(0.1) - grid_lay.addWidget(self.skewx_label, 24, 0) - grid_lay.addWidget(self.skewx_entry, 24, 1, 1, 2) + grid_lay.addWidget(self.skewx_label, 27, 0) + grid_lay.addWidget(self.skewx_entry, 27, 1, 1, 2) self.skewy_label = QtWidgets.QLabel(_("Skew Angle Y:")) self.skewy_label.setToolTip( @@ -447,20 +463,20 @@ class ToolCalibration(FlatCAMTool): self.skewy_entry.set_precision(self.decimals) self.skewy_entry.setSingleStep(0.1) - grid_lay.addWidget(self.skewy_label, 25, 0) - grid_lay.addWidget(self.skewy_entry, 25, 1, 1, 2) + grid_lay.addWidget(self.skewy_label, 28, 0) + grid_lay.addWidget(self.skewy_entry, 28, 1, 1, 2) self.skew_button = QtWidgets.QPushButton(_("Apply Skew Factors")) self.skew_button.setToolTip( _("Apply Skew factors on the calibration points.") ) self.skew_button.setStyleSheet(""" - QPushButton - { - font-weight: bold; - } - """) - grid_lay.addWidget(self.skew_button, 26, 0, 1, 3) + QPushButton + { + font-weight: bold; + } + """) + grid_lay.addWidget(self.skew_button, 29, 0, 1, 3) # final_factors_lbl = QtWidgets.QLabel('%s' % _("Final Factors")) # final_factors_lbl.setToolTip( @@ -519,22 +535,8 @@ class ToolCalibration(FlatCAMTool): # grid_lay.addWidget(self.fin_skewy_label, 31, 0) # grid_lay.addWidget(self.fin_skewy_entry, 31, 1, 1, 2) - separator_line1 = QtWidgets.QFrame() - separator_line1.setFrameShape(QtWidgets.QFrame.HLine) - separator_line1.setFrameShadow(QtWidgets.QFrame.Sunken) - grid_lay.addWidget(separator_line1, 32, 0, 1, 3) - - grid_lay.addWidget(QtWidgets.QLabel(''), 32, 0, 1, 3) - - # STEP 4 # - step_4 = QtWidgets.QLabel('%s' % _("STEP 4: Adjusted GCode")) - step_4.setToolTip( - _("Generate verification GCode file adjusted with\n" - "the factors above.") - ) - grid_lay.addWidget(step_4, 34, 0, 1, 3) - # ## Adjusted GCode Button + self.adj_gcode_button = QtWidgets.QPushButton(_("Generate Adjusted GCode")) self.adj_gcode_button.setToolTip( _("Generate verification GCode file adjusted with\n" @@ -735,6 +737,16 @@ class ToolCalibration(FlatCAMTool): self.object_combo.setDisabled(True) def on_start_collect_points(self): + + if self.cal_source_radio.get_value() == 'object': + selection_index = self.object_combo.currentIndex() + model_index = self.app.collection.index(selection_index, 0, self.object_combo.rootModelIndex()) + try: + self.target_obj = model_index.internalPointer().obj + except Exception: + self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no source FlatCAM object selected...")) + return + # disengage the grid snapping since it will be hard to find the drills on grid if self.app.ui.grid_snap_btn.isChecked(): self.grid_status_memory = True @@ -751,15 +763,6 @@ class ToolCalibration(FlatCAMTool): self.local_connected = True - if self.cal_source_radio.get_value() == 'object': - selection_index = self.object_combo.currentIndex() - model_index = self.app.collection.index(selection_index, 0, self.object_combo.rootModelIndex()) - try: - self.target_obj = model_index.internalPointer().obj - except Exception: - self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no target object loaded ...")) - return - self.reset_calibration_points() self.app.inform.emit(_("Get First calibration point. Bottom Left...")) @@ -1038,8 +1041,113 @@ class ToolCalibration(FlatCAMTool): self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no FlatCAM object selected...")) return 'fail' + obj_name = self.cal_object.options["name"] + "_calibrated" + + self.app.worker_task.emit({'fcn': self.new_calibrated_object, 'params': [obj_name]}) + + def new_calibrated_object(self, obj_name): + + try: + origin_x = self.click_points[0][0] + origin_y = self.click_points[0][1] + except IndexError as e: + log.debug("ToolCalibration.new_calibrated_object() --> %s" % str(e)) + return 'fail' + + scalex = self.scalex_entry.get_value() + scaley = self.scaley_entry.get_value() + + skewx = self.skewx_entry.get_value() + skewy = self.skewy_entry.get_value() + # create a new object adjusted (calibrated) - # TODO + def initialize_geometry(obj_init, app): + obj_init.solid_geometry = deepcopy(obj.solid_geometry) + try: + obj_init.follow_geometry = deepcopy(obj.follow_geometry) + except AttributeError: + pass + + try: + obj_init.apertures = deepcopy(obj.apertures) + except AttributeError: + pass + + try: + if obj.tools: + obj_init.tools = deepcopy(obj.tools) + except Exception as e: + log.debug("App.on_copy_object() --> %s" % str(e)) + + obj_init.scale(xfactor=scalex, yfactor=scaley, point=(origin_x, origin_y)) + obj_init.skew(angle_x=skewx, angle_y=skewy, point=(origin_x, origin_y)) + + try: + obj_init.source_file = deepcopy(obj.source_file) + except (AttributeError, TypeError): + pass + + def initialize_gerber(obj_init, app): + obj_init.solid_geometry = deepcopy(obj.solid_geometry) + try: + obj_init.follow_geometry = deepcopy(obj.follow_geometry) + except AttributeError: + pass + + try: + obj_init.apertures = deepcopy(obj.apertures) + except AttributeError: + pass + + try: + if obj.tools: + obj_init.tools = deepcopy(obj.tools) + except Exception as e: + log.debug("App.on_copy_object() --> %s" % str(e)) + + obj_init.scale(xfactor=scalex, yfactor=scaley, point=(origin_x, origin_y)) + obj_init.skew(angle_x=skewx, angle_y=skewy, point=(origin_x, origin_y)) + + try: + obj_init.source_file = self.export_gerber(obj_name=obj_name, filename=None, local_use=obj_init, + use_thread=False) + except (AttributeError, TypeError): + pass + + def initialize_excellon(obj_init, app): + obj_init.tools = deepcopy(obj.tools) + + # drills are offset, so they need to be deep copied + obj_init.drills = deepcopy(obj.drills) + # slots are offset, so they need to be deep copied + obj_init.slots = deepcopy(obj.slots) + + obj_init.scale(xfactor=scalex, yfactor=scaley, point=(origin_x, origin_y)) + obj_init.skew(angle_x=skewx, angle_y=skewy, point=(origin_x, origin_y)) + + obj_init.create_geometry() + + obj_init.source_file = self.app.export_excellon(obj_name=obj_name, local_use=obj, filename=None, + use_thread=False) + + obj = self.cal_object + obj_name = obj_name + + if obj is None: + self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no FlatCAM object selected...")) + log.debug("ToolCalibration.new_calibrated_object() --> No object to calibrate") + return 'fail' + + try: + if obj.kind.lower() == 'excellon': + self.app.new_object("excellon", str(obj_name), initialize_excellon) + elif obj.kind.lower() == 'gerber': + self.app.new_object("gerber", str(obj_name), initialize_gerber) + elif obj.kind.lower() == 'geometry': + self.app.new_object("geometry", str(obj_name), initialize_geometry) + except Exception as e: + log.debug("ToolCalibration.new_calibrated_object() --> %s" % str(e)) + return "Operation failed: %s" % str(e) def disconnect_cal_events(self): # restore the Grid snapping if it was active before