From e717cb8f15d351980c1832b993e85225fb3a09e0 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Fri, 15 Feb 2019 23:35:23 +0200 Subject: [PATCH] - remade the Excellon export function to work with parameters entered in Edit -> Preferences -> Excellon Export - added a new entry in the Project Context Menu named 'Save'. It will actually work for Geometry and it will do Export DXF and for Excellon and it will do Export Excellon --- FlatCAMApp.py | 96 ++++++++++++------ FlatCAMGUI.py | 17 ++-- FlatCAMObj.py | 232 ++++++++++++++++++++++---------------------- ObjectCollection.py | 3 + README.md | 4 +- 5 files changed, 195 insertions(+), 157 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 6420210a..a092cc84 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -1079,8 +1079,8 @@ class App(QtCore.QObject): self.ui.menufileexportsvg.triggered.connect(self.on_file_exportsvg) self.ui.menufileexportpng.triggered.connect(self.on_file_exportpng) - self.ui.menufileexportexcellon.triggered.connect(lambda: self.on_file_exportexcellon(altium_format=None)) - self.ui.menufileexportexcellon_altium.triggered.connect(lambda: self.on_file_exportexcellon(altium_format=True)) + self.ui.menufileexportexcellon.triggered.connect(self.on_file_exportexcellon) + self.ui.menufileexportdxf.triggered.connect(self.on_file_exportdxf) @@ -1156,6 +1156,7 @@ class App(QtCore.QObject): self.ui.menuprojectedit.triggered.connect(self.object2editor) self.ui.menuprojectdelete.triggered.connect(self.on_delete) + self.ui.menuprojectsave.triggered.connect(self.on_project_context_save) self.ui.menuprojectproperties.triggered.connect(self.obj_properties) # Toolbar @@ -4693,6 +4694,13 @@ class App(QtCore.QObject): self.properties_tool.run() + def on_project_context_save(self): + obj = self.collection.get_active() + if type(obj) == FlatCAMGeometry: + self.on_file_exportdxf() + elif type(obj) == FlatCAMExcellon: + self.on_file_exportexcellon() + def obj_move(self): self.report_usage("obj_move()") @@ -4939,7 +4947,7 @@ class App(QtCore.QObject): write_png(filename, data) self.file_saved.emit("png", filename) - def on_file_exportexcellon(self, altium_format=None): + def on_file_exportexcellon(self): """ Callback for menu item File->Export SVG. @@ -4971,7 +4979,7 @@ class App(QtCore.QObject): name = self.collection.get_active().options["name"] - filter = "Excellon File (*.drl);;Excellon File (*.txt);;All Files (*.*)" + filter = "Excellon File (*.DRL);;Excellon File (*.TXT);;All Files (*.*)" try: filename, _ = QtWidgets.QFileDialog.getSaveFileName( caption="Export Excellon", @@ -4986,12 +4994,8 @@ class App(QtCore.QObject): self.inform.emit("[WARNING_NOTCL]Export Excellon cancelled.") return else: - if altium_format is None: - self.export_excellon(name, filename) - self.file_saved.emit("Excellon", filename) - else: - self.export_excellon(name, filename, altium_format=True) - self.file_saved.emit("Excellon", filename) + self.export_excellon(name, filename) + self.file_saved.emit("Excellon", filename) def on_file_exportdxf(self): """ @@ -5528,9 +5532,17 @@ class App(QtCore.QObject): return "Could not retrieve object: %s" % obj_name # updated units - units = self.defaults["excellon_exp_units"] - whole = self.defaults["excellon_exp_integer"] - fract = self.defaults["excellon_exp_decimals"] + eunits = self.defaults["excellon_exp_units"] + ewhole = self.defaults["excellon_exp_integer"] + efract = self.defaults["excellon_exp_decimals"] + ezeros = self.defaults["excellon_exp_zeros"] + eformat = self.defaults[ "excellon_exp_format"] + + fc_units = self.general_options_form.general_app_group.units_radio.get_value().upper() + if fc_units == 'MM': + factor = 1 if eunits == 'METRIC' else 0.03937 + else: + factor = 25.4 if eunits == 'METRIC' else 1 def make_excellon(): try: @@ -5538,32 +5550,55 @@ class App(QtCore.QObject): header = 'M48\n' header += ';EXCELLON GENERATED BY FLATCAM v%s - www.flatcam.org - Version Date: %s\n' % \ - (str(self.app.version), str(self.app.version_date)) + (str(self.version), str(self.version_date)) header += ';Filename: %s' % str(obj_name) + '\n' header += ';Created on : %s' % time_str + '\n' - if self.defaults["excellon_exp_format"] == 'dec': - has_slots, excellon_code = obj.export_excellon_decimals(whole, fract, units) - header += units + '\n' + if eformat == 'dec': + has_slots, excellon_code = obj.export_excellon(ewhole, efract, factor=factor) + header += eunits + '\n' for tool in obj.tools: - if units == 'METRIC': - header += 'T' + str(tool) + 'F00S00' + 'C' + '%.2f' % float(obj.tools[tool]['C']) + '\n' + if eunits == 'METRIC': + header += "T{tool}F00S00C{:.{dec}f}\n".format(float(obj.tools[tool]['C']) * factor, + tool=str(tool), + dec=2) else: - header += 'T' + str(tool) + 'F00S00' + 'C' + '%.4f' % float(obj.tools[tool]['C']) + '\n' + header += "T{tool}F00S00C{:.{dec}f}\n".format(float(obj.tools[tool]['C']) * factor, + tool=str(tool), + dec=4) else: - has_slots, excellon_code = obj.export_excellon_ndecimals(whole, fract, units) - header += '%s,%s\n' % (units, self.defaults["excellon_exp_zeros"]) - header += format_exc + if ezeros == 'LZ': + has_slots, excellon_code = obj.export_excellon(ewhole, efract, + form='ndec', e_zeros='LZ', factor=factor) + header += '%s,%s\n' % (eunits, 'LZ') + header += format_exc - for tool in obj.tools: - if units == 'METRIC': - header += 'T' + str(tool) + 'F00S00' + 'C' + \ - '%.2f' % (float(obj.tools[tool]['C']) / 25.4) + '\n' - else: - header += 'T' + str(tool) + 'F00S00' + 'C' + '%.4f' % float(obj.tools[tool]['C']) + '\n' + for tool in obj.tools: + if eunits == 'METRIC': + header += "T{tool}F00S00C{:.{dec}f}\n".format(float(obj.tools[tool]['C']) * factor, + tool=str(tool), + dec=2) + else: + header += "T{tool}F00S00C{:.{dec}f}\n".format(float(obj.tools[tool]['C']) * factor, + tool=str(tool), + dec=4) + else: + has_slots, excellon_code = obj.export_excellon(ewhole, efract, + form='ndec', e_zeros='TZ', factor=factor) + header += '%s,%s\n' % (eunits, 'TZ') + header += format_exc + for tool in obj.tools: + if eunits == 'METRIC': + header += "T{tool}F00S00C{:.{dec}f}\n".format(float(obj.tools[tool]['C']) * factor, + tool=str(tool), + dec=2) + else: + header += "T{tool}F00S00C{:.{dec}f}\n".format(float(obj.tools[tool]['C']) * factor, + tool=str(tool), + dec=4) header += '%\n' footer = 'M30\n' @@ -5576,7 +5611,8 @@ class App(QtCore.QObject): self.file_saved.emit("Excellon", filename) self.inform.emit("[success] Excellon file exported to " + filename) - except: + except Exception as e: + log.debug("App.export_excellon.make_excellon() --> %s" % str(e)) return 'fail' if use_thread is True: diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py index 20bf1442..3b33b030 100644 --- a/FlatCAMGUI.py +++ b/FlatCAMGUI.py @@ -154,19 +154,11 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.menufileexportexcellon = QtWidgets.QAction(QtGui.QIcon('share/drill32.png'), 'Export &Excellon ...', self) self.menufileexportexcellon.setToolTip( "Will export an Excellon Object as Excellon file,\n" - "the coordinates format is decimal and the file units\n" - "are the current units set in FlatCAM." + "the coordinates format, the file units and zeros\n" + "are set in Preferences -> Excellon Export." ) self.menufileexport.addAction(self.menufileexportexcellon) - self.menufileexportexcellon_altium = QtWidgets.QAction(QtGui.QIcon('share/drill32.png'), - 'Export Excellon 2:4 LZ INCH ...', self) - self.menufileexportexcellon_altium.setToolTip( - "Will export an Excellon Object as Excellon file,\n" - "the coordinates format is 2:4, excellon zeros are LZ \n" - "and the file units are INCH." - ) - self.menufileexport.addAction(self.menufileexportexcellon_altium) # Separator self.menufile.addSeparator() @@ -428,10 +420,13 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.menuproject.addSeparator() self.menuprojectgeneratecnc = self.menuproject.addAction(QtGui.QIcon('share/cnc32.png'), 'Generate CNC') self.menuproject.addSeparator() + + self.menuprojectedit = self.menuproject.addAction(QtGui.QIcon('share/edit_ok32.png'), 'Edit') self.menuprojectcopy = self.menuproject.addAction(QtGui.QIcon('share/copy32.png'), 'Copy') self.menuprojectdelete = self.menuproject.addAction(QtGui.QIcon('share/delete32.png'), 'Delete') - self.menuprojectedit = self.menuproject.addAction(QtGui.QIcon('share/edit_ok32.png'), 'Edit') + self.menuprojectsave= self.menuproject.addAction(QtGui.QIcon('share/save_as.png'), 'Save') self.menuproject.addSeparator() + self.menuprojectproperties = self.menuproject.addAction(QtGui.QIcon('share/properties32.png'), 'Properties') ################ diff --git a/FlatCAMObj.py b/FlatCAMObj.py index f98cebf3..45ffc616 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -1612,32 +1612,64 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): item[0] = str(item[0]) return table_tools_items - def export_excellon_decimals(self, whole, fract, units): + def export_excellon(self, whole, fract, e_zeros=None, form='dec', factor=1): """ Returns two values, first is a boolean , if 1 then the file has slots and second contain the Excellon code :return: has_slots and Excellon_code """ excellon_code = '' - units = units # store here if the file has slots, return 1 if any slots, 0 if only drills has_slots = 0 # drills processing try: - for tool in self.tools: - if int(tool) < 10: - excellon_code += 'T0' + str(tool) + '\n' - else: - excellon_code += 'T' + str(tool) + '\n' + if self.drills: + length = whole + fract + for tool in self.tools: + excellon_code += 'T0%s\n' % str(tool) if int(tool) < 10 else 'T%s\n' % str(tool) - for drill in self.drills: - if tool == drill['tool']: - if units == 'MM': - excellon_code += 'X' + '%.3f' % drill['point'].x + 'Y' + '%.3f' % drill['point'].y + '\n' - else: - excellon_code += 'X' + '%.4f' % drill['point'].x + 'Y' + '%.4f' % drill['point'].y + '\n' + for drill in self.drills: + if form == 'dec' and tool == drill['tool']: + drill_x = drill['point'].x * factor + drill_y = drill['point'].y * factor + excellon_code += "X{:.{dec}f}Y{:.{dec}f}\n".format(drill_x, drill_y, dec=fract) + elif e_zeros == 'LZ' and tool == drill['tool']: + drill_x = drill['point'].x * factor + drill_y = drill['point'].y * factor + + exc_x_formatted = "{:.{dec}f}".format(drill_x, dec=fract) + exc_y_formatted = "{:.{dec}f}".format(drill_y, dec=fract) + + # extract whole part and decimal part + exc_x_formatted = exc_x_formatted.partition('.') + exc_y_formatted = exc_y_formatted.partition('.') + + # left padd the 'whole' part with zeros + x_whole = exc_x_formatted[0].rjust(whole, '0') + y_whole = exc_y_formatted[0].rjust(whole, '0') + + # restore the coordinate padded in the left with 0 and added the decimal part + # without the decinal dot + exc_x_formatted = x_whole + exc_x_formatted[2] + exc_y_formatted = y_whole + exc_y_formatted[2] + + excellon_code += "X{xform}Y{yform}\n".format(xform=exc_x_formatted, + yform=exc_y_formatted) + elif tool == drill['tool']: + drill_x = drill['point'].x * factor + drill_y = drill['point'].y * factor + + exc_x_formatted = "{:.{dec}f}".format(drill_x, dec=fract).replace('.', '') + exc_y_formatted = "{:.{dec}f}".format(drill_y, dec=fract).replace('.', '') + + # pad with rear zeros + exc_x_formatted.ljust(length, '0') + exc_y_formatted.ljust(length, '0') + + excellon_code += "X{xform}Y{yform}\n".format(xform=exc_x_formatted, + yform=exc_y_formatted) except Exception as e: log.debug(str(e)) @@ -1652,111 +1684,81 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): excellon_code += 'T' + str(tool) + '\n' for slot in self.slots: - if tool == slot['tool']: - if units == 'MM': - excellon_code += 'G00' + 'X' + '%.3f' % slot['start'].x + 'Y' + \ - '%.3f' % slot['start'].y + '\n' - excellon_code += 'M15\n' - excellon_code += 'G01' + 'X' + '%.3f' % slot['stop'].x + 'Y' + \ - '%.3f' % slot['stop'].y + '\n' - excellon_code += 'M16\n' - else: - excellon_code += 'G00' + 'X' + '%.4f' % slot['start'].x + 'Y' + \ - '%.4f' % slot['start'].y + '\n' - excellon_code += 'M15\n' - excellon_code += 'G01' + 'X' + '%.4f' % slot['stop'].x + 'Y' + \ - '%.4f' % slot['stop'].y + '\n' - excellon_code += 'M16\n' + if form == 'dec' and tool == slot['tool']: + start_slot_x = slot['start'].x * factor + start_slot_y = slot['start'].y * factor + stop_slot_x = slot['stop'].x * factor + stop_slot_y = slot['stop'].y * factor + + excellon_code += "G00X{:.{dec}f}Y{:.{dec}f}\nM15\n".format(start_slot_x, + start_slot_y, + dec=fract) + excellon_code += "G00X{:.{dec}f}Y{:.{dec}f}\nM16\n".format(stop_slot_x, + stop_slot_y, + dec=fract) + + elif e_zeros == 'LZ' and tool == slot['tool']: + start_slot_x = slot['start'].x * factor + start_slot_y = slot['start'].y * factor + stop_slot_x = slot['stop'].x * factor + stop_slot_y = slot['stop'].y * factor + + start_slot_x_formatted = "{:.{dec}f}".format(start_slot_x, dec=fract).replace('.', '') + start_slot_y_formatted = "{:.{dec}f}".format(start_slot_y, dec=fract).replace('.', '') + stop_slot_x_formatted = "{:.{dec}f}".format(stop_slot_x, dec=fract).replace('.', '') + stop_slot_y_formatted = "{:.{dec}f}".format(stop_slot_y, dec=fract).replace('.', '') + + # extract whole part and decimal part + start_slot_x_formatted = start_slot_x_formatted.partition('.') + start_slot_y_formatted = start_slot_y_formatted.partition('.') + stop_slot_x_formatted = stop_slot_x_formatted.partition('.') + stop_slot_y_formatted = stop_slot_y_formatted.partition('.') + + # left padd the 'whole' part with zeros + start_x_whole = start_slot_x_formatted[0].rjust(whole, '0') + start_y_whole = start_slot_y_formatted[0].rjust(whole, '0') + stop_x_whole = stop_slot_x_formatted[0].rjust(whole, '0') + stop_y_whole = stop_slot_y_formatted[0].rjust(whole, '0') + + # restore the coordinate padded in the left with 0 and added the decimal part + # without the decinal dot + start_slot_x_formatted = start_x_whole + start_slot_x_formatted[2] + start_slot_y_formatted = start_y_whole + start_slot_y_formatted[2] + stop_slot_x_formatted = stop_x_whole + stop_slot_x_formatted[2] + stop_slot_y_formatted = stop_y_whole + stop_slot_y_formatted[2] + + excellon_code += "G00X{xstart}Y{ystart}\nM15\n".format(xstart=start_slot_x_formatted, + ystart=start_slot_y_formatted) + excellon_code += "G00X{xstop}Y{ystop}\nM16\n".format(xstop=stop_slot_x_formatted, + ystop=stop_slot_y_formatted) + elif tool == slot['tool']: + start_slot_x = slot['start'].x * factor + start_slot_y = slot['start'].y * factor + stop_slot_x = slot['stop'].x * factor + stop_slot_y = slot['stop'].y * factor + length = whole + fract + + start_slot_x_formatted = "{:.{dec}f}".format(start_slot_x, dec=fract).replace('.', '') + start_slot_y_formatted = "{:.{dec}f}".format(start_slot_y, dec=fract).replace('.', '') + stop_slot_x_formatted = "{:.{dec}f}".format(stop_slot_x, dec=fract).replace('.', '') + stop_slot_y_formatted = "{:.{dec}f}".format(stop_slot_y, dec=fract).replace('.', '') + + # pad with rear zeros + start_slot_x_formatted.ljust(length, '0') + start_slot_y_formatted.ljust(length, '0') + stop_slot_x_formatted.ljust(length, '0') + stop_slot_y_formatted.ljust(length, '0') + + excellon_code += "G00X{xstart}Y{ystart}\nM15\n".format(xstart=start_slot_x_formatted, + ystart=start_slot_y_formatted) + excellon_code += "G00X{xstop}Y{ystop}\nM16\n".format(xstop=stop_slot_x_formatted, + ystop=stop_slot_y_formatted) except Exception as e: log.debug(str(e)) - return has_slots, excellon_code - - def export_excellon_ndecimals(self, whole, fract, units): - """ - Returns two values, first is a boolean , if 1 then the file has slots and second contain the Excellon code - :return: has_slots and Excellon_code - """ - - excellon_code = '' - units = units - - # store here if the file has slots, return 1 if any slots, 0 if only drills - has_slots = 0 - - # drills processing - try: - for tool in self.tools: - if int(tool) < 10: - excellon_code += 'T0' + str(tool) + '\n' - else: - excellon_code += 'T' + str(tool) + '\n' - - for drill in self.drills: - if tool == drill['tool']: - drill_x = drill['point'].x - drill_y = drill['point'].y - if units == 'MM': - drill_x /= 25.4 - drill_y /= 25.4 - exc_x_formatted = ('%.4f' % drill_x).replace('.', '') - if drill_x < 10: - exc_x_formatted = '0' + exc_x_formatted - - exc_y_formatted = ('%.4f' % drill_y).replace('.', '') - if drill_y < 10: - exc_y_formatted = '0' + exc_y_formatted - - excellon_code += 'X' + exc_x_formatted + 'Y' + exc_y_formatted + '\n' - except Exception as e: - log.debug(str(e)) - - # slots processing - try: - if self.slots: - has_slots = 1 - for tool in self.tools: - if int(tool) < 10: - excellon_code += 'T0' + str(tool) + '\n' - else: - excellon_code += 'T' + str(tool) + '\n' - - for slot in self.slots: - if tool == slot['tool']: - start_slot_x = slot['start'].x - start_slot_y = slot['start'].y - stop_slot_x = slot['stop'].x - stop_slot_y = slot['stop'].y - if units == 'MM': - start_slot_x /= 25.4 - start_slot_y /= 25.4 - stop_slot_x /= 25.4 - stop_slot_y /= 25.4 - - start_slot_x_formatted = ('%.4f' % start_slot_x).replace('.', '') - if start_slot_x < 10: - start_slot_x_formatted = '0' + start_slot_x_formatted - - start_slot_y_formatted = ('%.4f' % start_slot_y).replace('.', '') - if start_slot_y < 10: - start_slot_y_formatted = '0' + start_slot_y_formatted - - stop_slot_x_formatted = ('%.4f' % stop_slot_x).replace('.', '') - if stop_slot_x < 10: - stop_slot_x_formatted = '0' + stop_slot_x_formatted - - stop_slot_y_formatted = ('%.4f' % stop_slot_y).replace('.', '') - if stop_slot_y < 10: - stop_slot_y_formatted = '0' + stop_slot_y_formatted - - excellon_code += 'G00' + 'X' + start_slot_x_formatted + 'Y' + \ - start_slot_y_formatted + '\n' - excellon_code += 'M15\n' - excellon_code += 'G01' + 'X' + stop_slot_x_formatted + 'Y' + \ - stop_slot_y_formatted + '\n' - excellon_code += 'M16\n' - except Exception as e: - log.debug(str(e)) + if not self.drills and not self.slots: + log.debug("FlatCAMObj.FlatCAMExcellon.export_excellon() --> Excellon Object is empty: no drills, no slots.") + return 'fail' return has_slots, excellon_code diff --git a/ObjectCollection.py b/ObjectCollection.py index 45eb3a38..e9d9c740 100644 --- a/ObjectCollection.py +++ b/ObjectCollection.py @@ -507,17 +507,20 @@ class ObjectCollection(QtCore.QAbstractItemModel): self.app.ui.menuprojectcopy.setEnabled(sel) self.app.ui.menuprojectedit.setEnabled(sel) self.app.ui.menuprojectdelete.setEnabled(sel) + self.app.ui.menuprojectsave.setEnabled(sel) self.app.ui.menuprojectproperties.setEnabled(sel) if sel: self.app.ui.menuprojectgeneratecnc.setVisible(True) self.app.ui.menuprojectedit.setVisible(True) + self.app.ui.menuprojectsave.setVisible(True) for obj in self.get_selected(): if type(obj) != FlatCAMGeometry: self.app.ui.menuprojectgeneratecnc.setVisible(False) if type(obj) != FlatCAMGeometry and type(obj) != FlatCAMExcellon: self.app.ui.menuprojectedit.setVisible(False) + self.app.ui.menuprojectsavet.setVisible(False) else: self.app.ui.menuprojectgeneratecnc.setVisible(False) diff --git a/README.md b/README.md index 1bd12791..945cfe77 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,11 @@ CAD program, and create G-Code for Isolation routing. 15.02.2019 -- rearranged the FIle and Edit menu's and added some explanatory tooltips on certain menu items that could be seen as cryptic +- rearranged the File and Edit menu's and added some explanatory tooltips on certain menu items that could be seen as cryptic - added Excellon Export Options in Edit -> Preferences - started to work in using the Excellon Export parameters +- remade the Excellon export function to work with parameters entered in Edit -> Preferences -> Excellon Export +- added a new entry in the Project Context Menu named 'Save'. It will actually work for Geometry and it will do Export DXF and for Excellon and it will do Export Excellon 14.02.2019