Просмотр исходного кода

- working on a proper GCode Editor

Marius Stanciu 5 лет назад
Родитель
Сommit
9e8ab610b4
7 измененных файлов с 245 добавлено и 21 удалено
  1. 4 0
      CHANGELOG.md
  2. 13 9
      appEditors/AppTextEditor.py
  3. 199 0
      appEditors/appGCodeEditor.py
  4. 7 2
      appGUI/GUIElements.py
  5. 1 1
      appGUI/ObjectUI.py
  6. 18 3
      appObjects/FlatCAMCNCJob.py
  7. 3 6
      app_Main.py

+ 4 - 0
CHANGELOG.md

@@ -7,6 +7,10 @@ CHANGELOG for FlatCAM beta
 
 
 =================================================
 =================================================
 
 
+22.07.2020
+
+- working on a proper GCode Editor
+
 21.07.2020
 21.07.2020
 
 
 - updated the FCRadio class with a method that allow disabling certain options
 - updated the FCRadio class with a method that allow disabling certain options

+ 13 - 9
appEditors/AppTextEditor.py

@@ -5,7 +5,7 @@
 # MIT Licence                                              #
 # MIT Licence                                              #
 # ##########################################################
 # ##########################################################
 
 
-from appGUI.GUIElements import FCFileSaveDialog, FCEntry, FCTextAreaExtended, FCTextAreaLineNumber
+from appGUI.GUIElements import FCFileSaveDialog, FCEntry, FCTextAreaExtended, FCTextAreaLineNumber, FCButton
 from PyQt5 import QtPrintSupport, QtWidgets, QtCore, QtGui
 from PyQt5 import QtPrintSupport, QtWidgets, QtCore, QtGui
 
 
 from reportlab.platypus import SimpleDocTemplate, Paragraph
 from reportlab.platypus import SimpleDocTemplate, Paragraph
@@ -30,6 +30,7 @@ class AppTextEditor(QtWidgets.QWidget):
 
 
         self.app = app
         self.app = app
         self.plain_text = plain_text
         self.plain_text = plain_text
