فهرست منبع

- Tcl Shell - added a button to delete the content of the active line
- Tcl Command Isolate - fixed to work in the new configuration

Marius 5 سال پیش
والد
کامیت
2624df10bf
6فایلهای تغییر یافته به همراه320 افزوده شده و 4 حذف شده
  1. 291 0
      AppObjects/FlatCAMGerber.py
  2. 21 4
      AppTools/ToolShell.py
  3. 3 0
      App_Main.py
  4. 5 0
      CHANGELOG.md
  5. BIN
      assets/resources/clear_line16.png
  6. BIN
      assets/resources/dark_resources/clear_line16.png

+ 291 - 0
AppObjects/FlatCAMGerber.py

@@ -121,6 +121,7 @@ class GerberObject(FlatCAMObj, Gerber):
             "bboxrounded": False,
             "aperture_display": False,
             "follow": False,
+            "milling_type": 'cl',
         })
 
         # type of isolation: 0 = exteriors, 1 = interiors, 2 = complete isolation (both interiors and exteriors)
@@ -451,6 +452,296 @@ class GerberObject(FlatCAMObj, Gerber):
 
         self.app.app_obj.new_object("geometry", name, geo_init)
 
+    def isolate(self, iso_type=None, geometry=None, dia=None, passes=None, overlap=None, outname=None, combine=None,
+                milling_type=None, follow=None, plot=True):
+        """
+        Creates an isolation routing geometry object in the project.
+
+        :param iso_type:        type of isolation to be done: 0 = exteriors, 1 = interiors and 2 = both
+        :param geometry:        specific geometry to isolate
+        :param dia:             Tool diameter
+        :param passes:          Number of tool widths to cut
+        :param overlap:         Overlap between passes in fraction of tool diameter
+        :param outname:         Base name of the output object
+        :param combine:         Boolean: if to combine passes in one resulting object in case of multiple passes
+        :param milling_type:    type of milling: conventional or climbing
+        :param follow: Boolean: if to generate a 'follow' geometry
+        :param plot: Boolean:   if to plot the resulting geometry object
+        :return:                None
+        """
+
+        if geometry is None:
+            work_geo = self.follow_geometry if follow is True else self.solid_geometry
+        else:
+            work_geo = geometry
+
+        if dia is None:
+            dia = float(self.app.defaults["tools_iso_tooldia"])
+
+        if passes is None:
+            passes = int(self.app.defaults["tools_iso_passes"])
+
+        if overlap is None:
+            overlap = float(self.app.defaults["tools_iso_overlap"])
+
+        overlap /= 100.0
+
+        combine = self.app.defaults["tools_iso_combine_passes"] if combine is None else bool(combine)
+
+        if milling_type is None:
+            milling_type = self.app.defaults["tools_iso_milling_type"]
+
+        if iso_type is None:
+            iso_t = 2
+        else:
+            iso_t = iso_type
+
+        base_name = self.options["name"]
+
+        if combine:
+            if outname is None:
+                if self.iso_type == 0:
+                    iso_name = base_name + "_ext_iso"
+                elif self.iso_type == 1:
+                    iso_name = base_name + "_int_iso"
+                else:
+                    iso_name = base_name + "_iso"
+            else:
+                iso_name = outname
+
+            def iso_init(geo_obj, app_obj):
+                # Propagate options
+                geo_obj.options["cnctooldia"] = str(dia)
+                geo_obj.tool_type = self.app.defaults["tools_iso_tool_type"]
+
+                geo_obj.solid_geometry = []
+
+                # transfer the Cut Z and Vtip and VAngle values in case that we use the V-Shape tool in Gerber UI
+                if geo_obj.tool_type.lower() == 'v':
+                    new_cutz = self.app.defaults["tools_iso_tool_cutz"]
+                    new_vtipdia = self.app.defaults["tools_iso_tool_vtipdia"]
+                    new_vtipangle = self.app.defaults["tools_iso_tool_vtipangle"]
+                    tool_type = 'V'
+                else:
+                    new_cutz = self.app.defaults['geometry_cutz']
+                    new_vtipdia = self.app.defaults['geometry_vtipdia']
+                    new_vtipangle = self.app.defaults['geometry_vtipangle']
+                    tool_type = 'C1'
+
+                # store here the default data for Geometry Data
+                default_data = {}
+                default_data.update({
+                    "name": iso_name,
+                    "plot": self.app.defaults['geometry_plot'],
+                    "cutz": new_cutz,
+                    "vtipdia": new_vtipdia,
+                    "vtipangle": new_vtipangle,
+                    "travelz": self.app.defaults['geometry_travelz'],
+                    "feedrate": self.app.defaults['geometry_feedrate'],
+                    "feedrate_z": self.app.defaults['geometry_feedrate_z'],
+                    "feedrate_rapid": self.app.defaults['geometry_feedrate_rapid'],
+                    "dwell": self.app.defaults['geometry_dwell'],
+                    "dwelltime": self.app.defaults['geometry_dwelltime'],
+                    "multidepth": self.app.defaults['geometry_multidepth'],
+                    "ppname_g": self.app.defaults['geometry_ppname_g'],
+                    "depthperpass": self.app.defaults['geometry_depthperpass'],
+                    "extracut": self.app.defaults['geometry_extracut'],
+                    "extracut_length": self.app.defaults['geometry_extracut_length'],
+                    "toolchange": self.app.defaults['geometry_toolchange'],
+                    "toolchangez": self.app.defaults['geometry_toolchangez'],
+                    "endz": self.app.defaults['geometry_endz'],
+                    "spindlespeed": self.app.defaults['geometry_spindlespeed'],
+                    "toolchangexy": self.app.defaults['geometry_toolchangexy'],
+                    "startz": self.app.defaults['geometry_startz']
+                })
+
+                geo_obj.tools = {}
+                geo_obj.tools['1'] = {}
+                geo_obj.tools.update({
+                    '1': {
+                        'tooldia': dia,
+                        'offset': 'Path',
+                        'offset_value': 0.0,
+                        'type': _('Rough'),
+                        'tool_type': tool_type,
+                        'data': default_data,
+                        'solid_geometry': geo_obj.solid_geometry
+                    }
+                })
+
+                for nr_pass in range(passes):
+                    iso_offset = dia * ((2 * nr_pass + 1) / 2.0) - (nr_pass * overlap * dia)
+
+                    # if milling type is climb then the move is counter-clockwise around features
+                    mill_dir = 1 if milling_type == 'cl' else 0
+                    geom = self.generate_envelope(iso_offset, mill_dir, geometry=work_geo, env_iso_type=iso_t,
+                                                  follow=follow, nr_passes=nr_pass)
+
+                    if geom == 'fail':
+                        app_obj.inform.emit('[ERROR_NOTCL] %s' % _("Isolation geometry could not be generated."))
+                        return 'fail'
+                    geo_obj.solid_geometry.append(geom)
+
+                    # update the geometry in the tools
+                    geo_obj.tools['1']['solid_geometry'] = geo_obj.solid_geometry
+
+                # detect if solid_geometry is empty and this require list flattening which is "heavy"
+                # or just looking in the lists (they are one level depth) and if any is not empty
+                # proceed with object creation, if there are empty and the number of them is the length
+                # of the list then we have an empty solid_geometry which should raise a Custom Exception
+                empty_cnt = 0
+                if not isinstance(geo_obj.solid_geometry, list) and \
+                        not isinstance(geo_obj.solid_geometry, MultiPolygon):
+                    geo_obj.solid_geometry = [geo_obj.solid_geometry]
+
+                for g in geo_obj.solid_geometry:
+                    if g:
+                        break
+                    else:
+                        empty_cnt += 1
+
+                if empty_cnt == len(geo_obj.solid_geometry):
+                    raise ValidationError("Empty Geometry", None)
+                else:
+                    app_obj.inform.emit('[success] %s" %s' % (_("Isolation geometry created"), geo_obj.options["name"]))
+
+                # even if combine is checked, one pass is still single-geo
+                geo_obj.multigeo = True if passes > 1 else False
+
+                # ############################################################
+                # ########## AREA SUBTRACTION ################################
+                # ############################################################
+                # if self.app.defaults["tools_iso_except"]:
+                #     self.app.proc_container.update_view_text(' %s' % _("Subtracting Geo"))
+                #     geo_obj.solid_geometry = self.area_subtraction(geo_obj.solid_geometry)
+
+            self.app.app_obj.new_object("geometry", iso_name, iso_init, plot=plot)
+        else:
+            for i in range(passes):
+
+                offset = dia * ((2 * i + 1) / 2.0) - (i * overlap * dia)
+                if passes > 1:
+                    if outname is None:
+                        if self.iso_type == 0:
+                            iso_name = base_name + "_ext_iso" + str(i + 1)
+                        elif self.iso_type == 1:
+                            iso_name = base_name + "_int_iso" + str(i + 1)
+                        else:
+                            iso_name = base_name + "_iso" + str(i + 1)
+                    else:
+                        iso_name = outname
+                else:
+                    if outname is None:
+                        if self.iso_type == 0:
+                            iso_name = base_name + "_ext_iso"
+                        elif self.iso_type == 1:
+                            iso_name = base_name + "_int_iso"
+                        else:
+                            iso_name = base_name + "_iso"
+                    else:
+                        iso_name = outname
+
+                def iso_init(geo_obj, app_obj):
+                    # Propagate options
+                    geo_obj.options["cnctooldia"] = str(dia)
+                    geo_obj.tool_type = self.app.defaults["tools_iso_tool_type"]
+
+                    # if milling type is climb then the move is counter-clockwise around features
+                    mill_dir = 1 if milling_type == 'cl' else 0
+                    geom = self.generate_envelope(offset, mill_dir, geometry=work_geo, env_iso_type=iso_t,
+                                                  follow=follow, nr_passes=i)
+
+                    if geom == 'fail':
+                        app_obj.inform.emit('[ERROR_NOTCL] %s' % _("Isolation geometry could not be generated."))
+                        return 'fail'
+
+                    geo_obj.solid_geometry = geom
+
+                    # transfer the Cut Z and Vtip and VAngle values in case that we use the V-Shape tool in Gerber UI
+                    # even if the resulting geometry is not multigeo we add the tools dict which will hold the data
+                    # required to be transfered to the Geometry object
+                    if self.app.defaults["tools_iso_tool_type"].lower() == 'v':
+                        new_cutz = self.app.defaults["tools_iso_tool_cutz"]
+                        new_vtipdia = self.app.defaults["tools_iso_tool_vtipdia"]
+                        new_vtipangle = self.app.defaults["tools_iso_tool_vtipangle"]
+                        tool_type = 'V'
+                    else:
+                        new_cutz = self.app.defaults['geometry_cutz']
+                        new_vtipdia = self.app.defaults['geometry_vtipdia']
+                        new_vtipangle = self.app.defaults['geometry_vtipangle']
+                        tool_type = 'C1'
+
+                    # store here the default data for Geometry Data
+                    default_data = {}
+                    default_data.update({
+                        "name": iso_name,
+                        "plot": self.app.defaults['geometry_plot'],
+                        "cutz": new_cutz,
+                        "vtipdia": new_vtipdia,
+                        "vtipangle": new_vtipangle,
+                        "travelz": self.app.defaults['geometry_travelz'],
+                        "feedrate": self.app.defaults['geometry_feedrate'],
+                        "feedrate_z": self.app.defaults['geometry_feedrate_z'],
+                        "feedrate_rapid": self.app.defaults['geometry_feedrate_rapid'],
+                        "dwell": self.app.defaults['geometry_dwell'],
+                        "dwelltime": self.app.defaults['geometry_dwelltime'],
+                        "multidepth": self.app.defaults['geometry_multidepth'],
+                        "ppname_g": self.app.defaults['geometry_ppname_g'],
+                        "depthperpass": self.app.defaults['geometry_depthperpass'],
+                        "extracut": self.app.defaults['geometry_extracut'],
+                        "extracut_length": self.app.defaults['geometry_extracut_length'],
+                        "toolchange": self.app.defaults['geometry_toolchange'],
+                        "toolchangez": self.app.defaults['geometry_toolchangez'],
+                        "endz": self.app.defaults['geometry_endz'],
+                        "spindlespeed": self.app.defaults['geometry_spindlespeed'],
+                        "toolchangexy": self.app.defaults['geometry_toolchangexy'],
+                        "startz": self.app.defaults['geometry_startz']
+                    })
+
+                    geo_obj.tools = {}
+                    geo_obj.tools['1'] = {}
+                    geo_obj.tools.update({
+                        '1': {
+                            'tooldia': dia,
+                            'offset': 'Path',
+                            'offset_value': 0.0,
+                            'type': _('Rough'),
+                            'tool_type': tool_type,
+                            'data': default_data,
+                            'solid_geometry': geo_obj.solid_geometry
+                        }
+                    })
+
+                    # detect if solid_geometry is empty and this require list flattening which is "heavy"
+                    # or just looking in the lists (they are one level depth) and if any is not empty
+                    # proceed with object creation, if there are empty and the number of them is the length
+                    # of the list then we have an empty solid_geometry which should raise a Custom Exception
+                    empty_cnt = 0
+                    if not isinstance(geo_obj.solid_geometry, list):
+                        geo_obj.solid_geometry = [geo_obj.solid_geometry]
+
+                    for g in geo_obj.solid_geometry:
+                        if g:
+                            break
+                        else:
+                            empty_cnt += 1
+
+                    if empty_cnt == len(geo_obj.solid_geometry):
+                        raise ValidationError("Empty Geometry", None)
+                    else:
+                        app_obj.inform.emit('[success] %s: %s' %
+                                            (_("Isolation geometry created"), geo_obj.options["name"]))
+                    geo_obj.multigeo = False
+
+                    # ############################################################
+                    # ########## AREA SUBTRACTION ################################
+                    # ############################################################
+                    # if self.app.defaults["tools_iso_except"]:
+                    #     self.app.proc_container.update_view_text(' %s' % _("Subtracting Geo"))
+                    #     geo_obj.solid_geometry = self.area_subtraction(geo_obj.solid_geometry)
+
+                self.app.app_obj.new_object("geometry", iso_name, iso_init, plot=plot)
+
     def generate_envelope(self, offset, invert, geometry=None, env_iso_type=2, follow=None, nr_passes=0):
         # isolation_geometry produces an envelope that is going on the left of the geometry
         # (the copper features). To leave the least amount of burrs on the features

