Parcourir la source

Merged in Beta_hpgl (pull request #128)

Beta hpgl
Marius Stanciu il y a 7 ans
Parent
commit
7f57e8f8b2
7 fichiers modifiés avec 192 ajouts et 31 suppressions
  1. 2 1
      .gitignore
  2. 11 11
      FlatCAMApp.py
  3. 69 17
      FlatCAMObj.py
  4. 2 2
      ObjectUI.py
  5. 15 0
      README.md
  6. 17 0
      camlib.py
  7. 76 0
      postprocessors/hpgl.py

+ 2 - 1
.gitignore

@@ -1,3 +1,4 @@
 *.pyc
 .idea/
-tests/tmp/
+tests/tmp/
+build/

+ 11 - 11
FlatCAMApp.py

@@ -87,8 +87,8 @@ class App(QtCore.QObject):
     log.addHandler(handler)
 
     # Version
-    version = 8.901
-    version_date = "2019/01/09"
+    version = 8.902
+    version_date = "2019/01/20"
     beta = True
 
     # URL for update checks and statistics
@@ -2093,9 +2093,9 @@ class App(QtCore.QObject):
         if self.collection.get_list():
             msgbox = QtWidgets.QMessageBox()
             # msgbox.setText("<B>Save changes ...</B>")
-            msgbox.setInformativeText("There are files/objects opened in FlatCAM. "
-                                      "\n\n"
-                                      "Do you want to Save the project?")
+            msgbox.setText("There are files/objects opened in FlatCAM. "
+                           "\n"
+                           "Do you want to Save the project?")
             msgbox.setWindowTitle("Save changes")
             msgbox.setWindowIcon(QtGui.QIcon('share/save_as.png'))
             msgbox.setStandardButtons(QtWidgets.QMessageBox.Cancel | QtWidgets.QMessageBox.Ok)
@@ -2229,9 +2229,9 @@ class App(QtCore.QObject):
         if self.collection.get_list():
             msgbox = QtWidgets.QMessageBox()
             # msgbox.setText("<B>Save changes ...</B>")
-            msgbox.setInformativeText("There are files/objects opened in FlatCAM. "
-                                      "\n\n"
-                                      "Do you want to Save the project?")
+            msgbox.setText("There are files/objects opened in FlatCAM. "
+                           "\n"
+                           "Do you want to Save the project?")
             msgbox.setWindowTitle("Save changes")
             msgbox.setWindowIcon(QtGui.QIcon('share/save_as.png'))
             msgbox.setStandardButtons(QtWidgets.QMessageBox.Cancel | QtWidgets.QMessageBox.Ok)
@@ -3962,9 +3962,9 @@ class App(QtCore.QObject):
         if self.collection.get_list():
             msgbox = QtWidgets.QMessageBox()
             # msgbox.setText("<B>Save changes ...</B>")
-            msgbox.setInformativeText("There are files/objects opened in FlatCAM. "
-                                      "Creating a New project will delete them.\n\n"
-                                      "Do you want to Save the project?")
+            msgbox.setText("There are files/objects opened in FlatCAM.\n"
+                           "Creating a New project will delete them.\n"
+                           "Do you want to Save the project?")
             msgbox.setWindowTitle("Save changes")
             msgbox.setWindowIcon(QtGui.QIcon('share/save_as.png'))
             msgbox.setStandardButtons(QtWidgets.QMessageBox.Cancel | QtWidgets.QMessageBox.Ok)

+ 69 - 17
FlatCAMObj.py

@@ -1134,6 +1134,9 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
         })
 
         for name in list(self.app.postprocessors.keys()):
+            # the HPGL postprocessor is only for Geometry not for Excellon job therefore don't add it
+            if name == 'hpgl':
+                continue
             self.ui.pp_excellon_name_cb.addItem(name)
 
         # Fill form fields
@@ -2104,8 +2107,13 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
         self.ui.tool_offset_entry.hide()
         self.ui.tool_offset_lbl.hide()
 