+        self.callback = lambda x: None
 
 
         self.setSizePolicy(
         self.setSizePolicy(
             QtWidgets.QSizePolicy.MinimumExpanding,
             QtWidgets.QSizePolicy.MinimumExpanding,
@@ -71,17 +72,17 @@ class AppTextEditor(QtWidgets.QWidget):
         if text:
         if text:
             self.code_editor.setPlainText(text)
             self.code_editor.setPlainText(text)
 
 
-        self.buttonPreview = QtWidgets.QPushButton(_('Print Preview'))
+        self.buttonPreview = FCButton(_('Print Preview'))
         self.buttonPreview.setIcon(QtGui.QIcon(self.app.resource_location + '/preview32.png'))
         self.buttonPreview.setIcon(QtGui.QIcon(self.app.resource_location + '/preview32.png'))
         self.buttonPreview.setToolTip(_("Open a OS standard Preview Print window."))
         self.buttonPreview.setToolTip(_("Open a OS standard Preview Print window."))
         self.buttonPreview.setMinimumWidth(100)
         self.buttonPreview.setMinimumWidth(100)
 
 
-        self.buttonPrint = QtWidgets.QPushButton(_('Print Code'))
+        self.buttonPrint = FCButton(_('Print Code'))
         self.buttonPrint.setIcon(QtGui.QIcon(self.app.resource_location + '/printer32.png'))
         self.buttonPrint.setIcon(QtGui.QIcon(self.app.resource_location + '/printer32.png'))
         self.buttonPrint.setToolTip(_("Open a OS standard Print window."))
         self.buttonPrint.setToolTip(_("Open a OS standard Print window."))
         self.buttonPrint.setMinimumWidth(100)
         self.buttonPrint.setMinimumWidth(100)
 
 
-        self.buttonFind = QtWidgets.QPushButton(_('Find in Code'))
+        self.buttonFind = FCButton(_('Find in Code'))
         self.buttonFind.setIcon(QtGui.QIcon(self.app.resource_location + '/find32.png'))
         self.buttonFind.setIcon(QtGui.QIcon(self.app.resource_location + '/find32.png'))
         self.buttonFind.setToolTip(_("Will search and highlight in yellow the string in the Find box."))
         self.buttonFind.setToolTip(_("Will search and highlight in yellow the string in the Find box."))
         self.buttonFind.setMinimumWidth(100)
         self.buttonFind.setMinimumWidth(100)
@@ -89,7 +90,7 @@ class AppTextEditor(QtWidgets.QWidget):
         self.entryFind = FCEntry()
         self.entryFind = FCEntry()
         self.entryFind.setToolTip(_("Find box. Enter here the strings to be searched in the text."))
         self.entryFind.setToolTip(_("Find box. Enter here the strings to be searched in the text."))
 
 
-        self.buttonReplace = QtWidgets.QPushButton(_('Replace With'))
+        self.buttonReplace = FCButton(_('Replace With'))
         self.buttonReplace.setIcon(QtGui.QIcon(self.app.resource_location + '/replace32.png'))
         self.buttonReplace.setIcon(QtGui.QIcon(self.app.resource_location + '/replace32.png'))
         self.buttonReplace.setToolTip(_("Will replace the string from the Find box with the one in the Replace box."))
         self.buttonReplace.setToolTip(_("Will replace the string from the Find box with the one in the Replace box."))
         self.buttonReplace.setMinimumWidth(100)
         self.buttonReplace.setMinimumWidth(100)
@@ -101,22 +102,22 @@ class AppTextEditor(QtWidgets.QWidget):
         self.sel_all_cb.setToolTip(_("When checked it will replace all instances in the 'Find' box\n"
         self.sel_all_cb.setToolTip(_("When checked it will replace all instances in the 'Find' box\n"
                                      "with the text in the 'Replace' box.."))
                                      "with the text in the 'Replace' box.."))
 
 
-        self.button_copy_all = QtWidgets.QPushButton(_('Copy All'))
+        self.button_copy_all = FCButton(_('Copy All'))
         self.button_copy_all.setIcon(QtGui.QIcon(self.app.resource_location + '/copy_file32.png'))
         self.button_copy_all.setIcon(QtGui.QIcon(self.app.resource_location + '/copy_file32.png'))
         self.button_copy_all.setToolTip(_("Will copy all the text in the Code Editor to the clipboard."))
         self.button_copy_all.setToolTip(_("Will copy all the text in the Code Editor to the clipboard."))
         self.button_copy_all.setMinimumWidth(100)
         self.button_copy_all.setMinimumWidth(100)
 
 
-        self.buttonOpen = QtWidgets.QPushButton(_('Open Code'))
+        self.buttonOpen = FCButton(_('Open Code'))
         self.buttonOpen.setIcon(QtGui.QIcon(self.app.resource_location + '/folder32_bis.png'))
         self.buttonOpen.setIcon(QtGui.QIcon(self.app.resource_location + '/folder32_bis.png'))
         self.buttonOpen.setToolTip(_("Will open a text file in the editor."))
         self.buttonOpen.setToolTip(_("Will open a text file in the editor."))
         self.buttonOpen.setMinimumWidth(100)
         self.buttonOpen.setMinimumWidth(100)
 
 
-        self.buttonSave = QtWidgets.QPushButton(_('Save Code'))
+        self.buttonSave = FCButton(_('Save Code'))
         self.buttonSave.setIcon(QtGui.QIcon(self.app.resource_location + '/save_as.png'))
         self.buttonSave.setIcon(QtGui.QIcon(self.app.resource_location + '/save_as.png'))
         self.buttonSave.setToolTip(_("Will save the text in the editor into a file."))
         self.buttonSave.setToolTip(_("Will save the text in the editor into a file."))
         self.buttonSave.setMinimumWidth(100)
         self.buttonSave.setMinimumWidth(100)
 
 
-        self.buttonRun = QtWidgets.QPushButton(_('Run Code'))
+        self.buttonRun = FCButton(_('Run Code'))
         self.buttonRun.setToolTip(_("Will run the TCL commands found in the text file, one by one."))
         self.buttonRun.setToolTip(_("Will run the TCL commands found in the text file, one by one."))
         self.buttonRun.setMinimumWidth(100)
         self.buttonRun.setMinimumWidth(100)
 
 
@@ -162,6 +163,9 @@ class AppTextEditor(QtWidgets.QWidget):
 
 
         self.code_edited = ''
         self.code_edited = ''
 
 
+    def set_callback(self, callback):
+        self.callback = callback
+
     def handlePrint(self):
     def handlePrint(self):
         self.app.defaults.report_usage("handlePrint()")
         self.app.defaults.report_usage("handlePrint()")
 
 

+ 199 - 0
appEditors/appGCodeEditor.py

@@ -0,0 +1,199 @@
+# ##########################################################
+# FlatCAM: 2D Post-processing for Manufacturing            #
+# File Author: Marius Adrian Stanciu (c)                   #
+# Date: 07/22/2020                                         #
+# MIT Licence                                              #
+# ##########################################################
+
+from appEditors.AppTextEditor import AppTextEditor
+from appObjects import FlatCAMCNCJob
+from appGUI.GUIElements import FCFileSaveDialog, FCEntry, FCTextAreaExtended, FCTextAreaLineNumber, FCButton
+from PyQt5 import QtWidgets, QtCore, QtGui
+
+# from io import StringIO
+
+import logging
+
+import gettext
+import appTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+log = logging.getLogger('base')
+
+
+class appGCodeEditor(QtCore.QObject):
+
+    def __init__(self, app, parent=None):
+        super().__init__(parent=parent)
+
+        self.app = app
+        self.plain_text = ''
+        self.callback = lambda x: None
+
+        self.ui = appGCodeEditorUI(app=self.app)
+
+        # #################################################################################
+        # ################### SIGNALS #####################################################
+        # #################################################################################
+
+        self.gcode_obj = None
+        self.code_edited = ''
+
+    def set_ui(self):
+        pass
+
+    def build_ui(self):
+        pass
+
+    def ui_connect(self):
+        pass
+
+    def ui_disconnect(self):
+        pass
+
+    def handleTextChanged(self):
+        # enable = not self.ui.code_editor.document().isEmpty()
+        # self.ui.buttonPrint.setEnabled(enable)
+        # self.ui.buttonPreview.setEnabled(enable)
+
+        self.buttonSave.setStyleSheet("QPushButton {color: red;}")
+        self.buttonSave.setIcon(QtGui.QIcon(self.app.resource_location + '/save_as_red.png'))
+
+    def edit_fcgcode(self, cnc_obj):
+        assert isinstance(cnc_obj, FlatCAMCNCJob)
+        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.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):
+        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()")
+
+        if filt:
+            _filter_ = filt
+        else:
+            _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_)
+
+        if path:
+            file = QtCore.QFile(path)
+            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)
+                file.close()
+
+
+class appGCodeEditorUI:
+    def __init__(self, app):
+        self.app = app
+
+        # Number of decimals used by tools in this class
+        self.decimals = self.app.decimals
+
+        # ## Current application units in Upper Case
+        self.units = self.app.defaults['units'].upper()
+
+        # self.setSizePolicy(
+        #     QtWidgets.QSizePolicy.MinimumExpanding,
+        #     QtWidgets.QSizePolicy.MinimumExpanding
+        # )
+
+        self.layout = QtWidgets.QVBoxLayout()
+        self.layout.setContentsMargins(0, 0, 0, 0)
+
+        self.editor_frame = QtWidgets.QFrame()
+        self.editor_frame.setContentsMargins(0, 0, 0, 0)
+        self.layout.addWidget(self.editor_frame)
+
+        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 new TAB in the PLot Tab Area
+        # #############################################################################################################
+        self.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.gcode_editor_tab, '%s' % _("Code Editor"))
+        self.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.gcode_editor_tab.code_editor.completer_enable = False
+        self.gcode_editor_tab.buttonRun.hide()
+
+        # Switch plot_area to CNCJob tab
+        self.app.ui.plot_tab_area.setCurrentWidget(self.gcode_editor_tab)
+
+        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.gcode_editor_tab.t_frame.show()
+        self.app.proc_container.view.set_idle()
+
+        self.layout.addStretch()
+
+        # Editor
+        self.exit_editor_button = QtWidgets.QPushButton(_('Exit Editor'))
+        self.exit_editor_button.setIcon(QtGui.QIcon(self.app.resource_location + '/power16.png'))
+        self.exit_editor_button.setToolTip(
+            _("Exit from Editor.")
+        )
+        self.exit_editor_button.setStyleSheet("""
+                                          QPushButton
+                                          {
+                                              font-weight: bold;
+                                          }
+                                          """)
+        self.layout.addWidget(self.exit_editor_button)
+        # ############################ FINSIHED GUI ###################################
+        # #############################################################################
+
+    def confirmation_message(self, accepted, minval, maxval):
+        if accepted is False:
+            self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % (_("Edited value is out of range"),
+                                                                                  self.decimals,
+                                                                                  minval,
+                                                                                  self.decimals,
+                                                                                  maxval), False)
+        else:
+            self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
+
+    def confirmation_message_int(self, accepted, minval, maxval):
+        if accepted is False:
+            self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' %
+                                            (_("Edited value is out of range"), minval, maxval), False)
+        else:
+            self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)