+ 21 - 4
AppTools/ToolShell.py

@@ -8,9 +8,9 @@
 
 
 from PyQt5.QtCore import Qt
-from PyQt5.QtGui import QTextCursor
-from PyQt5.QtWidgets import QVBoxLayout, QWidget
-from AppGUI.GUIElements import _BrowserTextEdit, _ExpandableTextEdit
+from PyQt5.QtGui import QTextCursor, QPixmap
+from PyQt5.QtWidgets import QVBoxLayout, QWidget, QHBoxLayout, QLabel
+from AppGUI.GUIElements import _BrowserTextEdit, _ExpandableTextEdit, FCLabel
 import html
 import sys
 import traceback
@@ -38,6 +38,8 @@ class TermWidget(QWidget):
     def __init__(self, version, app, *args):
         QWidget.__init__(self, *args)
 
+        self.app = app
+
         self._browser = _BrowserTextEdit(version=version, app=app)
         self._browser.setStyleSheet("font: 9pt \"Courier\";")
         self._browser.setReadOnly(True)
@@ -51,15 +53,30 @@ class TermWidget(QWidget):
         self._edit.setFocus()
         self.setFocusProxy(self._edit)
 
+        self._delete_line = FCLabel()
+        self._delete_line.setPixmap(QPixmap(self.app.resource_location + '/clear_line16.png'))
+        self._delete_line.setMargin(3)
+        self._delete_line.setToolTip(_("Clear the text."))
+
         layout = QVBoxLayout(self)
         layout.setSpacing(0)
         layout.setContentsMargins(0, 0, 0, 0)
         layout.addWidget(self._browser)
