diff --git a/CHANGELOG.md b/CHANGELOG.md index f57c81e8..eb38c754 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ CHANGELOG for FlatCAM beta 22.07.2020 - working on a proper GCode Editor +- wip in the GCode Editor 21.07.2020 diff --git a/appEditors/AppExcEditor.py b/appEditors/AppExcEditor.py index 7640d406..77d1479f 100644 --- a/appEditors/AppExcEditor.py +++ b/appEditors/AppExcEditor.py @@ -2569,7 +2569,7 @@ class AppExcEditor(QtCore.QObject): self.set_ui() - # now that we hava data, create the appGUI interface and add it to the Tool Tab + # now that we have data, create the appGUI interface and add it to the Tool Tab self.build_ui(first_run=True) # we activate this after the initial build as we don't need to see the tool been populated diff --git a/appEditors/appGCodeEditor.py b/appEditors/appGCodeEditor.py index 61a4e0f2..af186ca0 100644 --- a/appEditors/appGCodeEditor.py +++ b/appEditors/appGCodeEditor.py @@ -6,8 +6,8 @@ # ########################################################## from appEditors.AppTextEditor import AppTextEditor -from appObjects import FlatCAMCNCJob -from appGUI.GUIElements import FCFileSaveDialog, FCEntry, FCTextAreaExtended, FCTextAreaLineNumber, FCButton +from appObjects.FlatCAMCNCJob import CNCJobObject +from appGUI.GUIElements import FCTextArea, FCEntry, FCButton from PyQt5 import QtWidgets, QtCore, QtGui # from io import StringIO @@ -25,7 +25,7 @@ if '_' not in builtins.__dict__: log = logging.getLogger('base') -class appGCodeEditor(QtCore.QObject): +class AppGCodeEditor(QtCore.QObject): def __init__(self, app, parent=None): super().__init__(parent=parent) @@ -34,7 +34,7 @@ class appGCodeEditor(QtCore.QObject): self.plain_text = '' self.callback = lambda x: None - self.ui = appGCodeEditorUI(app=self.app) + self.ui = AppGCodeEditorUI(app=self.app) # ################################################################################# # ################### SIGNALS ##################################################### @@ -44,10 +44,44 @@ class appGCodeEditor(QtCore.QObject): self.code_edited = '' def set_ui(self): - pass + # ############################################################################################################# + # ############# ADD a new TAB in the PLot Tab Area + # ############################################################################################################# + self.ui.gcode_editor_tab = AppTextEditor(app=self.app, plain_text=True) + + # add the tab if it was closed + self.app.ui.plot_tab_area.addTab(self.ui.gcode_editor_tab, '%s' % _("Code Editor")) + self.ui.gcode_editor_tab.setObjectName('code_editor_tab') + + # delete the absolute and relative position and messages in the infobar + self.app.ui.position_label.setText("") + self.app.ui.rel_position_label.setText("") + + self.ui.gcode_editor_tab.code_editor.completer_enable = False + self.ui.gcode_editor_tab.buttonRun.hide() + + # Switch plot_area to CNCJob tab + self.app.ui.plot_tab_area.setCurrentWidget(self.ui.gcode_editor_tab) + + self.ui.gcode_editor_tab.t_frame.hide() + + self.ui.gcode_editor_tab.t_frame.show() + self.app.proc_container.view.set_idle() + # ############################################################################################################# + # ############################################################################################################# + + self.ui.append_text.set_value(self.app.defaults["cncjob_append"]) + self.ui.prepend_text.set_value(self.app.defaults["cncjob_prepend"]) + + self.ui.exit_editor_button.buttonSave.clicked.connect(self.update_fcgcode) def build_ui(self): - pass + # Remove anything else in the GUI Selected Tab + self.app.ui.selected_scroll_area.takeWidget() + # Put ourselves in the GUI Selected Tab + self.app.ui.selected_scroll_area.setWidget(self.ui.edit_widget) + # Switch notebook to Selected page + self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab) def ui_connect(self): pass @@ -64,35 +98,31 @@ class appGCodeEditor(QtCore.QObject): self.buttonSave.setIcon(QtGui.QIcon(self.app.resource_location + '/save_as_red.png')) def edit_fcgcode(self, cnc_obj): - assert isinstance(cnc_obj, FlatCAMCNCJob) + assert isinstance(cnc_obj, CNCJobObject) self.gcode_obj = cnc_obj - preamble = str(self.ui.prepend_text.get_value()) - postamble = str(self.ui.append_text.get_value()) - gcode_text = self.gcode_obj.source_file - self.gcode_editor_tab.buttonSave.clicked.connect(self.on_update_source_file) + self.set_ui() + self.build_ui() + # then append the text from GCode to the text editor + self.ui.gcode_editor_tab.load_text(gcode_text, move_to_start=True, clear_text=True) self.app.inform.emit('[success] %s...' % _('Loaded Machine Code into Code Editor')) - self.ui.gcode_editor_tab.load_text(self, gcode_text, move_to_start=True, clear_text=True) - - def update_gcode(self): + def update_fcgcode(self): + preamble = str(self.ui.prepend_text.get_value()) + postamble = str(self.ui.append_text.get_value()) my_gcode = self.ui.gcode_editor_tab.code_editor.toPlainText() self.gcode_obj.source_file = my_gcode self.ui.gcode_editor_tab.buttonSave.setStyleSheet("") self.ui.gcode_editor_tab.setIcon(QtGui.QIcon(self.app.resource_location + '/save_as.png')) - def handleOpen(self, filt=None): - self.app.defaults.report_usage("handleOpen()") + def on_open_gcode(self): - if filt: - _filter_ = filt - else: - _filter_ = "G-Code Files (*.nc);; G-Code Files (*.txt);; G-Code Files (*.tap);; G-Code Files (*.cnc);; " \ - "All Files (*.*)" + _filter_ = "G-Code Files (*.nc);; G-Code Files (*.txt);; G-Code Files (*.tap);; G-Code Files (*.cnc);; " \ + "All Files (*.*)" path, _f = QtWidgets.QFileDialog.getOpenFileName( caption=_('Open file'), directory=self.app.get_last_folder(), filter=_filter_) @@ -102,11 +132,11 @@ class appGCodeEditor(QtCore.QObject): if file.open(QtCore.QIODevice.ReadOnly): stream = QtCore.QTextStream(file) self.code_edited = stream.readAll() - self.ui.gcode_editor_tab.load_text(self, self.code_edited, move_to_start=True, clear_text=True) + self.ui.gcode_editor_tab.load_text(self.code_edited, move_to_start=True, clear_text=True) file.close() -class appGCodeEditorUI: +class AppGCodeEditorUI: def __init__(self, app): self.app = app @@ -121,52 +151,96 @@ class appGCodeEditorUI: # QtWidgets.QSizePolicy.MinimumExpanding # ) - self.layout = QtWidgets.QVBoxLayout() - self.layout.setContentsMargins(0, 0, 0, 0) + self.gcode_editor_tab = None - self.editor_frame = QtWidgets.QFrame() - self.editor_frame.setContentsMargins(0, 0, 0, 0) - self.layout.addWidget(self.editor_frame) + self.edit_widget = QtWidgets.QWidget() + # ## Box for custom widgets + # This gets populated in offspring implementations. + layout = QtWidgets.QVBoxLayout() + self.edit_widget.setLayout(layout) - self.editor_layout = QtWidgets.QGridLayout(self.editor_frame) - self.editor_layout.setContentsMargins(2, 2, 2, 2) - self.editor_frame.setLayout(self.editor_layout) + # add a frame and inside add a vertical box layout. Inside this vbox layout I add all the Drills widgets + # this way I can hide/show the frame + self.edit_frame = QtWidgets.QFrame() + self.edit_frame.setContentsMargins(0, 0, 0, 0) + layout.addWidget(self.edit_frame) + self.edit_box = QtWidgets.QVBoxLayout() + self.edit_box.setContentsMargins(0, 0, 0, 0) + self.edit_frame.setLayout(self.edit_box) - # ############################################################################################################# - # ############# ADD a new TAB in the PLot Tab Area - # ############################################################################################################# - self.gcode_editor_tab = AppTextEditor(app=self.app, plain_text=True) + # ## Page Title box (spacing between children) + self.title_box = QtWidgets.QHBoxLayout() + self.edit_box.addLayout(self.title_box) - # add the tab if it was closed - self.app.ui.plot_tab_area.addTab(self.gcode_editor_tab, '%s' % _("Code Editor")) - self.gcode_editor_tab.setObjectName('code_editor_tab') + # ## Page Title icon + pixmap = QtGui.QPixmap(self.app.resource_location + '/flatcam_icon32.png') + self.icon = QtWidgets.QLabel() + self.icon.setPixmap(pixmap) + self.title_box.addWidget(self.icon, stretch=0) - # delete the absolute and relative position and messages in the infobar - self.app.ui.position_label.setText("") - self.app.ui.rel_position_label.setText("") + # ## Title label + self.title_label = QtWidgets.QLabel("%s" % _('GCode Editor')) + self.title_label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) + self.title_box.addWidget(self.title_label, stretch=1) - self.gcode_editor_tab.code_editor.completer_enable = False - self.gcode_editor_tab.buttonRun.hide() + # ## Object name + self.name_box = QtWidgets.QHBoxLayout() + self.edit_box.addLayout(self.name_box) + name_label = QtWidgets.QLabel(_("Name:")) + self.name_box.addWidget(name_label) + self.name_entry = FCEntry() + self.name_box.addWidget(self.name_entry) - # Switch plot_area to CNCJob tab - self.app.ui.plot_tab_area.setCurrentWidget(self.gcode_editor_tab) + # Prepend text to GCode + prependlabel = QtWidgets.QLabel('%s:' % _('Prepend to CNC Code')) + prependlabel.setToolTip( + _("Type here any G-Code commands you would\n" + "like to add at the beginning of the G-Code file.") + ) + self.edit_box.addWidget(prependlabel) - self.gcode_editor_tab.t_frame.hide() - # then append the text from GCode to the text editor - try: - self.gcode_editor_tab.load_text(self.app.gcode_edited.getvalue(), move_to_start=True, clear_text=True) - except Exception as e: - log.debug('FlatCAMCNNJob.on_edit_code_click() -->%s' % str(e)) - self.app.inform.emit('[ERROR] %s %s' % ('FlatCAMCNNJob.on_edit_code_click() -->', str(e))) - return + self.prepend_text = FCTextArea() + self.prepend_text.setPlaceholderText( + _("Type here any G-Code commands you would\n" + "like to add at the beginning of the G-Code file.") + ) + self.edit_box.addWidget(self.prepend_text) - self.gcode_editor_tab.t_frame.show() - self.app.proc_container.view.set_idle() + # Append text to GCode + appendlabel = QtWidgets.QLabel('%s:' % _('Append to CNC Code')) + appendlabel.setToolTip( + _("Type here any G-Code commands you would\n" + "like to append to the generated file.\n" + "I.e.: M2 (End of program)") + ) + self.edit_box.addWidget(appendlabel) - self.layout.addStretch() + self.append_text = FCTextArea() + self.append_text.setPlaceholderText( + _("Type here any G-Code commands you would\n" + "like to append to the generated file.\n" + "I.e.: M2 (End of program)") + ) + self.edit_box.addWidget(self.append_text) + + h_lay = QtWidgets.QHBoxLayout() + h_lay.setAlignment(QtCore.Qt.AlignVCenter) + self.edit_box.addLayout(h_lay) + + # GO Button + self.update_gcode_button = FCButton(_('Update Code')) + # self.update_gcode_button.setIcon(QtGui.QIcon(self.app.resource_location + '/save_as.png')) + self.update_gcode_button.setToolTip( + _("Update the Gcode in the Editor with the values\n" + "in the 'Prepend' and 'Append' text boxes.") + ) + + h_lay.addWidget(self.update_gcode_button) + + layout.addStretch() # Editor - self.exit_editor_button = QtWidgets.QPushButton(_('Exit Editor')) + self.exit_editor_button = FCButton(_('Exit Editor')) self.exit_editor_button.setIcon(QtGui.QIcon(self.app.resource_location + '/power16.png')) self.exit_editor_button.setToolTip( _("Exit from Editor.") @@ -177,9 +251,9 @@ class appGCodeEditorUI: font-weight: bold; } """) - self.layout.addWidget(self.exit_editor_button) - # ############################ FINSIHED GUI ################################### - # ############################################################################# + layout.addWidget(self.exit_editor_button) + # ############################ FINSIHED GUI ################################################################## + # ############################################################################################################# def confirmation_message(self, accepted, minval, maxval): if accepted is False: diff --git a/appObjects/FlatCAMCNCJob.py b/appObjects/FlatCAMCNCJob.py index a6e55750..9fd26b27 100644 --- a/appObjects/FlatCAMCNCJob.py +++ b/appObjects/FlatCAMCNCJob.py @@ -595,8 +595,7 @@ class CNCJobObject(FlatCAMObj, CNCjob): try: self.gcode_editor_tab.load_text(self.app.gcode_edited.getvalue(), move_to_start=True, clear_text=True) except Exception as e: - log.debug('FlatCAMCNNJob.on_edit_code_click() -->%s' % str(e)) - self.app.inform.emit('[ERROR] %s %s' % ('FlatCAMCNNJob.on_edit_code_click() -->', str(e))) + log.debug('FlatCAMCNCJob.on_edit_code_click() -->%s' % str(e)) return self.gcode_editor_tab.t_frame.show() diff --git a/appObjects/FlatCAMGeometry.py b/appObjects/FlatCAMGeometry.py index 76395ebd..7c85b45b 100644 --- a/appObjects/FlatCAMGeometry.py +++ b/appObjects/FlatCAMGeometry.py @@ -1881,6 +1881,7 @@ class GeometryObject(FlatCAMObj, Geometry): job_obj.z_pdepth = float(self.app.defaults["geometry_z_pdepth"]) job_obj.feedrate_probe = float(self.app.defaults["geometry_feedrate_probe"]) + total_gcode = '' for tooluid_key in list(tools_dict.keys()): tool_cnt += 1 @@ -1970,6 +1971,8 @@ class GeometryObject(FlatCAMObj, Geometry): else: dia_cnc_dict['gcode'] = res + total_gcode += res + # tell gcode_parse from which point to start drawing the lines depending on what kind of # object is the source of gcode job_obj.toolchange_xy_type = "geometry" @@ -1993,6 +1996,8 @@ class GeometryObject(FlatCAMObj, Geometry): }) dia_cnc_dict.clear() + job_obj.source_file = total_gcode + # Object initialization function for app.app_obj.new_object() # RUNNING ON SEPARATE THREAD! def job_init_multi_geometry(job_obj, app_obj): @@ -2031,6 +2036,7 @@ class GeometryObject(FlatCAMObj, Geometry): self.app.inform.emit('[ERROR_NOTCL] %s...' % _('Cancelled. Empty file, it has no geometry')) return 'fail' + total_gcode = '' for tooluid_key in list(tools_dict.keys()): tool_cnt += 1 dia_cnc_dict = deepcopy(tools_dict[tooluid_key]) @@ -2123,6 +2129,7 @@ class GeometryObject(FlatCAMObj, Geometry): return 'fail' else: dia_cnc_dict['gcode'] = res + total_gcode += res self.app.inform.emit('[success] %s' % _("G-Code parsing in progress...")) dia_cnc_dict['gcode_parsed'] = job_obj.gcode_parse() @@ -2149,6 +2156,8 @@ class GeometryObject(FlatCAMObj, Geometry): }) dia_cnc_dict.clear() + job_obj.source_file = total_gcode + if use_thread: # To be run in separate thread def job_thread(a_obj): @@ -2288,17 +2297,18 @@ class GeometryObject(FlatCAMObj, Geometry): # it seems that the tolerance needs to be a lot lower value than 0.01 and it was hardcoded initially # to a value of 0.0005 which is 20 times less than 0.01 tol = float(self.app.defaults['global_tolerance']) / 20 - job_obj.generate_from_geometry_2( - self, tooldia=tooldia, offset=offset, tolerance=tol, - z_cut=z_cut, z_move=z_move, - feedrate=feedrate, feedrate_z=feedrate_z, feedrate_rapid=feedrate_rapid, - spindlespeed=spindlespeed, dwell=dwell, dwelltime=dwelltime, - multidepth=multidepth, depthpercut=depthperpass, - toolchange=toolchange, toolchangez=toolchangez, toolchangexy=toolchangexy, - extracut=extracut, extracut_length=extracut_length, startz=startz, endz=endz, endxy=endxy, - pp_geometry_name=ppname_g + res = job_obj.generate_from_geometry_2(self, tooldia=tooldia, offset=offset, tolerance=tol, + z_cut=z_cut, z_move=z_move, feedrate=feedrate, + feedrate_z=feedrate_z, feedrate_rapid=feedrate_rapid, + spindlespeed=spindlespeed, dwell=dwell, dwelltime=dwelltime, + multidepth=multidepth, depthpercut=depthperpass, + toolchange=toolchange, toolchangez=toolchangez, + toolchangexy=toolchangexy, + extracut=extracut, extracut_length=extracut_length, + startz=startz, endz=endz, endxy=endxy, + pp_geometry_name=ppname_g ) - + job_obj.source_file = res # tell gcode_parse from which point to start drawing the lines depending on what kind of object is the # source of gcode job_obj.toolchange_xy_type = "geometry" diff --git a/appTools/ToolDrilling.py b/appTools/ToolDrilling.py index 142570d6..b62cca2c 100644 --- a/appTools/ToolDrilling.py +++ b/appTools/ToolDrilling.py @@ -1797,6 +1797,7 @@ class ToolDrilling(AppTool, Excellon): self.total_gcode_parsed += tool_gcode_parsed job_obj.gcode = self.total_gcode + job_obj.source_file = self.total_gcode job_obj.gcode_parsed = self.total_gcode_parsed if job_obj.gcode == 'fail': return 'fail' diff --git a/appTools/ToolSolderPaste.py b/appTools/ToolSolderPaste.py index e0565ba7..59e567be 100644 --- a/appTools/ToolSolderPaste.py +++ b/appTools/ToolSolderPaste.py @@ -913,6 +913,7 @@ class SolderPaste(AppTool): job_obj.options['xmax'] = xmax job_obj.options['ymax'] = ymax + total_gcode = '' for tooluid_key, tooluid_value in obj.tools.items(): # find the tool_dia associated with the tooluid_key tool_dia = tooluid_value['tooldia'] @@ -934,6 +935,7 @@ class SolderPaste(AppTool): return 'fail' else: tool_cnc_dict['gcode'] = res + total_gcode += res # ## PARSE GCODE # ## tool_cnc_dict['gcode_parsed'] = job_obj.gcode_parse() @@ -949,6 +951,8 @@ class SolderPaste(AppTool): }) tool_cnc_dict.clear() + job_obj.source_file = total_gcode + if use_thread: # To be run in separate thread def job_thread(app_obj): diff --git a/app_Main.py b/app_Main.py index 28e9f81b..ebc62447 100644 --- a/app_Main.py +++ b/app_Main.py @@ -83,6 +83,7 @@ from appEditors.AppGeoEditor import AppGeoEditor from appEditors.AppExcEditor import AppExcEditor from appEditors.AppGerberEditor import AppGerberEditor from appEditors.AppTextEditor import AppTextEditor +from appEditors.appGCodeEditor import AppGCodeEditor from appParsers.ParseHPGL2 import HPGL2 # FlatCAM Workers @@ -1574,6 +1575,12 @@ class App(QtCore.QObject): self.grb_editor = AppGerberEditor(self) except Exception as es: log.debug("app_Main.__init__() --> Gerber Editor Error: %s" % str(es)) + + try: + self.gcode_editor = AppGCodeEditor(self) + except Exception as es: + log.debug("app_Main.__init__() --> GCode Editor Error: %s" % str(es)) + self.log.debug("Finished adding FlatCAM Editor's.") self.set_ui_title(name=_("New Project - Not saved")) @@ -2226,7 +2233,10 @@ class App(QtCore.QObject): if self.ui.splitter.sizes()[0] == 0: self.ui.splitter.setSizes([1, 1]) - edited_object.on_edit_code_click() + # set call source to the Editor we go into + self.call_source = 'gcode_editor' + + self.gcode_editor.edit_fcgcode(edited_object) return # make sure that we can't select another object while in Editor Mode: diff --git a/tclCommands/TclCommandDrillcncjob.py b/tclCommands/TclCommandDrillcncjob.py index 8940a697..e6a07e0a 100644 --- a/tclCommands/TclCommandDrillcncjob.py +++ b/tclCommands/TclCommandDrillcncjob.py @@ -332,6 +332,7 @@ class TclCommandDrillcncjob(TclCommandSignaled): job_obj.excellon_optimization_type = opt_type ret_val = job_obj.generate_from_excellon_by_tool(obj, tools, use_ui=False) + job_obj.source_file = ret_val if ret_val == 'fail': return 'fail'