-        assert isinstance(self.ui, GeometryObjectUI), \
-            "Expected a GeometryObjectUI, got %s" % type(self.ui)
+        # used to store the state of the mpass_cb if the selected postproc for geometry is hpgl
+        self.old_pp_state = self.default_data['multidepth']
+        self.old_toolchangeg_state = self.default_data['toolchange']
+
+        if not isinstance(self.ui, GeometryObjectUI):
+            log.debug("Expected a GeometryObjectUI, got %s" % type(self.ui))
+            return
 
         self.ui.geo_tools_table.setupContextMenu()
         self.ui.geo_tools_table.addContextMenu(
@@ -2116,6 +2124,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
         self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
         self.ui.generate_cnc_button.clicked.connect(self.on_generatecnc_button_click)
         self.ui.paint_tool_button.clicked.connect(self.app.paint_tool.run)
+        self.ui.pp_geometry_name_cb.activated.connect(self.on_pp_changed)
 
     def set_tool_offset_visibility(self, current_row):
         if current_row is None:
@@ -2793,6 +2802,24 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
             item[0] = str(item[0])
         return table_tools_items
 
+    def on_pp_changed(self):
+        current_pp = self.ui.pp_geometry_name_cb.get_value()
+        if current_pp == 'hpgl':
+            self.old_pp_state = self.ui.mpass_cb.get_value()
+            self.old_toolchangeg_state = self.ui.toolchangeg_cb.get_value()
+
+            self.ui.mpass_cb.set_value(False)
+            self.ui.mpass_cb.setDisabled(True)
+
+            self.ui.toolchangeg_cb.set_value(True)
+            self.ui.toolchangeg_cb.setDisabled(True)
+        else:
+            self.ui.mpass_cb.set_value(self.old_pp_state)
+            self.ui.mpass_cb.setDisabled(False)
+
+            self.ui.toolchangeg_cb.set_value(self.old_toolchangeg_state)
+            self.ui.toolchangeg_cb.setDisabled(False)
+
     def on_generatecnc_button_click(self, *args):
 
         self.app.report_usage("geometry_on_generatecnc_button")
@@ -3771,21 +3798,24 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
         if 'Roland' in self.pp_excellon_name or 'Roland' in self.pp_geometry_name:
             _filter_ = "RML1 Files (*.rol);;" \
                        "All Files (*.*)"
+        elif 'hpgl' in self.pp_geometry_name:
+            _filter_ = "HPGL Files (*.plt);;" \
+                       "All Files (*.*)"
         else:
             _filter_ = "G-Code Files (*.nc);;G-Code Files (*.txt);;G-Code Files (*.tap);;G-Code Files (*.cnc);;" \
                        "G-Code Files (*.g-code);;All Files (*.*)"
         try:
             filename = str(QtWidgets.QFileDialog.getSaveFileName(
-                caption="Export G-Code ...", directory=self.app.get_last_save_folder(), filter=_filter_)[0])
+                caption="Export Machine Code ...", directory=self.app.get_last_save_folder(), filter=_filter_)[0])
         except TypeError:
-            filename = str(QtWidgets.QFileDialog.getSaveFileName(caption="Export G-Code ...", filter=_filter_)[0])
+            filename = str(QtWidgets.QFileDialog.getSaveFileName(caption="Export Machine Code ...", filter=_filter_)[0])
 
         preamble = str(self.ui.prepend_text.get_value())
         postamble = str(self.ui.append_text.get_value())
 
         self.export_gcode(filename, preamble=preamble, postamble=postamble)
         self.app.file_saved.emit("gcode", filename)
-        self.app.inform.emit("[success] G-Code file saved to: %s" % filename)
+        self.app.inform.emit("[success] Machine Code file saved to: %s" % filename)
 
     def on_modifygcode_button_click(self, *args):
         # add the tab if it was closed
@@ -3819,11 +3849,16 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
         log.debug("FlatCAMCNCJob.gcode_header()")
         time_str = "{:%A, %d %B %Y at %H:%M}".format(datetime.now())
         marlin = False
+        hpgl = False
+
         try:
             for key in self.cnc_tools:
                 if self.cnc_tools[key]['data']['ppname_g'] == 'marlin':
                     marlin = True
                     break
+                if self.cnc_tools[key]['data']['ppname_g'] == 'hpgl':
+                    hpgl = True
+                    break
         except Exception as e:
             log.debug("FlatCAMCNCJob.gcode_header() error: --> %s" % str(e))
             try:
@@ -3834,37 +3869,49 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
             except:
                 pass
 
