- Tcl Shell - added a button to delete the content of the active line
- Tcl Command Isolate - fixed to work in the new configuration
This commit is contained in:
parent
6b247ff5e1
commit
2624df10bf
|
@ -121,6 +121,7 @@ class GerberObject(FlatCAMObj, Gerber):
|
||||||
"bboxrounded": False,
|
"bboxrounded": False,
|
||||||
"aperture_display": False,
|
"aperture_display": False,
|
||||||
"follow": False,
|
"follow": False,
|
||||||
|
"milling_type": 'cl',
|
||||||
})
|
})
|
||||||
|
|
||||||
# type of isolation: 0 = exteriors, 1 = interiors, 2 = complete isolation (both interiors and exteriors)
|
# 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)
|
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):
|
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
|
# 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
|
# (the copper features). To leave the least amount of burrs on the features
|
||||||
|
|
|
@ -8,9 +8,9 @@
|
||||||
|
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtGui import QTextCursor
|
from PyQt5.QtGui import QTextCursor, QPixmap
|
||||||
from PyQt5.QtWidgets import QVBoxLayout, QWidget
|
from PyQt5.QtWidgets import QVBoxLayout, QWidget, QHBoxLayout, QLabel
|
||||||
from AppGUI.GUIElements import _BrowserTextEdit, _ExpandableTextEdit
|
from AppGUI.GUIElements import _BrowserTextEdit, _ExpandableTextEdit, FCLabel
|
||||||
import html
|
import html
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
@ -38,6 +38,8 @@ class TermWidget(QWidget):
|
||||||
def __init__(self, version, app, *args):
|
def __init__(self, version, app, *args):
|
||||||
QWidget.__init__(self, *args)
|
QWidget.__init__(self, *args)
|
||||||
|
|
||||||
|
self.app = app
|
||||||
|
|
||||||
self._browser = _BrowserTextEdit(version=version, app=app)
|
self._browser = _BrowserTextEdit(version=version, app=app)
|
||||||
self._browser.setStyleSheet("font: 9pt \"Courier\";")
|
self._browser.setStyleSheet("font: 9pt \"Courier\";")
|
||||||
self._browser.setReadOnly(True)
|
self._browser.setReadOnly(True)
|
||||||
|
@ -51,15 +53,30 @@ class TermWidget(QWidget):
|
||||||
self._edit.setFocus()
|
self._edit.setFocus()
|
||||||
self.setFocusProxy(self._edit)
|
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 = QVBoxLayout(self)
|
||||||
layout.setSpacing(0)
|
layout.setSpacing(0)
|
||||||
layout.setContentsMargins(0, 0, 0, 0)
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
layout.addWidget(self._browser)
|
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._history = [''] # current empty line
|
||||||
self._historyIndex = 0
|
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):
|
def open_processing(self, detail=None):
|
||||||
"""
|
"""
|
||||||
Open processing and disable using shell commands again until all commands are finished
|
Open processing and disable using shell commands again until all commands are finished
|
||||||
|
|
|
@ -2728,6 +2728,9 @@ class App(QtCore.QObject):
|
||||||
'title="Flaticon">www.flaticon.com</a></div>'
|
'title="Flaticon">www.flaticon.com</a></div>'
|
||||||
'<div>Icons by <a target="_blank" href="https://icons8.com">Icons8</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>'
|
'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)
|
attributions_label.setOpenExternalLinks(True)
|
||||||
|
|
|
@ -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
|
1.06.2020
|
||||||
|
|
||||||
- made the Distance Tool display the angle in values between 0 and 359.9999 degrees
|
- made the Distance Tool display the angle in values between 0 and 359.9999 degrees
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 556 B |
Binary file not shown.
After Width: | Height: | Size: 556 B |
Loading…
Reference in New Issue