diff --git a/FlatCAMApp.py b/FlatCAMApp.py index a43f9da6..e735f676 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -651,6 +651,8 @@ class App(QtCore.QObject): "cncjob_coords_decimals": 4, "cncjob_fr_decimals": 2, "cncjob_steps_per_circle": 128, + "cncjob_footer": False, + "cncjob_line_ending": False, # CNC Job Options "cncjob_prepend": "", @@ -1188,6 +1190,7 @@ class App(QtCore.QObject): "cncjob_coords_decimals": self.ui.cncjob_defaults_form.cncjob_gen_group.coords_dec_entry, "cncjob_fr_decimals": self.ui.cncjob_defaults_form.cncjob_gen_group.fr_dec_entry, "cncjob_steps_per_circle": self.ui.cncjob_defaults_form.cncjob_gen_group.steps_per_circle_entry, + "cncjob_line_ending": self.ui.cncjob_defaults_form.cncjob_gen_group.line_ending_cb, # CNC Job Options "cncjob_prepend": self.ui.cncjob_defaults_form.cncjob_opt_group.prepend_text, diff --git a/FlatCAMObj.py b/FlatCAMObj.py index b4de03ca..a78f6196 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -6071,13 +6071,24 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob): self.app.inform.emit('[success] %s...' % _('Loaded Machine Code into Code Editor')) - def gcode_header(self): + def gcode_header(self, comment_start_symbol=None, comment_stop_symbol=None): + """ + Will create a header to be added to all GCode files generated by FlatCAM + + :param comment_start_symbol: a symbol to be used as the first symbol in a comment + :param comment_stop_symbol: a symbol to be used as the last symbol in a comment + :return: a string with a GCode header + """ + log.debug("FlatCAMCNCJob.gcode_header()") time_str = "{:%A, %d %B %Y at %H:%M}".format(datetime.now()) marlin = False hpgl = False probe_pp = False + start_comment = comment_start_symbol if comment_start_symbol is not None else '(' + stop_comment = comment_stop_symbol if comment_stop_symbol is not None else ')' + try: for key in self.cnc_tools: ppg = self.cnc_tools[key]['data']['ppname_g'] @@ -6151,17 +6162,17 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob): gcode += '(Units: ' + self.units.upper() + ')\n' + "\n" gcode += '(Created on ' + time_str + ')\n' + '\n' else: - gcode = '(G-CODE GENERATED BY FLATCAM v%s - www.flatcam.org - Version Date: %s)\n' % \ - (str(self.app.version), str(self.app.version_date)) + '\n' + gcode = '%sG-CODE GENERATED BY FLATCAM v%s - www.flatcam.org - Version Date: %s%s\n' % \ + (start_comment, str(self.app.version), str(self.app.version_date), stop_comment) + '\n' - gcode += '(Name: ' + str(self.options['name']) + ')\n' - gcode += '(Type: ' + "G-code from " + str(self.options['type']) + ')\n' + gcode += '%sName: ' % start_comment + str(self.options['name']) + '%s\n' % stop_comment + gcode += '%sType: ' % start_comment + "G-code from " + str(self.options['type']) + '%s\n' % stop_comment # if str(p['options']['type']) == 'Excellon' or str(p['options']['type']) == 'Excellon Geometry': # gcode += '(Tools in use: ' + str(p['options']['Tools_in_use']) + ')\n' - gcode += '(Units: ' + self.units.upper() + ')\n' + "\n" - gcode += '(Created on ' + time_str + ')\n' + '\n' + gcode += '%sUnits: ' % start_comment + self.units.upper() + '%s\n' % stop_comment + "\n" + gcode += '%sCreated on ' % start_comment + time_str + '%s\n' % stop_comment + '\n' return gcode @@ -6177,6 +6188,15 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob): return 'M02' def export_gcode(self, filename=None, preamble='', postamble='', to_file=False): + """ + This will save the GCode from the Gcode object to a file on the OS filesystem + + :param filename: filename for the GCode file + :param preamble: a custom Gcode block to be added at the beginning of the Gcode file + :param postamble: a custom Gcode block to be added at the end of the Gcode file + :param to_file: if False then no actual file is saved but the app will know that a file was created + :return: None + """ gcode = '' roland = False hpgl = False @@ -6241,7 +6261,9 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob): _("G-code does not have a units code: either G20 or G21")) return - g = gcode[:g_idx] + preamble + '\n' + gcode[g_idx:] + postamble + self.gcode_footer() + footer = self.app.defaults['cncjob_footer'] + end_gcode = self.gcode_footer() if footer is True else '' + g = gcode[:g_idx] + preamble + '\n' + gcode[g_idx:] + postamble + end_gcode # if toolchange custom is used, replace M6 code with the code from the Toolchange Custom Text box if self.ui.toolchange_cb.get_value() is True: @@ -6258,15 +6280,20 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob): self.app.inform.emit('[success] %s' % _("Toolchange G-code was replaced by a custom code.")) - # lines = StringIO(self.gcode) lines = StringIO(g) # Write if filename is not None: try: - with open(filename, 'w') as f: - for line in lines: - f.write(line) + force_windows_line_endings = self.app.defaults['cncjob_line_ending'] + if force_windows_line_endings and sys.platform != 'win32': + with open(filename, 'w', newline='\r\n') as f: + for line in lines: + f.write(line) + else: + with open(filename, 'w') as f: + for line in lines: + f.write(line) except FileNotFoundError: self.app.inform.emit('[WARNING_NOTCL] %s' % _("No such file or directory")) diff --git a/README.md b/README.md index c7b60f3e..97f55356 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ CAD program, and create G-Code for Isolation routing. 7.11.2019 - added the '.ngc' fiel extension to the GCode Save file dialog filter +- made the 'M2' Gcode command footer optional, default is False (can be set using the TclCommand: set_sys cncjob_footer True) +- added a setting in Preferences to force the GCode output to have the Windows line-endings even for non-Windows OS's 6.11.2019 diff --git a/flatcamGUI/PreferencesUI.py b/flatcamGUI/PreferencesUI.py index 546567ea..0faeae7a 100644 --- a/flatcamGUI/PreferencesUI.py +++ b/flatcamGUI/PreferencesUI.py @@ -3458,18 +3458,15 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI): grid0.addWidget(QtWidgets.QLabel(''), 1, 2) # Display Annotation - self.annotation_label = QtWidgets.QLabel('%s:' % _("Display Annotation")) - self.annotation_label.setToolTip( + self.annotation_cb = FCCheckBox(_("Display Annotation")) + self.annotation_cb.setToolTip( _("This selects if to display text annotation on the plot.\n" "When checked it will display numbers in order for each end\n" "of a travel line." ) ) - self.annotation_cb = FCCheckBox() - grid0.addWidget(self.annotation_label, 2, 0) - grid0.addWidget(self.annotation_cb, 2, 1) - grid0.addWidget(QtWidgets.QLabel(''), 2, 2) + grid0.addWidget(self.annotation_cb, 2, 0, 1, 3) # ################################################################### # Number of circle steps for circular aperture linear approximation # @@ -3547,6 +3544,15 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI): coords_type_label.hide() self.coords_type_radio.hide() + # Line Endings + self.line_ending_cb = FCCheckBox(_("Force Windows style line-ending")) + self.line_ending_cb.setToolTip( + _("When checked will force a Windows style line-ending\n" + "(\\r\\n) on non-Windows OS's.") + ) + + grid0.addWidget(self.line_ending_cb, 9, 0, 1, 3) + self.layout.addStretch()