-        layout.addWidget(self._edit)
+
+        hlay = QHBoxLayout()
+        hlay.addWidget(self._delete_line)
+        hlay.addWidget(QLabel(" "))
+        hlay.addWidget(self._edit)
+        layout.addLayout(hlay)
 
         self._history = ['']  # current empty line
         self._historyIndex = 0
 
+        self._delete_line.clicked.connect(self.on_delete_line_clicked)
+
+    def on_delete_line_clicked(self):
+        self._edit.clear()
+
     def open_processing(self, detail=None):
         """
         Open processing and disable using shell commands again until all commands are finished

+ 3 - 0
App_Main.py

@@ -2728,6 +2728,9 @@ class App(QtCore.QObject):
                         'title="Flaticon">www.flaticon.com</a></div>'
                         '<div>Icons by <a target="_blank" href="https://icons8.com">Icons8</a></div>'
                         'Icons by <a href="http://www.onlinewebfonts.com">oNline Web Fonts</a>'
+                        '<div>Icons by <a href="https://www.flaticon.com/authors/pixel-perfect" '
+                        'title="Pixel perfect">Pixel perfect</a> from <a href="https://www.flaticon.com/" '
+                        'title="Flaticon">www.flaticon.com</a></div>'
                     )
                 )
                 attributions_label.setOpenExternalLinks(True)

+ 5 - 0
CHANGELOG.md

@@ -7,6 +7,11 @@ CHANGELOG for FlatCAM beta
 
 =================================================
 
+2.06.2020
+
+- Tcl Shell - added a button to delete the content of the active line
+- Tcl Command Isolate - fixed to work in the new configuration
+
 1.06.2020
 
 - made the Distance Tool display the angle in values between 0 and 359.9999 degrees

BIN
assets/resources/clear_line16.png


BIN
assets/resources/dark_resources/clear_line16.png