-        if marlin is False:
-            gcode = '(G-CODE GENERATED BY FLATCAM v%s - www.flatcam.org - Version Date: %s)\n' % \
+        if marlin is True:
+            gcode = ';Marlin 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 += '(Name: ' + str(self.options['name']) + ')\n'
-            gcode += '(Type: ' + "G-code from " + str(self.options['type']) + ')\n'
+            gcode += ';Name: ' + str(self.options['name']) + '\n'
+            gcode += ';Type: ' + "G-code from " + str(self.options['type']) + '\n'
 
             # 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 += ';Units: ' + self.units.upper() + '\n' + "\n"
+            gcode += ';Created on ' + time_str + '\n' + '\n'
+        elif hpgl is True:
+            gcode = 'CO "HPGL CODE GENERATED BY FLATCAM v%s - www.flatcam.org - Version Date:    %s' % \
+                    (str(self.app.version), str(self.app.version_date)) + '";\n'
+
+            gcode += 'CO "Name: ' + str(self.options['name']) + '";\n'
+            gcode += 'CO "Type: ' + "HPGL code from " + str(self.options['type']) + '";\n'
 
+            # 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 += 'CO "Units: ' + self.units.upper() + '";\n'
+            gcode += 'CO "Created on ' + time_str + '";\n'
         else:
-            gcode = ';G-CODE GENERATED BY FLATCAM v%s - www.flatcam.org - Version Date: %s\n' % \
+            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 += ';Name: ' + str(self.options['name']) + '\n'
-            gcode += ';Type: ' + "G-code from " + str(self.options['type']) + '\n'
+            gcode += '(Name: ' + str(self.options['name']) + ')\n'
+            gcode += '(Type: ' + "G-code from " + str(self.options['type']) + ')\n'
 
             # 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 += '(Units: ' + self.units.upper() + ')\n' + "\n"
+            gcode += '(Created on ' + time_str + ')\n' + '\n'
 
         return gcode
 
     def export_gcode(self, filename=None, preamble='', postamble='', to_file=False):
         gcode = ''
         roland = False
+        hpgl = False
 
         # detect if using Roland postprocessor
         try:
@@ -3872,6 +3919,9 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
                 if self.cnc_tools[key]['data']['ppname_g'] == 'Roland_MDX_20':
                     roland = True
                     break
+                if self.cnc_tools[key]['data']['ppname_g'] == 'hpgl':
+                    hpgl = True
+                    break
         except:
             try:
                 for key in self.cnc_tools:
@@ -3882,7 +3932,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
                 pass
 
         # do not add gcode_header when using the Roland postprocessor, add it for every other postprocessor
-        if roland is False:
+        if roland is False and hpgl is False:
             gcode = self.gcode_header()
 
         # detect if using multi-tool and make the Gcode summation correctly for each case
@@ -3897,6 +3947,8 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
 
         if roland is True:
             g = preamble + gcode + postamble
+        elif hpgl is True:
+            g = self.gcode_header() + preamble + gcode + postamble
         else:
             # fix so the preamble gets inserted in between the comments header and the actual start of GCODE
             g_idx = gcode.rfind('G20')

+ 2 - 2
ObjectUI.py

@@ -882,7 +882,7 @@ class GeometryObjectUI(ObjectUI):
         self.toolchangeg_cb = FCCheckBox("Tool change")
         self.toolchangeg_cb.setToolTip(
             "Include tool-change sequence\n"
-            "in G-Code (Pause for tool change)."
+            "in the Machine Code (Pause for tool change)."
         )
         self.toolchangez_entry = LengthEntry()
 
@@ -982,7 +982,7 @@ class GeometryObjectUI(ObjectUI):
         pp_label = QtWidgets.QLabel("PostProcessor:")
         pp_label.setToolTip(
             "The Postprocessor file that dictates\n"
-            "Gcode output."
+            "the Machine Code (like GCode, RML, HPGL) output."
         )
         self.grid3.addWidget(pp_label, 16, 0)
         self.pp_geometry_name_cb = FCComboBox()

+ 15 - 0
README.md

@@ -9,6 +9,21 @@ CAD program, and create G-Code for Isolation routing.
 
 =================================================
 
+20.01.2019
+
+- fixed the HPGL code geometry rendering when travel
+- fixed the message box layout when asking to save the current work
+- made sure that whenever the HPGL postprocessor is selected the Toolchange is always ON and the MultiDepth is OFF
+- the HPGL postprocessor entry is not allowed in Excellon Object postprocessor selection combobox as it is only applicable for Geometry
+- when saving HPGL code it will be saved as a file with extension .plt
+- the units mentioned in HPGL format are only METRIC therefore if FlatCAM units are in INCH they will be transform to METRIC
+- the minimum unit in HPGL is 0.025mm therefore the coordinates are rounded to a multiple of 0.025mm
+
+19.01.2019
+
+- added initial implementation of HPGL postprocessor
+- fixed display HPGL code geometry on canvas
+
 11.01.2019
 
 - added a status message for font parsing