+ 7 - 2
appGUI/GUIElements.py

@@ -1557,8 +1557,13 @@ class FCInputDialog(QtWidgets.QInputDialog):
 
 
 
 
 class FCButton(QtWidgets.QPushButton):
 class FCButton(QtWidgets.QPushButton):
-    def __init__(self, parent=None):
-        super(FCButton, self).__init__(parent)
+    def __init__(self, text=None, checkable=None, click_callback=None, parent=None):
+        super(FCButton, self).__init__(text, parent)
+        if not checkable is None:
+            self.setCheckable(checkable)
+
+        if not click_callback is None:
+            self.clicked.connect(click_callback)
 
 
     def get_value(self):
     def get_value(self):
         return self.isChecked()
         return self.isChecked()

+ 1 - 1
appGUI/ObjectUI.py

@@ -1870,7 +1870,7 @@ class CNCObjectUI(ObjectUI):
         self.custom_box.addWidget(self.updateplot_button)
         self.custom_box.addWidget(self.updateplot_button)
 
 
         # Editor
         # Editor
-        self.editor_button = QtWidgets.QPushButton(_('GCode Editor'))
+        self.editor_button = FCButton(_('GCode Editor'))
         self.editor_button.setIcon(QtGui.QIcon(self.app.resource_location + '/edit_file32.png'))
         self.editor_button.setIcon(QtGui.QIcon(self.app.resource_location + '/edit_file32.png'))
 
 
         self.editor_button.setToolTip(
         self.editor_button.setToolTip(

+ 18 - 3
appObjects/FlatCAMCNCJob.py

@@ -149,6 +149,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
 
 
         self.gcode_editor_tab = None
         self.gcode_editor_tab = None
 
 
+        self.source_file = ''
         self.units_found = self.app.defaults['units']
         self.units_found = self.app.defaults['units']
 
 
     def build_ui(self):
     def build_ui(self):
@@ -538,10 +539,14 @@ class CNCJobObject(FlatCAMObj, CNCjob):
         self.ui.name_entry.set_value(new_name)
         self.ui.name_entry.set_value(new_name)
         self.on_name_activate(silent=True)
         self.on_name_activate(silent=True)
 
 
-        preamble = str(self.ui.prepend_text.get_value())
-        postamble = str(self.ui.append_text.get_value())
+        try:
+            preamble = str(self.ui.prepend_text.get_value())
+            postamble = str(self.ui.append_text.get_value())
+            gc = self.export_gcode(filename, preamble=preamble, postamble=postamble)
+        except Exception as err:
+            log.debug("CNCJobObject.export_gcode_handler() --> %s" % str(err))
+            gc = self.export_gcode(filename)
 
 
-        gc = self.export_gcode(filename, preamble=preamble, postamble=postamble)
         if gc == 'fail':
         if gc == 'fail':
             return
             return
 
 
@@ -597,8 +602,13 @@ class CNCJobObject(FlatCAMObj, CNCjob):
         self.gcode_editor_tab.t_frame.show()
         self.gcode_editor_tab.t_frame.show()
         self.app.proc_container.view.set_idle()
         self.app.proc_container.view.set_idle()
 
 
+        self.gcode_editor_tab.buttonSave.clicked.connect(self.on_update_source_file)
+
         self.app.inform.emit('[success] %s...' % _('Loaded Machine Code into Code Editor'))
         self.app.inform.emit('[success] %s...' % _('Loaded Machine Code into Code Editor'))
 
 
+    def on_update_source_file(self):
+        self.source_file = self.gcode_editor_tab.code_editor.toPlainText()
+
     def gcode_header(self, comment_start_symbol=None, comment_stop_symbol=None):
     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
         Will create a header to be added to all GCode files generated by FlatCAM
@@ -735,6 +745,11 @@ class CNCJobObject(FlatCAMObj, CNCjob):
 
 
         include_header = True
         include_header = True
 
 
+        if preamble == '':
+            preamble = self.app.defaults["cncjob_prepend"]
+        if postamble == '':
+            preamble = self.app.defaults["cncjob_append"]
+
         try:
         try:
             if self.special_group:
             if self.special_group:
                 self.app.inform.emit('[WARNING_NOTCL] %s %s %s.' %
                 self.app.inform.emit('[WARNING_NOTCL] %s %s %s.' %

+ 3 - 6
app_Main.py

@@ -7745,10 +7745,7 @@ class App(QtCore.QObject):
         # then append the text from GCode to the text editor
         # then append the text from GCode to the text editor
         if obj.kind == 'cncjob':
         if obj.kind == 'cncjob':
             try:
             try:
-                file = obj.export_gcode(
-                    preamble=self.defaults["cncjob_prepend"],
-                    postamble=self.defaults["cncjob_append"],
-                    to_file=True)
+                file = obj.export_gcode(to_file=True)
                 if file == 'fail':
                 if file == 'fail':
                     return 'fail'
                     return 'fail'
             except AttributeError:
             except AttributeError:
@@ -8665,14 +8662,14 @@ class App(QtCore.QObject):
                 def job_thread_grb(app_obj):
                 def job_thread_grb(app_obj):
                     ret = make_gerber()
                     ret = make_gerber()
                     if ret == 'fail':
                     if ret == 'fail':
-                        self.inform.emit('[ERROR_NOTCL] %s' % _('Could not export Gerber file.'))
+                        self.inform.emit('[ERROR_NOTCL] %s' % _('Could not export file.'))
                         return
                         return
 
 
                 self.worker_task.emit({'fcn': job_thread_grb, 'params': [self]})
                 self.worker_task.emit({'fcn': job_thread_grb, 'params': [self]})
         else:
         else:
             gret = make_gerber()
             gret = make_gerber()
             if gret == 'fail':
             if gret == 'fail':
-                self.inform.emit('[ERROR_NOTCL] %s' % _('Could not export Gerber file.'))
+                self.inform.emit('[ERROR_NOTCL] %s' % _('Could not export file.'))
                 return 'fail'
                 return 'fail'
             if local_use is not None:
             if local_use is not None:
                 return gret
                 return gret