+ 17 - 0
camlib.py

@@ -5212,6 +5212,21 @@ class CNCjob(Geometry):
                 command['Y'] = float(match_z.group(2).replace(" ", "")) * 0.025
                 command['Z'] = float(match_z.group(3).replace(" ", "")) * 0.025
 
+        elif 'hpgl' in self.pp_excellon_name or 'hpgl' in self.pp_geometry_name:
+            match_pa = re.search(r"^PA(\s*-?\d+\.\d+?),(\s*\s*-?\d+\.\d+?)*;$", gline)
+            if match_pa:
+                command['G'] = 0
+                command['X'] = float(match_pa.group(1).replace(" ", ""))
+                command['Y'] = float(match_pa.group(2).replace(" ", ""))
+            match_pen = re.search(r"^(P[U|D])", gline)
+            if match_pen:
+                if match_pen.group(1) == 'PU':
+                    # the value does not matter, only that it is positive so the gcode_parse() know it is > 0,
+                    # therefore the move is of kind T (travel)
+                    command['Z'] = 1
+                else:
+                    command['Z'] = 0
+
         else:
             match = re.search(r'^\s*([A-Z])\s*([\+\-\.\d\s]+)', gline)
             while match:
@@ -5260,6 +5275,8 @@ class CNCjob(Geometry):
             if 'Z' in gobj:
                 if 'Roland' in self.pp_excellon_name or 'Roland' in self.pp_geometry_name:
                     pass
+                elif 'hpgl' in self.pp_excellon_name or 'hpgl' in self.pp_geometry_name:
+                    pass
                 elif ('X' in gobj or 'Y' in gobj) and gobj['Z'] != current['Z']:
                     log.warning("Non-orthogonal motion: From %s" % str(current))
                     log.warning("  To: %s" % str(gobj))

+ 76 - 0
postprocessors/hpgl.py

@@ -0,0 +1,76 @@
+from FlatCAMPostProc import *
+
+
+# for Roland Postprocessors it is mandatory for the postprocessor name (python file and class name, both of them must be
+# the same) to contain the following keyword, case-sensitive: 'Roland' without the quotes.
+class hpgl(FlatCAMPostProc):
+
+    coordinate_format = "%.*f"
+
+    def start_code(self, p):
+        gcode = 'IN;'
+        return gcode
+
+    def startz_code(self, p):
+        return ''
+
+    def lift_code(self, p):
+        gcode = 'PU;' + '\n'
+        return gcode
+
+    def down_code(self, p):
+        gcode = 'PD;' + '\n'
+        return gcode
+
+    def toolchange_code(self, p):
+        return 'SP%d;' % int(p.tool)
+
+    def up_to_zero_code(self, p):
+        return ''
+
+    def position_code(self, p):
+        units = str(p['units']).lower()
+
+        # we work only with METRIC units because HPGL mention only metric units so if FlatCAM units are INCH we
+        # transform them in METRIC
+        if units == 'in':
+            x = p.x * 25.4
+            y = p.y * 25.4
+        else:
+            x = p.x
+            y = p.y
+
+        # we need to have the coordinates as multiples of 0.025mm
+        x = round(x / 0.025) * 25 / 1000
+        y = round(y / 0.025) * 25 / 1000
+
+        return ('PA' + self.coordinate_format + ',' + self.coordinate_format + ';') % \
+               (p.coords_decimals, x, p.coords_decimals, y)
+
+    def rapid_code(self, p):
+        return self.position_code(p).format(**p)
+
+    def linear_code(self, p):
+        return self.position_code(p).format(**p)
+
+    def end_code(self, p):
+        gcode = self.position_code(p).format(**p)
+        return gcode
+
+    def feedrate_code(self, p):
+        return ''
+
+    def feedrate_z_code(self, p):
+        return ''
+
+    def feedrate_rapid_code(self, p):
+        return ''
+
+    def spindle_code(self, p):
+        return ''
+
+    def dwell_code(self, p):
+        return ''
+
+    def spindle_stop_code(self,p):
+        return ''