commit
fdad91f04e
2135
FlatCAMApp.py
2135
FlatCAMApp.py
File diff suppressed because it is too large
Load Diff
1226
FlatCAMCommon.py
1226
FlatCAMCommon.py
File diff suppressed because it is too large
Load Diff
389
FlatCAMObj.py
389
FlatCAMObj.py
|
@ -3306,104 +3306,6 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
optionChanged = QtCore.pyqtSignal(str)
|
||||
ui_type = GeometryObjectUI
|
||||
|
||||
def merge(self, geo_list, geo_final, multigeo=None):
|
||||
"""
|
||||
Merges the geometry of objects in grb_list into
|
||||
the geometry of geo_final.
|
||||
|
||||
:param geo_list: List of FlatCAMGerber Objects to join.
|
||||
:param geo_final: Destination FlatCAMGerber object.
|
||||
:return: None
|
||||
"""
|
||||
|
||||
if geo_final.solid_geometry is None:
|
||||
geo_final.solid_geometry = []
|
||||
|
||||
if type(geo_final.solid_geometry) is not list:
|
||||
geo_final.solid_geometry = [geo_final.solid_geometry]
|
||||
|
||||
for geo in geo_list:
|
||||
for option in geo.options:
|
||||
if option is not 'name':
|
||||
try:
|
||||
geo_final.options[option] = geo.options[option]
|
||||
except Exception as e:
|
||||
log.warning("Failed to copy option %s. Error: %s" % (str(option), str(e)))
|
||||
|
||||
# Expand lists
|
||||
if type(geo) is list:
|
||||
FlatCAMGeometry.merge(self, geo_list=geo, geo_final=geo_final)
|
||||
# If not list, just append
|
||||
else:
|
||||
# merge solid_geometry, useful for singletool geometry, for multitool each is empty
|
||||
if multigeo is None or multigeo is False:
|
||||
geo_final.multigeo = False
|
||||
try:
|
||||
geo_final.solid_geometry.append(geo.solid_geometry)
|
||||
except Exception as e:
|
||||
log.debug("FlatCAMGeometry.merge() --> %s" % str(e))
|
||||
else:
|
||||
geo_final.multigeo = True
|
||||
# if multigeo the solid_geometry is empty in the object attributes because it now lives in the
|
||||
# tools object attribute, as a key value
|
||||
geo_final.solid_geometry = []
|
||||
|
||||
# find the tool_uid maximum value in the geo_final
|
||||
geo_final_uid_list = []
|
||||
for key in geo_final.tools:
|
||||
geo_final_uid_list.append(int(key))
|
||||
|
||||
try:
|
||||
max_uid = max(geo_final_uid_list, key=int)
|
||||
except ValueError:
|
||||
max_uid = 0
|
||||
|
||||
# add and merge tools. If what we try to merge as Geometry is Excellon's and/or Gerber's then don't try
|
||||
# to merge the obj.tools as it is likely there is none to merge.
|
||||
if not isinstance(geo, FlatCAMGerber) and not isinstance(geo, FlatCAMExcellon):
|
||||
for tool_uid in geo.tools:
|
||||
max_uid += 1
|
||||
geo_final.tools[max_uid] = deepcopy(geo.tools[tool_uid])
|
||||
|
||||
@staticmethod
|
||||
def get_pts(o):
|
||||
"""
|
||||
Returns a list of all points in the object, where
|
||||
the object can be a MultiPolygon, Polygon, Not a polygon, or a list
|
||||
of such. Search is done recursively.
|
||||
|
||||
:param: geometric object
|
||||
:return: List of points
|
||||
:rtype: list
|
||||
"""
|
||||
pts = []
|
||||
|
||||
# Iterable: descend into each item.
|
||||
try:
|
||||
for subo in o:
|
||||
pts += FlatCAMGeometry.get_pts(subo)
|
||||
|
||||
# Non-iterable
|
||||
except TypeError:
|
||||
if o is not None:
|
||||
if type(o) == MultiPolygon:
|
||||
for poly in o:
|
||||
pts += FlatCAMGeometry.get_pts(poly)
|
||||
# ## Descend into .exerior and .interiors
|
||||
elif type(o) == Polygon:
|
||||
pts += FlatCAMGeometry.get_pts(o.exterior)
|
||||
for i in o.interiors:
|
||||
pts += FlatCAMGeometry.get_pts(i)
|
||||
elif type(o) == MultiLineString:
|
||||
for line in o:
|
||||
pts += FlatCAMGeometry.get_pts(line)
|
||||
# ## Has .coords: list them.
|
||||
else:
|
||||
pts += list(o.coords)
|
||||
else:
|
||||
return
|
||||
return pts
|
||||
|
||||
def __init__(self, name):
|
||||
FlatCAMObj.__init__(self, name)
|
||||
Geometry.__init__(self, geo_steps_per_circle=int(self.app.defaults["geometry_circle_steps"]))
|
||||
|
@ -3477,7 +3379,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
|
||||
# flag to store if the geometry is part of a special group of geometries that can't be processed by the default
|
||||
# engine of FlatCAM. Most likely are generated by some of tools and are special cases of geometries.
|
||||
self. special_group = None
|
||||
self.special_group = None
|
||||
|
||||
self.old_pp_state = self.app.defaults["geometry_multidepth"]
|
||||
self.old_toolchangeg_state = self.app.defaults["geometry_toolchange"]
|
||||
|
@ -3506,9 +3408,9 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
tool_idx += 1
|
||||
row_no = tool_idx - 1
|
||||
|
||||
id = QtWidgets.QTableWidgetItem('%d' % int(tool_idx))
|
||||
id.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
self.ui.geo_tools_table.setItem(row_no, 0, id) # Tool name/id
|
||||
tool_id = QtWidgets.QTableWidgetItem('%d' % int(tool_idx))
|
||||
tool_id.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
self.ui.geo_tools_table.setItem(row_no, 0, tool_id) # Tool name/id
|
||||
|
||||
# Make sure that the tool diameter when in MM is with no more than 2 decimals.
|
||||
# There are no tool bits in MM with more than 3 decimals diameter.
|
||||
|
@ -3754,7 +3656,6 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
# again float type; dict's don't like having keys changed when iterated through therefore the need for the
|
||||
# following convoluted way of changing the keys from string to float type
|
||||
temp_tools = {}
|
||||
new_key = 0.0
|
||||
for tooluid_key in self.tools:
|
||||
val = deepcopy(self.tools[tooluid_key])
|
||||
new_key = deepcopy(int(tooluid_key))
|
||||
|
@ -3775,6 +3676,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
return
|
||||
|
||||
self.ui.geo_tools_table.setupContextMenu()
|
||||
self.ui.geo_tools_table.addContextMenu(
|
||||
_("Add from Tool DB"), self.on_tool_add_from_db_clicked, icon=QtGui.QIcon("share/plus16.png"))
|
||||
self.ui.geo_tools_table.addContextMenu(
|
||||
_("Copy"), self.on_tool_copy, icon=QtGui.QIcon("share/copy16.png"))
|
||||
self.ui.geo_tools_table.addContextMenu(
|
||||
|
@ -3813,6 +3716,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
self.ui.tipdia_entry.valueChanged.connect(self.update_cutz)
|
||||
self.ui.tipangle_entry.valueChanged.connect(self.update_cutz)
|
||||
|
||||
self.ui.addtool_from_db_btn.clicked.connect(self.on_tool_add_from_db_clicked)
|
||||
|
||||
def set_tool_offset_visibility(self, current_row):
|
||||
if current_row is None:
|
||||
return
|
||||
|
@ -4064,8 +3969,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
self.ser_attrs.append('tools')
|
||||
|
||||
if change_message is False:
|
||||
self.app.inform.emit('[success] %s' %
|
||||
_("Tool added in Tool Table."))
|
||||
self.app.inform.emit('[success] %s' % _("Tool added in Tool Table."))
|
||||
else:
|
||||
change_message = False
|
||||
self.app.inform.emit('[WARNING_NOTCL] %s' %
|
||||
|
@ -4076,6 +3980,73 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
if self.ui.geo_tools_table.rowCount() != 0:
|
||||
self.ui.geo_param_frame.setDisabled(False)
|
||||
|
||||
def on_tool_add_from_db_clicked(self):
|
||||
"""
|
||||
Called when the user wants to add a new tool from Tools Database. It will create the Tools Database object
|
||||
and display the Tools Database tab in the form needed for the Tool adding
|
||||
:return: None
|
||||
"""
|
||||
self.app.on_tools_database()
|
||||
self.app.tools_db_tab.buttons_frame.hide()
|
||||
self.app.tools_db_tab.add_tool_from_db.show()
|
||||
|
||||
def on_tool_from_db_inserted(self, tool):
|
||||
"""
|
||||
Called from the Tools DB object through a App method when adding a tool from Tools Database
|
||||
:param tool: a dict with the tool data
|
||||
:return: None
|
||||
"""
|
||||
|
||||
self.ui_disconnect()
|
||||
self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper()
|
||||
|
||||
tooldia = float(tool['tooldia'])
|
||||
|
||||
# construct a list of all 'tooluid' in the self.tools
|
||||
tool_uid_list = []
|
||||
for tooluid_key in self.tools:
|
||||
tool_uid_item = int(tooluid_key)
|
||||
tool_uid_list.append(tool_uid_item)
|
||||
|
||||
# find maximum from the temp_uid, add 1 and this is the new 'tooluid'
|
||||
if not tool_uid_list:
|
||||
max_uid = 0
|
||||
else:
|
||||
max_uid = max(tool_uid_list)
|
||||
self.tooluid = max_uid + 1
|
||||
|
||||
tooldia = float('%.*f' % (self.decimals, tooldia))
|
||||
|
||||
self.tools.update({
|
||||
self.tooluid: {
|
||||
'tooldia': tooldia,
|
||||
'offset': tool['offset'],
|
||||
'offset_value': float(tool['offset_value']),
|
||||
'type': tool['type'],
|
||||
'tool_type': tool['tool_type'],
|
||||
'data': deepcopy(tool['data']),
|
||||
'solid_geometry': self.solid_geometry
|
||||
}
|
||||
})
|
||||
|
||||
self.tools[self.tooluid]['data']['name'] = self.options['name']
|
||||
|
||||
self.ui.tool_offset_entry.hide()
|
||||
self.ui.tool_offset_lbl.hide()
|
||||
|
||||
# we do this HACK to make sure the tools attribute to be serialized is updated in the self.ser_attrs list
|
||||
try:
|
||||
self.ser_attrs.remove('tools')
|
||||
except TypeError:
|
||||
pass
|
||||
self.ser_attrs.append('tools')
|
||||
|
||||
self.build_ui()
|
||||
|
||||
# if there is no tool left in the Tools Table, enable the parameters GUI
|
||||
if self.ui.geo_tools_table.rowCount() != 0:
|
||||
self.ui.geo_param_frame.setDisabled(False)
|
||||
|
||||
def on_tool_copy(self, all=None):
|
||||
self.ui_disconnect()
|
||||
|
||||
|
@ -4670,16 +4641,16 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
# test to see if we have tools available in the tool table
|
||||
if self.ui.geo_tools_table.selectedItems():
|
||||
for x in self.ui.geo_tools_table.selectedItems():
|
||||
try:
|
||||
tooldia = float(self.ui.geo_tools_table.item(x.row(), 1).text())
|
||||
except ValueError:
|
||||
# try to convert comma to decimal point. if it's still not working error message and return
|
||||
try:
|
||||
tooldia = float(self.ui.geo_tools_table.item(x.row(), 1).text().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_("Wrong value format entered, use a number."))
|
||||
return
|
||||
# try:
|
||||
# tooldia = float(self.ui.geo_tools_table.item(x.row(), 1).text())
|
||||
# except ValueError:
|
||||
# # try to convert comma to decimal point. if it's still not working error message and return
|
||||
# try:
|
||||
# tooldia = float(self.ui.geo_tools_table.item(x.row(), 1).text().replace(',', '.'))
|
||||
# except ValueError:
|
||||
# self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
# _("Wrong value format entered, use a number."))
|
||||
# return
|
||||
tooluid = int(self.ui.geo_tools_table.item(x.row(), 5).text())
|
||||
|
||||
for tooluid_key, tooluid_value in self.tools.items():
|
||||
|
@ -4956,16 +4927,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
elif dia_cnc_dict['offset'].lower() == 'out':
|
||||
tool_offset = tooldia_val / 2
|
||||
elif dia_cnc_dict['offset'].lower() == 'custom':
|
||||
try:
|
||||
offset_value = float(self.ui.tool_offset_entry.get_value())
|
||||
except ValueError:
|
||||
# try to convert comma to decimal point. if it's still not working error message and return
|
||||
try:
|
||||
offset_value = float(self.ui.tool_offset_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_("Wrong value format entered, use a number."))
|
||||
return
|
||||
offset_value = float(self.ui.tool_offset_entry.get_value())
|
||||
if offset_value:
|
||||
tool_offset = float(offset_value)
|
||||
else:
|
||||
|
@ -5169,27 +5131,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
job_obj.segx = segx
|
||||
job_obj.segy = segy
|
||||
|
||||
try:
|
||||
job_obj.z_pdepth = float(self.options["z_pdepth"])
|
||||
except ValueError:
|
||||
# try to convert comma to decimal point. if it's still not working error message and return
|
||||
try:
|
||||
job_obj.z_pdepth = float(self.options["z_pdepth"].replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_('Wrong value format for self.defaults["z_pdepth"] or '
|
||||
'self.options["z_pdepth"]'))
|
||||
|
||||
try:
|
||||
job_obj.feedrate_probe = float(self.options["feedrate_probe"])
|
||||
except ValueError:
|
||||
# try to convert comma to decimal point. if it's still not working error message and return
|
||||
try:
|
||||
job_obj.feedrate_probe = float(self.options["feedrate_probe"].replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_('Wrong value format for self.defaults["feedrate_probe"] '
|
||||
'or self.options["feedrate_probe"]'))
|
||||
job_obj.z_pdepth = float(self.options["z_pdepth"])
|
||||
job_obj.feedrate_probe = float(self.options["feedrate_probe"])
|
||||
|
||||
job_obj.options['xmin'] = self.options['xmin']
|
||||
job_obj.options['ymin'] = self.options['ymin']
|
||||
|
@ -5630,6 +5573,105 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
self.ui.plot_cb.setChecked(True)
|
||||
self.ui_connect()
|
||||
|
||||
def merge(self, geo_list, geo_final, multigeo=None):
|
||||
"""
|
||||
Merges the geometry of objects in grb_list into
|
||||
the geometry of geo_final.
|
||||
|
||||
:param geo_list: List of FlatCAMGerber Objects to join.
|
||||
:param geo_final: Destination FlatCAMGerber object.
|
||||
:param multigeo: if the merged geometry objects are of type MultiGeo
|
||||
:return: None
|
||||
"""
|
||||
|
||||
if geo_final.solid_geometry is None:
|
||||
geo_final.solid_geometry = []
|
||||
|
||||
if type(geo_final.solid_geometry) is not list:
|
||||
geo_final.solid_geometry = [geo_final.solid_geometry]
|
||||
|
||||
for geo in geo_list:
|
||||
for option in geo.options:
|
||||
if option is not 'name':
|
||||
try:
|
||||
geo_final.options[option] = deepcopy(geo.options[option])
|
||||
except Exception as e:
|
||||
log.warning("Failed to copy option %s. Error: %s" % (str(option), str(e)))
|
||||
|
||||
# Expand lists
|
||||
if type(geo) is list:
|
||||
FlatCAMGeometry.merge(self, geo_list=geo, geo_final=geo_final)
|
||||
# If not list, just append
|
||||
else:
|
||||
# merge solid_geometry, useful for singletool geometry, for multitool each is empty
|
||||
if multigeo is None or multigeo is False:
|
||||
geo_final.multigeo = False
|
||||
try:
|
||||
geo_final.solid_geometry.append(deepcopy(geo.solid_geometry))
|
||||
except Exception as e:
|
||||
log.debug("FlatCAMGeometry.merge() --> %s" % str(e))
|
||||
else:
|
||||
geo_final.multigeo = True
|
||||
# if multigeo the solid_geometry is empty in the object attributes because it now lives in the
|
||||
# tools object attribute, as a key value
|
||||
geo_final.solid_geometry = []
|
||||
|
||||
# find the tool_uid maximum value in the geo_final
|
||||
geo_final_uid_list = []
|
||||
for key in geo_final.tools:
|
||||
geo_final_uid_list.append(int(key))
|
||||
|
||||
try:
|
||||
max_uid = max(geo_final_uid_list, key=int)
|
||||
except ValueError:
|
||||
max_uid = 0
|
||||
|
||||
# add and merge tools. If what we try to merge as Geometry is Excellon's and/or Gerber's then don't try
|
||||
# to merge the obj.tools as it is likely there is none to merge.
|
||||
if not isinstance(geo, FlatCAMGerber) and not isinstance(geo, FlatCAMExcellon):
|
||||
for tool_uid in geo.tools:
|
||||
max_uid += 1
|
||||
geo_final.tools[max_uid] = deepcopy(geo.tools[tool_uid])
|
||||
|
||||
@staticmethod
|
||||
def get_pts(o):
|
||||
"""
|
||||
Returns a list of all points in the object, where
|
||||
the object can be a MultiPolygon, Polygon, Not a polygon, or a list
|
||||
of such. Search is done recursively.
|
||||
|
||||
:param: geometric object
|
||||
:return: List of points
|
||||
:rtype: list
|
||||
"""
|
||||
pts = []
|
||||
|
||||
# Iterable: descend into each item.
|
||||
try:
|
||||
for subo in o:
|
||||
pts += FlatCAMGeometry.get_pts(subo)
|
||||
|
||||
# Non-iterable
|
||||
except TypeError:
|
||||
if o is not None:
|
||||
if type(o) == MultiPolygon:
|
||||
for poly in o:
|
||||
pts += FlatCAMGeometry.get_pts(poly)
|
||||
# ## Descend into .exerior and .interiors
|
||||
elif type(o) == Polygon:
|
||||
pts += FlatCAMGeometry.get_pts(o.exterior)
|
||||
for i in o.interiors:
|
||||
pts += FlatCAMGeometry.get_pts(i)
|
||||
elif type(o) == MultiLineString:
|
||||
for line in o:
|
||||
pts += FlatCAMGeometry.get_pts(line)
|
||||
# ## Has .coords: list them.
|
||||
else:
|
||||
pts += list(o.coords)
|
||||
else:
|
||||
return
|
||||
return pts
|
||||
|
||||
|
||||
class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
||||
"""
|
||||
|
@ -6002,8 +6044,8 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
|||
_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 (*.*)"
|
||||
_filter_ = "G-Code Files (*.nc);;G-Code Files (*.txt);;G-Code Files (*.tap);;G-Code Files (*.ngc);;" \
|
||||
"G-Code Files (*.cnc);;G-Code Files (*.g-code);;All Files (*.*)"
|
||||
|
||||
try:
|
||||
dir_file_to_save = self.app.get_last_save_folder() + '/' + str(name)
|
||||
|
@ -6094,13 +6136,24 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
|||
self.app.inform.emit('[success] %s...' %
|
||||
_('Loaded Machine Code into Code Editor'))
|
||||
|
||||
def gcode_header(self):
|
||||
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
|
||||
|
||||
:param comment_start_symbol: a symbol to be used as the first symbol in a comment
|
||||
:param comment_stop_symbol: a symbol to be used as the last symbol in a comment
|
||||
:return: a string with a GCode header
|
||||
"""
|
||||
|
||||
log.debug("FlatCAMCNCJob.gcode_header()")
|
||||
time_str = "{:%A, %d %B %Y at %H:%M}".format(datetime.now())
|
||||
marlin = False
|
||||
hpgl = False
|
||||
probe_pp = False
|
||||
|
||||
start_comment = comment_start_symbol if comment_start_symbol is not None else '('
|
||||
stop_comment = comment_stop_symbol if comment_stop_symbol is not None else ')'
|
||||
|
||||
try:
|
||||
for key in self.cnc_tools:
|
||||
ppg = self.cnc_tools[key]['data']['ppname_g']
|
||||
|
@ -6174,17 +6227,17 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
|||
gcode += '(Units: ' + self.units.upper() + ')\n' + "\n"
|
||||
gcode += '(Created on ' + time_str + ')\n' + '\n'
|
||||
else:
|
||||
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 = '%sG-CODE GENERATED BY FLATCAM v%s - www.flatcam.org - Version Date: %s%s\n' % \
|
||||
(start_comment, str(self.app.version), str(self.app.version_date), stop_comment) + '\n'
|
||||
|
||||
gcode += '(Name: ' + str(self.options['name']) + ')\n'
|
||||
gcode += '(Type: ' + "G-code from " + str(self.options['type']) + ')\n'
|
||||
gcode += '%sName: ' % start_comment + str(self.options['name']) + '%s\n' % stop_comment
|
||||
gcode += '%sType: ' % start_comment + "G-code from " + str(self.options['type']) + '%s\n' % stop_comment
|
||||
|
||||
# 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 += '%sUnits: ' % start_comment + self.units.upper() + '%s\n' % stop_comment + "\n"
|
||||
gcode += '%sCreated on ' % start_comment + time_str + '%s\n' % stop_comment + '\n'
|
||||
|
||||
return gcode
|
||||
|
||||
|
@ -6200,6 +6253,15 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
|||
return 'M02'
|
||||
|
||||
def export_gcode(self, filename=None, preamble='', postamble='', to_file=False):
|
||||
"""
|
||||
This will save the GCode from the Gcode object to a file on the OS filesystem
|
||||
|
||||
:param filename: filename for the GCode file
|
||||
:param preamble: a custom Gcode block to be added at the beginning of the Gcode file
|
||||
:param postamble: a custom Gcode block to be added at the end of the Gcode file
|
||||
:param to_file: if False then no actual file is saved but the app will know that a file was created
|
||||
:return: None
|
||||
"""
|
||||
gcode = ''
|
||||
roland = False
|
||||
hpgl = False
|
||||
|
@ -6264,7 +6326,9 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
|||
_("G-code does not have a units code: either G20 or G21"))
|
||||
return
|
||||
|
||||
g = gcode[:g_idx] + preamble + '\n' + gcode[g_idx:] + postamble + self.gcode_footer()
|
||||
footer = self.app.defaults['cncjob_footer']
|
||||
end_gcode = self.gcode_footer() if footer is True else ''
|
||||
g = gcode[:g_idx] + preamble + '\n' + gcode[g_idx:] + postamble + end_gcode
|
||||
|
||||
# if toolchange custom is used, replace M6 code with the code from the Toolchange Custom Text box
|
||||
if self.ui.toolchange_cb.get_value() is True:
|
||||
|
@ -6281,15 +6345,20 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
|||
self.app.inform.emit('[success] %s' %
|
||||
_("Toolchange G-code was replaced by a custom code."))
|
||||
|
||||
# lines = StringIO(self.gcode)
|
||||
lines = StringIO(g)
|
||||
|
||||
# Write
|
||||
if filename is not None:
|
||||
try:
|
||||
with open(filename, 'w') as f:
|
||||
for line in lines:
|
||||
f.write(line)
|
||||
force_windows_line_endings = self.app.defaults['cncjob_line_ending']
|
||||
if force_windows_line_endings and sys.platform != 'win32':
|
||||
with open(filename, 'w', newline='\r\n') as f:
|
||||
for line in lines:
|
||||
f.write(line)
|
||||
else:
|
||||
with open(filename, 'w') as f:
|
||||
for line in lines:
|
||||
f.write(line)
|
||||
except FileNotFoundError:
|
||||
self.app.inform.emit('[WARNING_NOTCL] %s' %
|
||||
_("No such file or directory"))
|
||||
|
|
|
@ -18,7 +18,7 @@ postprocessors = {}
|
|||
|
||||
|
||||
class ABCPostProcRegister(ABCMeta):
|
||||
# handles postprocessors registration on instantation
|
||||
# handles postprocessors registration on instantiation
|
||||
def __new__(cls, clsname, bases, attrs):
|
||||
newclass = super(ABCPostProcRegister, cls).__new__(cls, clsname, bases, attrs)
|
||||
if object not in bases:
|
||||
|
|
34
README.md
34
README.md
|
@ -9,9 +9,43 @@ CAD program, and create G-Code for Isolation routing.
|
|||
|
||||
=================================================
|
||||
|
||||
9.11.2019
|
||||
|
||||
- fixed a new bug that did not allow to open the FlatCAM Preferences files by doubleclick in Windows
|
||||
- added a new feature: Tools Database for Geometry objects; resolved issue #308
|
||||
- added tooltips for the Tools Database table headers and buttons
|
||||
|
||||
|
||||
8.11.2019
|
||||
|
||||
- updated the make file for freezed executable
|
||||
|
||||
7.11.2019
|
||||
|
||||
- added the '.ngc' file extension to the GCode Save file dialog filter
|
||||
- made the 'M2' Gcode command footer optional, default is False (can be set using the TclCommand: set_sys cncjob_footer True)
|
||||
- added a setting in Preferences to force the GCode output to have the Windows line-endings even for non-Windows OS's
|
||||
|
||||
6.11.2019
|
||||
|
||||
- the "CRTL+S" key combo when the Preferences Tab is in focus will save the Preferences instead of saving the Project
|
||||
- fixed bug in the Paint Tool that did not allow choosing a Paint Method that was not Standard
|
||||
- made sure that in the FlatCAMGeometry.merge() all the source data is deepcopy-ed in the final object
|
||||
- the font color of the Preferences tab will change to red if settings are not saved and it will revert to default when saved
|
||||
- fixed issue #333. The Geometry Editor Paint tool was not working and using it resulted in an error
|
||||
|
||||
5.11.2019
|
||||
|
||||
- added a new setting named 'Allow Machinist Unsafe Settings' that will allow the Travel Z and Cut Z to take both positive and negative values
|
||||
- fixed some issues when editing a multigeo geometry
|
||||
|
||||
4.11.2019
|
||||
|
||||
- wip
|
||||
- getting rid of all the Options GUI and related functions as it is no longer supported
|
||||
- updated the UI in Geometry UI
|
||||
- optimized the order of the defaults storage declaration and the update of the Preferences GUI from the defaults
|
||||
- started to add a Tool Database
|
||||
|
||||
3.11.2019
|
||||
|
||||
|
|
223
camlib.py
223
camlib.py
|
@ -7,7 +7,7 @@
|
|||
# ########################################################## ##
|
||||
|
||||
|
||||
from PyQt5 import QtWidgets
|
||||
from PyQt5 import QtWidgets, QtCore
|
||||
from io import StringIO
|
||||
|
||||
import numpy as np
|
||||
|
@ -497,10 +497,6 @@ class Geometry(object):
|
|||
from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy
|
||||
self.temp_shapes = ShapeCollectionLegacy(obj=self, app=self.app, name='camlib.geometry')
|
||||
|
||||
# if geo_steps_per_circle is None:
|
||||
# geo_steps_per_circle = int(Geometry.defaults["geo_steps_per_circle"])
|
||||
# self.geo_steps_per_circle = geo_steps_per_circle
|
||||
|
||||
def plot_temp_shapes(self, element, color='red'):
|
||||
|
||||
try:
|
||||
|
@ -2147,6 +2143,12 @@ class CNCjob(Geometry):
|
|||
"excellon_optimization_type": "B",
|
||||
}
|
||||
|
||||
settings = QtCore.QSettings("Open Source", "FlatCAM")
|
||||
if settings.contains("machinist"):
|
||||
machinist_setting = settings.value('machinist', type=int)
|
||||
else:
|
||||
machinist_setting = 0
|
||||
|
||||
def __init__(self,
|
||||
units="in", kind="generic", tooldia=0.0,
|
||||
z_cut=-0.002, z_move=0.1,
|
||||
|
@ -2372,21 +2374,21 @@ class CNCjob(Geometry):
|
|||
self.exc_drills = deepcopy(exobj.drills)
|
||||
self.exc_tools = deepcopy(exobj.tools)
|
||||
|
||||
if drillz > 0:
|
||||
self.app.inform.emit('[WARNING] %s' %
|
||||
_("The Cut Z parameter has positive value. "
|
||||
"It is the depth value to drill into material.\n"
|
||||
"The Cut Z parameter needs to have a negative value, assuming it is a typo "
|
||||
"therefore the app will convert the value to negative. "
|
||||
"Check the resulting CNC code (Gcode etc)."))
|
||||
self.z_cut = -drillz
|
||||
elif drillz == 0:
|
||||
self.app.inform.emit('[WARNING] %s: %s' %
|
||||
(_("The Cut Z parameter is zero. There will be no cut, skipping file"),
|
||||
exobj.options['name']))
|
||||
return 'fail'
|
||||
else:
|
||||
self.z_cut = drillz
|
||||
self.z_cut = drillz
|
||||
if self.machinist_setting == 0:
|
||||
if drillz > 0:
|
||||
self.app.inform.emit('[WARNING] %s' %
|
||||
_("The Cut Z parameter has positive value. "
|
||||
"It is the depth value to drill into material.\n"
|
||||
"The Cut Z parameter needs to have a negative value, assuming it is a typo "
|
||||
"therefore the app will convert the value to negative. "
|
||||
"Check the resulting CNC code (Gcode etc)."))
|
||||
self.z_cut = -drillz
|
||||
elif drillz == 0:
|
||||
self.app.inform.emit('[WARNING] %s: %s' %
|
||||
(_("The Cut Z parameter is zero. There will be no cut, skipping file"),
|
||||
exobj.options['name']))
|
||||
return 'fail'
|
||||
|
||||
self.z_toolchange = toolchangez
|
||||
|
||||
|
@ -2516,8 +2518,7 @@ class CNCjob(Geometry):
|
|||
measured_up_to_zero_distance = 0.0
|
||||
measured_lift_distance = 0.0
|
||||
|
||||
self.app.inform.emit('%s...' %
|
||||
_("Starting G-Code"))
|
||||
self.app.inform.emit('%s...' % _("Starting G-Code"))
|
||||
|
||||
current_platform = platform.architecture()[0]
|
||||
if current_platform == '64bit':
|
||||
|
@ -2671,8 +2672,7 @@ class CNCjob(Geometry):
|
|||
old_disp_number = disp_number
|
||||
|
||||
else:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s...' %
|
||||
_('G91 coordinates not implemented'))
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s...' % _('G91 coordinates not implemented'))
|
||||
return 'fail'
|
||||
else:
|
||||
log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
|
||||
|
@ -2818,8 +2818,7 @@ class CNCjob(Geometry):
|
|||
old_disp_number = disp_number
|
||||
|
||||
else:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s...' %
|
||||
_('G91 coordinates not implemented'))
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s...' % _('G91 coordinates not implemented'))
|
||||
return 'fail'
|
||||
else:
|
||||
log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
|
||||
|
@ -2924,8 +2923,7 @@ class CNCjob(Geometry):
|
|||
self.app.proc_container.update_view_text(' %d%%' % disp_number)
|
||||
old_disp_number = disp_number
|
||||
else:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s...' %
|
||||
_('G91 coordinates not implemented'))
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s...' % _('G91 coordinates not implemented'))
|
||||
return 'fail'
|
||||
else:
|
||||
log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
|
||||
|
@ -3002,10 +3000,10 @@ class CNCjob(Geometry):
|
|||
|
||||
self.tooldia = float(tooldia) if tooldia else None
|
||||
self.z_cut = float(z_cut) if z_cut else None
|
||||
self.z_move = float(z_move) if z_move else None
|
||||
self.z_move = float(z_move) if z_move is not None else None
|
||||
|
||||
self.feedrate = float(feedrate) if feedrate else None
|
||||
self.z_feedrate = float(feedrate_z) if feedrate_z else None
|
||||
self.z_feedrate = float(feedrate_z) if feedrate_z is not None else None
|
||||
self.feedrate_rapid = float(feedrate_rapid) if feedrate_rapid else None
|
||||
|
||||
self.spindlespeed = int(spindlespeed) if spindlespeed else None
|
||||
|
@ -3013,13 +3011,13 @@ class CNCjob(Geometry):
|
|||
self.dwell = dwell
|
||||
self.dwelltime = float(dwelltime) if dwelltime else None
|
||||
|
||||
self.startz = float(startz) if startz else None
|
||||
self.z_end = float(endz) if endz else None
|
||||
self.startz = float(startz) if startz is not None else None
|
||||
self.z_end = float(endz) if endz is not None else None
|
||||
|
||||
self.z_depthpercut = float(depthpercut) if depthpercut else None
|
||||
self.multidepth = multidepth
|
||||
|
||||
self.z_toolchange = float(toolchangez) if toolchangez else None
|
||||
self.z_toolchange = float(toolchangez) if toolchangez is not None else None
|
||||
|
||||
# it servers in the postprocessor file
|
||||
self.tool = tool_no
|
||||
|
@ -3044,46 +3042,47 @@ class CNCjob(Geometry):
|
|||
if self.z_cut is None:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_("Cut_Z parameter is None or zero. Most likely a bad combinations of "
|
||||
"other parameters."))
|
||||
"other parameters."))
|
||||
return 'fail'
|
||||
|
||||
if self.z_cut > 0:
|
||||
self.app.inform.emit('[WARNING] %s' %
|
||||
_("The Cut Z parameter has positive value. "
|
||||
"It is the depth value to cut into material.\n"
|
||||
"The Cut Z parameter needs to have a negative value, assuming it is a typo "
|
||||
"therefore the app will convert the value to negative."
|
||||
"Check the resulting CNC code (Gcode etc)."))
|
||||
self.z_cut = -self.z_cut
|
||||
elif self.z_cut == 0:
|
||||
self.app.inform.emit('[WARNING] %s: %s' %
|
||||
(_("The Cut Z parameter is zero. There will be no cut, skipping file"),
|
||||
self.options['name']))
|
||||
return 'fail'
|
||||
if self.machinist_setting == 0:
|
||||
if self.z_cut > 0:
|
||||
self.app.inform.emit('[WARNING] %s' %
|
||||
_("The Cut Z parameter has positive value. "
|
||||
"It is the depth value to cut into material.\n"
|
||||
"The Cut Z parameter needs to have a negative value, assuming it is a typo "
|
||||
"therefore the app will convert the value to negative."
|
||||
"Check the resulting CNC code (Gcode etc)."))
|
||||
self.z_cut = -self.z_cut
|
||||
elif self.z_cut == 0:
|
||||
self.app.inform.emit('[WARNING] %s: %s' %
|
||||
(_("The Cut Z parameter is zero. There will be no cut, skipping file"),
|
||||
self.options['name']))
|
||||
return 'fail'
|
||||
|
||||
if self.z_move is None:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_("Travel Z parameter is None or zero."))
|
||||
return 'fail'
|
||||
|
||||
if self.z_move < 0:
|
||||
self.app.inform.emit('[WARNING] %s' %
|
||||
_("The Travel Z parameter has negative value. "
|
||||
"It is the height value to travel between cuts.\n"
|
||||
"The Z Travel parameter needs to have a positive value, assuming it is a typo "
|
||||
"therefore the app will convert the value to positive."
|
||||
"Check the resulting CNC code (Gcode etc)."))
|
||||
self.z_move = -self.z_move
|
||||
elif self.z_move == 0:
|
||||
self.app.inform.emit('[WARNING] %s: %s' %
|
||||
(_("The Z Travel parameter is zero. This is dangerous, skipping file"),
|
||||
self.options['name']))
|
||||
return 'fail'
|
||||
|
||||
# made sure that depth_per_cut is no more then the z_cut
|
||||
if abs(self.z_cut) < self.z_depthpercut:
|
||||
self.z_depthpercut = abs(self.z_cut)
|
||||
|
||||
if self.z_move is None:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_("Travel Z parameter is None or zero."))
|
||||
return 'fail'
|
||||
|
||||
if self.z_move < 0:
|
||||
self.app.inform.emit('[WARNING] %s' %
|
||||
_("The Travel Z parameter has negative value. "
|
||||
"It is the height value to travel between cuts.\n"
|
||||
"The Z Travel parameter needs to have a positive value, assuming it is a typo "
|
||||
"therefore the app will convert the value to positive."
|
||||
"Check the resulting CNC code (Gcode etc)."))
|
||||
self.z_move = -self.z_move
|
||||
elif self.z_move == 0:
|
||||
self.app.inform.emit('[WARNING] %s: %s' %
|
||||
(_("The Z Travel parameter is zero. This is dangerous, skipping file"),
|
||||
self.options['name']))
|
||||
return 'fail'
|
||||
|
||||
# ## Index first and last points in paths
|
||||
# What points to index.
|
||||
def get_pts(o):
|
||||
|
@ -3356,11 +3355,11 @@ class CNCjob(Geometry):
|
|||
except ValueError:
|
||||
self.tooldia = [float(el) for el in tooldia.split(',') if el != ''] if tooldia else None
|
||||
|
||||
self.z_cut = float(z_cut) if z_cut else None
|
||||
self.z_move = float(z_move) if z_move else None
|
||||
self.z_cut = float(z_cut) if z_cut is not None else None
|
||||
self.z_move = float(z_move) if z_move is not None else None
|
||||
|
||||
self.feedrate = float(feedrate) if feedrate else None
|
||||
self.z_feedrate = float(feedrate_z) if feedrate_z else None
|
||||
self.z_feedrate = float(feedrate_z) if feedrate_z is not None else None
|
||||
self.feedrate_rapid = float(feedrate_rapid) if feedrate_rapid else None
|
||||
|
||||
self.spindlespeed = int(spindlespeed) if spindlespeed else None
|
||||
|
@ -3368,11 +3367,11 @@ class CNCjob(Geometry):
|
|||
self.dwell = dwell
|
||||
self.dwelltime = float(dwelltime) if dwelltime else None
|
||||
|
||||
self.startz = float(startz) if startz else None
|
||||
self.z_end = float(endz) if endz else None
|
||||
self.startz = float(startz) if startz is not None else None
|
||||
self.z_end = float(endz) if endz is not None else None
|
||||
self.z_depthpercut = float(depthpercut) if depthpercut else None
|
||||
self.multidepth = multidepth
|
||||
self.z_toolchange = float(toolchangez) if toolchangez else None
|
||||
self.z_toolchange = float(toolchangez) if toolchangez is not None else None
|
||||
|
||||
try:
|
||||
if toolchangexy == '':
|
||||
|
@ -3391,44 +3390,45 @@ class CNCjob(Geometry):
|
|||
self.pp_geometry_name = pp_geometry_name if pp_geometry_name else 'default'
|
||||
self.f_plunge = self.app.defaults["geometry_f_plunge"]
|
||||
|
||||
if self.z_cut is None:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_("Cut_Z parameter is None or zero. Most likely a bad combinations of "
|
||||
"other parameters."))
|
||||
return 'fail'
|
||||
if self.machinist_setting == 0:
|
||||
if self.z_cut is None:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_("Cut_Z parameter is None or zero. Most likely a bad combinations of "
|
||||
"other parameters."))
|
||||
return 'fail'
|
||||
|
||||
if self.z_cut > 0:
|
||||
self.app.inform.emit('[WARNING] %s' %
|
||||
_("The Cut Z parameter has positive value. "
|
||||
"It is the depth value to cut into material.\n"
|
||||
"The Cut Z parameter needs to have a negative value, assuming it is a typo "
|
||||
"therefore the app will convert the value to negative."
|
||||
"Check the resulting CNC code (Gcode etc)."))
|
||||
self.z_cut = -self.z_cut
|
||||
elif self.z_cut == 0:
|
||||
self.app.inform.emit('[WARNING] %s: %s' %
|
||||
(_("The Cut Z parameter is zero. There will be no cut, skipping file"),
|
||||
geometry.options['name']))
|
||||
return 'fail'
|
||||
if self.z_cut > 0:
|
||||
self.app.inform.emit('[WARNING] %s' %
|
||||
_("The Cut Z parameter has positive value. "
|
||||
"It is the depth value to cut into material.\n"
|
||||
"The Cut Z parameter needs to have a negative value, assuming it is a typo "
|
||||
"therefore the app will convert the value to negative."
|
||||
"Check the resulting CNC code (Gcode etc)."))
|
||||
self.z_cut = -self.z_cut
|
||||
elif self.z_cut == 0:
|
||||
self.app.inform.emit('[WARNING] %s: %s' %
|
||||
(_("The Cut Z parameter is zero. There will be no cut, skipping file"),
|
||||
geometry.options['name']))
|
||||
return 'fail'
|
||||
|
||||
if self.z_move is None:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_("Travel Z parameter is None or zero."))
|
||||
return 'fail'
|
||||
if self.z_move is None:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_("Travel Z parameter is None or zero."))
|
||||
return 'fail'
|
||||
|
||||
if self.z_move < 0:
|
||||
self.app.inform.emit('[WARNING] %s' %
|
||||
_("The Travel Z parameter has negative value. "
|
||||
"It is the height value to travel between cuts.\n"
|
||||
"The Z Travel parameter needs to have a positive value, assuming it is a typo "
|
||||
"therefore the app will convert the value to positive."
|
||||
"Check the resulting CNC code (Gcode etc)."))
|
||||
self.z_move = -self.z_move
|
||||
elif self.z_move == 0:
|
||||
self.app.inform.emit('[WARNING] %s: %s' %
|
||||
(_("The Z Travel parameter is zero. "
|
||||
"This is dangerous, skipping file"), self.options['name']))
|
||||
return 'fail'
|
||||
if self.z_move < 0:
|
||||
self.app.inform.emit('[WARNING] %s' %
|
||||
_("The Travel Z parameter has negative value. "
|
||||
"It is the height value to travel between cuts.\n"
|
||||
"The Z Travel parameter needs to have a positive value, assuming it is a typo "
|
||||
"therefore the app will convert the value to positive."
|
||||
"Check the resulting CNC code (Gcode etc)."))
|
||||
self.z_move = -self.z_move
|
||||
elif self.z_move == 0:
|
||||
self.app.inform.emit('[WARNING] %s: %s' %
|
||||
(_("The Z Travel parameter is zero. "
|
||||
"This is dangerous, skipping file"), self.options['name']))
|
||||
return 'fail'
|
||||
|
||||
# made sure that depth_per_cut is no more then the z_cut
|
||||
if abs(self.z_cut) < self.z_depthpercut:
|
||||
|
@ -3590,12 +3590,9 @@ class CNCjob(Geometry):
|
|||
self.gcode += self.doformat(p.spindle_stop_code)
|
||||
self.gcode += self.doformat(p.lift_code, x=current_pt[0], y=current_pt[1])
|
||||
self.gcode += self.doformat(p.end_code, x=0, y=0)
|
||||
self.app.inform.emit('%s... %s %s' %
|
||||
(_("Finished G-Code generation"),
|
||||
str(path_count),
|
||||
_(" paths traced.")
|
||||
)
|
||||
)
|
||||
self.app.inform.emit(
|
||||
'%s... %s %s' % (_("Finished G-Code generation"), str(path_count), _(" paths traced."))
|
||||
)
|
||||
|
||||
return self.gcode
|
||||
|
||||
|
|
|
@ -1449,7 +1449,7 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
self.decimals = 4
|
||||
|
||||
# ## Current application units in Upper Case
|
||||
self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper()
|
||||
self.units = self.app.defaults['units'].upper()
|
||||
|
||||
self.exc_edit_widget = QtWidgets.QWidget()
|
||||
# ## Box for custom widgets
|
||||
|
@ -2099,7 +2099,7 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
"corner_snap": False,
|
||||
"grid_gap_link": True
|
||||
}
|
||||
self.app.options_read_form()
|
||||
self.options.update(self.app.options)
|
||||
|
||||
for option in self.options:
|
||||
if option in self.app.options:
|
||||
|
|
|
@ -3139,7 +3139,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
"corner_snap": False,
|
||||
"grid_gap_link": True
|
||||
}
|
||||
self.app.options_read_form()
|
||||
self.options.update(self.app.options)
|
||||
|
||||
for option in self.options:
|
||||
if option in self.app.options:
|
||||
|
@ -4664,17 +4664,16 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
poly_buf = Polygon(geo_obj).buffer(-margin)
|
||||
|
||||
if method == "seed":
|
||||
cp = Geometry.clear_polygon2(poly_buf,
|
||||
tooldia, self.app.defaults["geometry_circle_steps"],
|
||||
cp = Geometry.clear_polygon2(self, polygon_to_clear=poly_buf, tooldia=tooldia,
|
||||
steps_per_circle=self.app.defaults["geometry_circle_steps"],
|
||||
overlap=overlap, contour=contour, connect=connect)
|
||||
elif method == "lines":
|
||||
cp = Geometry.clear_polygon3(poly_buf,
|
||||
tooldia, self.app.defaults["geometry_circle_steps"],
|
||||
cp = Geometry.clear_polygon3(self, polygon=poly_buf, tooldia=tooldia,
|
||||
steps_per_circle=self.app.defaults["geometry_circle_steps"],
|
||||
overlap=overlap, contour=contour, connect=connect)
|
||||
|
||||
else:
|
||||
cp = Geometry.clear_polygon(poly_buf,
|
||||
tooldia, self.app.defaults["geometry_circle_steps"],
|
||||
cp = Geometry.clear_polygon(self, polygon=poly_buf, tooldia=tooldia,
|
||||
steps_per_circle=self.app.defaults["geometry_circle_steps"],
|
||||
overlap=overlap, contour=contour, connect=connect)
|
||||
|
||||
if cp is not None:
|
||||
|
|
|
@ -2356,7 +2356,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
self.decimals = 4
|
||||
|
||||
# Current application units in Upper Case
|
||||
self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper()
|
||||
self.units = self.app.defaults['units'].upper()
|
||||
|
||||
self.grb_edit_widget = QtWidgets.QWidget()
|
||||
layout = QtWidgets.QVBoxLayout()
|
||||
|
@ -2947,7 +2947,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
"corner_snap": False,
|
||||
"grid_gap_link": True
|
||||
}
|
||||
self.app.options_read_form()
|
||||
self.options.update(self.app.options)
|
||||
|
||||
for option in self.options:
|
||||
if option in self.app.options:
|
||||
|
@ -3023,7 +3023,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
|
||||
def set_ui(self):
|
||||
# updated units
|
||||
self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper()
|
||||
self.units = self.app.defaults['units'].upper()
|
||||
|
||||
if self.units == "IN":
|
||||
self.decimals = 4
|
||||
|
|
|
@ -193,12 +193,12 @@ class TextEditor(QtWidgets.QWidget):
|
|||
|
||||
try:
|
||||
filename = str(QtWidgets.QFileDialog.getSaveFileName(
|
||||
caption=_("Export G-Code ..."),
|
||||
caption=_("Export Code ..."),
|
||||
directory=self.app.defaults["global_last_folder"] + '/' + str(obj_name),
|
||||
filter=_filter_
|
||||
)[0])
|
||||
except TypeError:
|
||||
filename = str(QtWidgets.QFileDialog.getSaveFileName(caption=_("Export G-Code ..."), filter=_filter_)[0])
|
||||
filename = str(QtWidgets.QFileDialog.getSaveFileName(caption=_("Export Code ..."), filter=_filter_)[0])
|
||||
|
||||
if filename == "":
|
||||
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Export Code cancelled."))
|
||||
|
|
|
@ -337,21 +337,11 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.menuedit.addSeparator()
|
||||
self.menueditpreferences = self.menuedit.addAction(QtGui.QIcon('share/pref.png'), _('&Preferences\tSHIFT+P'))
|
||||
|
||||
# ## Options # ##
|
||||
# ########################################################################
|
||||
# ########################## OPTIONS # ###################################
|
||||
# ########################################################################
|
||||
|
||||
self.menuoptions = self.menu.addMenu(_('Options'))
|
||||
# self.menuoptions_transfer = self.menuoptions.addMenu(QtGui.QIcon('share/transfer.png'), 'Transfer options')
|
||||
# self.menuoptions_transfer_a2p = self.menuoptions_transfer.addAction("Application to Project")
|
||||
# self.menuoptions_transfer_p2a = self.menuoptions_transfer.addAction("Project to Application")
|
||||
# self.menuoptions_transfer_p2o = self.menuoptions_transfer.addAction("Project to Object")
|
||||
# self.menuoptions_transfer_o2p = self.menuoptions_transfer.addAction("Object to Project")
|
||||
# self.menuoptions_transfer_a2o = self.menuoptions_transfer.addAction("Application to Object")
|
||||
# self.menuoptions_transfer_o2a = self.menuoptions_transfer.addAction("Object to Application")
|
||||
|
||||
# Separator
|
||||
# self.menuoptions.addSeparator()
|
||||
|
||||
# self.menuoptions_transform = self.menuoptions.addMenu(QtGui.QIcon('share/transform.png'),
|
||||
# '&Transform Object')
|
||||
self.menuoptions_transform_rotate = self.menuoptions.addAction(QtGui.QIcon('share/rotate.png'),
|
||||
_("&Rotate Selection\tSHIFT+(R)"))
|
||||
# Separator
|
||||
|
@ -373,6 +363,8 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
|
||||
self.menuoptions_view_source = self.menuoptions.addAction(QtGui.QIcon('share/source32.png'),
|
||||
_("View source\tALT+S"))
|
||||
self.menuoptions_tools_db = self.menuoptions.addAction(QtGui.QIcon('share/database32.png'),
|
||||
_("Tools DataBase\tCTRL+D"))
|
||||
# Separator
|
||||
self.menuoptions.addSeparator()
|
||||
|
||||
|
@ -958,6 +950,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
# ########################## PREFERENCES AREA Tab # ######################
|
||||
# ########################################################################
|
||||
self.preferences_tab = QtWidgets.QWidget()
|
||||
self.preferences_tab.setObjectName("preferences_tab")
|
||||
self.pref_tab_layout = QtWidgets.QVBoxLayout(self.preferences_tab)
|
||||
self.pref_tab_layout.setContentsMargins(2, 2, 2, 2)
|
||||
|
||||
|
@ -978,13 +971,6 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.hlay1 = QtWidgets.QHBoxLayout()
|
||||
self.general_tab_lay.addLayout(self.hlay1)
|
||||
|
||||
self.options_combo = QtWidgets.QComboBox()
|
||||
self.options_combo.addItem(_("APP. DEFAULTS"))
|
||||
self.options_combo.addItem(_("PROJ. OPTIONS "))
|
||||
self.hlay1.addWidget(self.options_combo)
|
||||
|
||||
# disable this button as it may no longer be useful
|
||||
self.options_combo.setVisible(False)
|
||||
self.hlay1.addStretch()
|
||||
|
||||
self.general_scroll_area = QtWidgets.QScrollArea()
|
||||
|
@ -1236,6 +1222,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
<td height="20"><strong>CTRL+C</strong></td>
|
||||
<td> %s</td>
|
||||
</tr>
|
||||
<tr height="20">
|
||||
<td height="20"><strong>CTRL+D</strong></td>
|
||||
<td> %s</td>
|
||||
</tr>
|
||||
<tr height="20">
|
||||
<td height="20"><strong>CTRL+E</strong></td>
|
||||
<td> %s</td>
|
||||
|
@ -1437,7 +1427,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
_("Flip on X_axis"), _("Flip on Y_axis"), _("Zoom Out"), _("Zoom In"),
|
||||
|
||||
# CTRL section
|
||||
_("Select All"), _("Copy Obj"),
|
||||
_("Select All"), _("Copy Obj"), _("Open Tools Database"),
|
||||
_("Open Excellon File"), _("Open Gerber File"), _("New Project"), _("Distance Tool"),
|
||||
_("Open Project"), _("PDF Import Tool"), _("Save Project As"), _("Toggle Plot Area"),
|
||||
|
||||
|
@ -2022,15 +2012,6 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.tools2_defaults_form = Tools2PreferencesUI()
|
||||
self.util_defaults_form = UtilPreferencesUI()
|
||||
|
||||
self.general_options_form = GeneralPreferencesUI()
|
||||
self.gerber_options_form = GerberPreferencesUI()
|
||||
self.excellon_options_form = ExcellonPreferencesUI()
|
||||
self.geometry_options_form = GeometryPreferencesUI()
|
||||
self.cncjob_options_form = CNCJobPreferencesUI()
|
||||
self.tools_options_form = ToolsPreferencesUI()
|
||||
self.tools2_options_form = Tools2PreferencesUI()
|
||||
self.util_options_form = UtilPreferencesUI()
|
||||
|
||||
QtWidgets.qApp.installEventFilter(self)
|
||||
|
||||
# restore the Toolbar State from file
|
||||
|
@ -2399,6 +2380,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
if key == QtCore.Qt.Key_C:
|
||||
self.app.on_copy_object()
|
||||
|
||||
# Copy an FlatCAM object
|
||||
if key == QtCore.Qt.Key_D:
|
||||
self.app.on_tools_database()
|
||||
|
||||
# Open Excellon file
|
||||
if key == QtCore.Qt.Key_E:
|
||||
self.app.on_fileopenexcellon()
|
||||
|
@ -2425,6 +2410,17 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
|
||||
# Save Project
|
||||
if key == QtCore.Qt.Key_S:
|
||||
widget_name = self.plot_tab_area.currentWidget().objectName()
|
||||
if widget_name == 'preferences_tab':
|
||||
self.app.on_save_button()
|
||||
return
|
||||
|
||||
if widget_name == 'database_tab':
|
||||
# Tools DB saved, update flag
|
||||
self.app.tools_db_changed_flag = False
|
||||
self.app.tools_db_tab.on_save_tools_db()
|
||||
return
|
||||
|
||||
self.app.on_file_saveproject()
|
||||
|
||||
# Toggle Plot Area
|
||||
|
@ -2764,7 +2760,6 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
messagebox.setDefaultButton(QtWidgets.QMessageBox.Ok)
|
||||
messagebox.exec_()
|
||||
return
|
||||
|
||||
elif modifiers == QtCore.Qt.ShiftModifier:
|
||||
# Run Distance Minimum Tool
|
||||
if key == QtCore.Qt.Key_M or key == 'M':
|
||||
|
@ -2854,10 +2849,12 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
if key == QtCore.Qt.Key_Space or key == 'Space':
|
||||
self.app.geo_editor.transform_tool.on_rotate_key()
|
||||
|
||||
# Zoom Out
|
||||
if key == QtCore.Qt.Key_Minus or key == '-':
|
||||
self.app.plotcanvas.zoom(1 / self.app.defaults['global_zoom_ratio'],
|
||||
[self.app.geo_editor.snap_x, self.app.geo_editor.snap_y])
|
||||
|
||||
# Zoom In
|
||||
if key == QtCore.Qt.Key_Equal or key == '=':
|
||||
self.app.plotcanvas.zoom(self.app.defaults['global_zoom_ratio'],
|
||||
[self.app.geo_editor.snap_x, self.app.geo_editor.snap_y])
|
||||
|
@ -3649,9 +3646,27 @@ class FlatCAMActivityView(QtWidgets.QWidget):
|
|||
This class create and control the activity icon displayed in the App status bar
|
||||
"""
|
||||
|
||||
def __init__(self, movie="share/active.gif", icon='share/active_static.png', parent=None):
|
||||
def __init__(self, app, parent=None):
|
||||
super().__init__(parent=parent)
|
||||
|
||||
self.app = app
|
||||
|
||||
if self.app.defaults["global_activity_icon"] == "Ball green":
|
||||
icon = 'share/active_2_static.png'
|
||||
movie = "share/active_2.gif"
|
||||
elif self.app.defaults["global_activity_icon"] == "Ball black":
|
||||
icon = 'share/active_static.png'
|
||||
movie = "share/active.gif"
|
||||
elif self.app.defaults["global_activity_icon"] == "Arrow green":
|
||||
icon = 'share/active_3_static.png'
|
||||
movie = "share/active_3.gif"
|
||||
elif self.app.defaults["global_activity_icon"] == "Eclipse green":
|
||||
icon = 'share/active_4_static.png'
|
||||
movie = "share/active_4.gif"
|
||||
else:
|
||||
icon = 'share/active_static.png'
|
||||
movie = "share/active.gif"
|
||||
|
||||
self.setMinimumWidth(200)
|
||||
self.movie_path = movie
|
||||
self.icon_path = icon
|
||||
|
@ -3797,377 +3812,4 @@ class FlatCAMSystemTray(QtWidgets.QSystemTrayIcon):
|
|||
|
||||
exitAction.triggered.connect(self.app.final_save)
|
||||
|
||||
|
||||
class BookmarkManager(QtWidgets.QWidget):
|
||||
|
||||
mark_rows = QtCore.pyqtSignal()
|
||||
|
||||
def __init__(self, app, storage, parent=None):
|
||||
super(BookmarkManager, self).__init__(parent)
|
||||
|
||||
self.app = app
|
||||
|
||||
assert isinstance(storage, dict), "Storage argument is not a dictionary"
|
||||
|
||||
self.bm_dict = deepcopy(storage)
|
||||
|
||||
# Icon and title
|
||||
# self.setWindowIcon(parent.app_icon)
|
||||
# self.setWindowTitle(_("Bookmark Manager"))
|
||||
# self.resize(600, 400)
|
||||
|
||||
# title = QtWidgets.QLabel(
|
||||
# "<font size=8><B>FlatCAM</B></font><BR>"
|
||||
# )
|
||||
# title.setOpenExternalLinks(True)
|
||||
|
||||
# layouts
|
||||
layout = QtWidgets.QVBoxLayout()
|
||||
self.setLayout(layout)
|
||||
|
||||
table_hlay = QtWidgets.QHBoxLayout()
|
||||
layout.addLayout(table_hlay)
|
||||
|
||||
self.table_widget = FCTable(drag_drop=True, protected_rows=[0, 1])
|
||||
self.table_widget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
||||
table_hlay.addWidget(self.table_widget)
|
||||
|
||||
self.table_widget.setColumnCount(3)
|
||||
self.table_widget.setColumnWidth(0, 20)
|
||||
self.table_widget.setHorizontalHeaderLabels(
|
||||
[
|
||||
'#',
|
||||
_('Title'),
|
||||
_('Web Link')
|
||||
]
|
||||
)
|
||||
self.table_widget.horizontalHeaderItem(0).setToolTip(
|
||||
_("Index.\n"
|
||||
"The rows in gray color will populate the Bookmarks menu.\n"
|
||||
"The number of gray colored rows is set in Preferences."))
|
||||
self.table_widget.horizontalHeaderItem(1).setToolTip(
|
||||
_("Description of the link that is set as an menu action.\n"
|
||||
"Try to keep it short because it is installed as a menu item."))
|
||||
self.table_widget.horizontalHeaderItem(2).setToolTip(
|
||||
_("Web Link. E.g: https://your_website.org "))
|
||||
|
||||
# pal = QtGui.QPalette()
|
||||
# pal.setColor(QtGui.QPalette.Background, Qt.white)
|
||||
|
||||
# New Bookmark
|
||||
new_vlay = QtWidgets.QVBoxLayout()
|
||||
layout.addLayout(new_vlay)
|
||||
|
||||
new_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _("New Bookmark"))
|
||||
new_vlay.addWidget(new_title_lbl)
|
||||
|
||||
form0 = QtWidgets.QFormLayout()
|
||||
new_vlay.addLayout(form0)
|
||||
|
||||
title_lbl = QtWidgets.QLabel('%s:' % _("Title"))
|
||||
self.title_entry = FCEntry()
|
||||
form0.addRow(title_lbl, self.title_entry)
|
||||
|
||||
link_lbl = QtWidgets.QLabel('%s:' % _("Web Link"))
|
||||
self.link_entry = FCEntry()
|
||||
self.link_entry.set_value('http://')
|
||||
form0.addRow(link_lbl, self.link_entry)
|
||||
|
||||
# Buttons Layout
|
||||
button_hlay = QtWidgets.QHBoxLayout()
|
||||
layout.addLayout(button_hlay)
|
||||
|
||||
add_entry_btn = FCButton(_("Add Entry"))
|
||||
remove_entry_btn = FCButton(_("Remove Entry"))
|
||||
export_list_btn = FCButton(_("Export List"))
|
||||
import_list_btn = FCButton(_("Import List"))
|
||||
closebtn = QtWidgets.QPushButton(_("Close"))
|
||||
|
||||
# button_hlay.addStretch()
|
||||
button_hlay.addWidget(add_entry_btn)
|
||||
button_hlay.addWidget(remove_entry_btn)
|
||||
|
||||
button_hlay.addWidget(export_list_btn)
|
||||
button_hlay.addWidget(import_list_btn)
|
||||
# button_hlay.addWidget(closebtn)
|
||||
# ##############################################################################
|
||||
# ######################## SIGNALS #############################################
|
||||
# ##############################################################################
|
||||
|
||||
add_entry_btn.clicked.connect(self.on_add_entry)
|
||||
remove_entry_btn.clicked.connect(self.on_remove_entry)
|
||||
export_list_btn.clicked.connect(self.on_export_bookmarks)
|
||||
import_list_btn.clicked.connect(self.on_import_bookmarks)
|
||||
self.title_entry.returnPressed.connect(self.on_add_entry)
|
||||
self.link_entry.returnPressed.connect(self.on_add_entry)
|
||||
# closebtn.clicked.connect(self.accept)
|
||||
|
||||
self.table_widget.drag_drop_sig.connect(self.mark_table_rows_for_actions)
|
||||
self.build_bm_ui()
|
||||
|
||||
def build_bm_ui(self):
|
||||
|
||||
self.table_widget.setRowCount(len(self.bm_dict))
|
||||
|
||||
nr_crt = 0
|
||||
sorted_bookmarks = sorted(list(self.bm_dict.items()), key=lambda x: int(x[0]))
|
||||
for entry, bookmark in sorted_bookmarks:
|
||||
row = nr_crt
|
||||
nr_crt += 1
|
||||
|
||||
title = bookmark[0]
|
||||
weblink = bookmark[1]
|
||||
|
||||
id_item = QtWidgets.QTableWidgetItem('%d' % int(nr_crt))
|
||||
# id.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
|
||||
self.table_widget.setItem(row, 0, id_item) # Tool name/id
|
||||
|
||||
title_item = QtWidgets.QTableWidgetItem(title)
|
||||
self.table_widget.setItem(row, 1, title_item)
|
||||
|
||||
weblink_txt = QtWidgets.QTextBrowser()
|
||||
weblink_txt.setOpenExternalLinks(True)
|
||||
weblink_txt.setFrameStyle(QtWidgets.QFrame.NoFrame)
|
||||
weblink_txt.document().setDefaultStyleSheet("a{ text-decoration: none; }")
|
||||
|
||||
weblink_txt.setHtml('<a href=%s>%s</a>' % (weblink, weblink))
|
||||
|
||||
self.table_widget.setCellWidget(row, 2, weblink_txt)
|
||||
|
||||
vertical_header = self.table_widget.verticalHeader()
|
||||
vertical_header.hide()
|
||||
|
||||
horizontal_header = self.table_widget.horizontalHeader()
|
||||
horizontal_header.setMinimumSectionSize(10)
|
||||
horizontal_header.setDefaultSectionSize(70)
|
||||
horizontal_header.setSectionResizeMode(0, QtWidgets.QHeaderView.Fixed)
|
||||
horizontal_header.resizeSection(0, 20)
|
||||
horizontal_header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents)
|
||||
horizontal_header.setSectionResizeMode(2, QtWidgets.QHeaderView.Stretch)
|
||||
|
||||
self.mark_table_rows_for_actions()
|
||||
|
||||
self.app.defaults["global_bookmarks"].clear()
|
||||
for key, val in self.bm_dict.items():
|
||||
self.app.defaults["global_bookmarks"][key] = deepcopy(val)
|
||||
|
||||
def on_add_entry(self, **kwargs):
|
||||
"""
|
||||
Add a entry in the Bookmark Table and in the menu actions
|
||||
:return: None
|
||||
"""
|
||||
if 'title' in kwargs:
|
||||
title = kwargs['title']
|
||||
else:
|
||||
title = self.title_entry.get_value()
|
||||
if title == '':
|
||||
self.app.inform.emit(f'[ERROR_NOTCL] {_("Title entry is empty.")}')
|
||||
return 'fail'
|
||||
|
||||
if 'link' is kwargs:
|
||||
link = kwargs['link']
|
||||
else:
|
||||
link = self.link_entry.get_value()
|
||||
|
||||
if link == 'http://':
|
||||
self.app.inform.emit(f'[ERROR_NOTCL] {_("Web link entry is empty.")}')
|
||||
return 'fail'
|
||||
|
||||
# if 'http' not in link or 'https' not in link:
|
||||
# link = 'http://' + link
|
||||
|
||||
for bookmark in self.bm_dict.values():
|
||||
if title == bookmark[0] or link == bookmark[1]:
|
||||
self.app.inform.emit(f'[ERROR_NOTCL] {_("Either the Title or the Weblink already in the table.")}')
|
||||
return 'fail'
|
||||
|
||||
# for some reason if the last char in the weblink is a slash it does not make the link clickable
|
||||
# so I remove it
|
||||
if link[-1] == '/':
|
||||
link = link[:-1]
|
||||
# add the new entry to storage
|
||||
new_entry = len(self.bm_dict) + 1
|
||||
self.bm_dict[str(new_entry)] = [title, link]
|
||||
|
||||
# add the link to the menu but only if it is within the set limit
|
||||
bm_limit = int(self.app.defaults["global_bookmarks_limit"])
|
||||
if len(self.bm_dict) < bm_limit:
|
||||
act = QtWidgets.QAction(parent=self.app.ui.menuhelp_bookmarks)
|
||||
act.setText(title)
|
||||
act.setIcon(QtGui.QIcon('share/link16.png'))
|
||||
act.triggered.connect(lambda: webbrowser.open(link))
|
||||
self.app.ui.menuhelp_bookmarks.insertAction(self.app.ui.menuhelp_bookmarks_manager, act)
|
||||
|
||||
self.app.inform.emit(f'[success] {_("Bookmark added.")}')
|
||||
|
||||
# add the new entry to the bookmark manager table
|
||||
self.build_bm_ui()
|
||||
|
||||
def on_remove_entry(self):
|
||||
"""
|
||||
Remove an Entry in the Bookmark table and from the menu actions
|
||||
:return:
|
||||
"""
|
||||
index_list = []
|
||||
for model_index in self.table_widget.selectionModel().selectedRows():
|
||||
index = QtCore.QPersistentModelIndex(model_index)
|
||||
index_list.append(index)
|
||||
title_to_remove = self.table_widget.item(model_index.row(), 1).text()
|
||||
|
||||
if title_to_remove == 'FlatCAM' or title_to_remove == 'Backup Site':
|
||||
self.app.inform.emit('[WARNING_NOTCL] %s.' % _("This bookmark can not be removed"))
|
||||
self.build_bm_ui()
|
||||
return
|
||||
else:
|
||||
for k, bookmark in list(self.bm_dict.items()):
|
||||
if title_to_remove == bookmark[0]:
|
||||
# remove from the storage
|
||||
self.bm_dict.pop(k, None)
|
||||
|
||||
for act in self.app.ui.menuhelp_bookmarks.actions():
|
||||
if act.text() == title_to_remove:
|
||||
# disconnect the signal
|
||||
try:
|
||||
act.triggered.disconnect()
|
||||
except TypeError:
|
||||
pass
|
||||
# remove the action from the menu
|
||||
self.app.ui.menuhelp_bookmarks.removeAction(act)
|
||||
|
||||
# house keeping: it pays to have keys increased by one
|
||||
new_key = 0
|
||||
new_dict = dict()
|
||||
for k, v in self.bm_dict.items():
|
||||
# we start with key 1 so we can use the len(self.bm_dict)
|
||||
# when adding bookmarks (keys in bm_dict)
|
||||
new_key += 1
|
||||
new_dict[str(new_key)] = v
|
||||
|
||||
self.bm_dict = deepcopy(new_dict)
|
||||
new_dict.clear()
|
||||
|
||||
self.app.inform.emit(f'[success] {_("Bookmark removed.")}')
|
||||
|
||||
# for index in index_list:
|
||||
# self.table_widget.model().removeRow(index.row())
|
||||
self.build_bm_ui()
|
||||
|
||||
def on_export_bookmarks(self):
|
||||
self.app.report_usage("on_export_bookmarks")
|
||||
self.app.log.debug("on_export_bookmarks()")
|
||||
|
||||
date = str(datetime.today()).rpartition('.')[0]
|
||||
date = ''.join(c for c in date if c not in ':-')
|
||||
date = date.replace(' ', '_')
|
||||
|
||||
filter__ = "Text File (*.TXT);;All Files (*.*)"
|
||||
filename, _f = QtWidgets.QFileDialog.getSaveFileName(caption=_("Export FlatCAM Preferences"),
|
||||
directory='{l_save}/FlatCAM_{n}_{date}'.format(
|
||||
l_save=str(self.app.get_last_save_folder()),
|
||||
n=_("Bookmarks"),
|
||||
date=date),
|
||||
filter=filter__)
|
||||
|
||||
filename = str(filename)
|
||||
|
||||
if filename == "":
|
||||
self.app.inform.emit('[WARNING_NOTCL] %s' % _("FlatCAM bookmarks export cancelled."))
|
||||
return
|
||||
else:
|
||||
try:
|
||||
f = open(filename, 'w')
|
||||
f.close()
|
||||
except PermissionError:
|
||||
self.app.inform.emit('[WARNING] %s' %
|
||||
_("Permission denied, saving not possible.\n"
|
||||
"Most likely another app is holding the file open and not accessible."))
|
||||
return
|
||||
except IOError:
|
||||
self.app.log.debug('Creating a new bookmarks file ...')
|
||||
f = open(filename, 'w')
|
||||
f.close()
|
||||
except:
|
||||
e = sys.exc_info()[0]
|
||||
self.app.log.error("Could not load defaults file.")
|
||||
self.app.log.error(str(e))
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_("Could not load bookmarks file."))
|
||||
return
|
||||
|
||||
# Save update options
|
||||
try:
|
||||
with open(filename, "w") as f:
|
||||
for title, link in self.bm_dict.items():
|
||||
line2write = str(title) + ':' + str(link) + '\n'
|
||||
f.write(line2write)
|
||||
except:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_("Failed to write bookmarks to file."))
|
||||
return
|
||||
self.app.inform.emit('[success] %s: %s' %
|
||||
(_("Exported bookmarks to"), filename))
|
||||
|
||||
def on_import_bookmarks(self):
|
||||
self.app.log.debug("on_import_bookmarks()")
|
||||
|
||||
filter_ = "Text File (*.txt);;All Files (*.*)"
|
||||
filename, _f = QtWidgets.QFileDialog.getOpenFileName(caption=_("Import FlatCAM Bookmarks"),
|
||||
filter=filter_)
|
||||
|
||||
filename = str(filename)
|
||||
|
||||
if filename == "":
|
||||
self.app.inform.emit('[WARNING_NOTCL] %s' %
|
||||
_("FlatCAM bookmarks import cancelled."))
|
||||
else:
|
||||
try:
|
||||
with open(filename) as f:
|
||||
bookmarks = f.readlines()
|
||||
except IOError:
|
||||
self.app.log.error("Could not load bookmarks file.")
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_("Could not load bookmarks file."))
|
||||
return
|
||||
|
||||
for line in bookmarks:
|
||||
proc_line = line.replace(' ', '').partition(':')
|
||||
self.on_add_entry(title=proc_line[0], link=proc_line[2])
|
||||
|
||||
self.app.inform.emit('[success] %s: %s' %
|
||||
(_("Imported Bookmarks from"), filename))
|
||||
|
||||
def mark_table_rows_for_actions(self):
|
||||
for row in range(self.table_widget.rowCount()):
|
||||
item_to_paint = self.table_widget.item(row, 0)
|
||||
if row < self.app.defaults["global_bookmarks_limit"]:
|
||||
item_to_paint.setBackground(QtGui.QColor('gray'))
|
||||
# item_to_paint.setForeground(QtGui.QColor('black'))
|
||||
else:
|
||||
item_to_paint.setBackground(QtGui.QColor('white'))
|
||||
# item_to_paint.setForeground(QtGui.QColor('black'))
|
||||
|
||||
def rebuild_actions(self):
|
||||
# rebuild the storage to reflect the order of the lines
|
||||
self.bm_dict.clear()
|
||||
for row in range(self.table_widget.rowCount()):
|
||||
title = self.table_widget.item(row, 1).text()
|
||||
wlink = self.table_widget.cellWidget(row, 2).toPlainText()
|
||||
|
||||
entry = int(row) + 1
|
||||
self.bm_dict.update(
|
||||
{
|
||||
str(entry): [title, wlink]
|
||||
}
|
||||
)
|
||||
|
||||
self.app.install_bookmarks(book_dict=self.bm_dict)
|
||||
|
||||
# def accept(self):
|
||||
# self.rebuild_actions()
|
||||
# super().accept()
|
||||
|
||||
def closeEvent(self, QCloseEvent):
|
||||
self.rebuild_actions()
|
||||
super().closeEvent(QCloseEvent)
|
||||
|
||||
# end of file
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
# ##########################################################
|
||||
|
||||
from PyQt5 import QtGui, QtCore, QtWidgets
|
||||
from PyQt5.QtCore import Qt, pyqtSlot
|
||||
from PyQt5.QtCore import Qt, pyqtSlot, QSettings
|
||||
from PyQt5.QtWidgets import QTextEdit, QCompleter, QAction
|
||||
from PyQt5.QtGui import QKeySequence, QTextCursor
|
||||
|
||||
|
@ -376,9 +376,9 @@ class FCEntry(QtWidgets.QLineEdit):
|
|||
def get_value(self):
|
||||
return str(self.text())
|
||||
|
||||
def set_value(self, val):
|
||||
def set_value(self, val, decimals=4):
|
||||
if type(val) is float:
|
||||
self.setText('%.4f' % val)
|
||||
self.setText('%.*f' % (decimals, val))
|
||||
else:
|
||||
self.setText(str(val))
|
||||
|
||||
|
|
|
@ -22,6 +22,12 @@ fcTranslate.apply_language('strings')
|
|||
if '_' not in builtins.__dict__:
|
||||
_ = gettext.gettext
|
||||
|
||||
settings = QtCore.QSettings("Open Source", "FlatCAM")
|
||||
if settings.contains("machinist"):
|
||||
machinist_setting = settings.value('machinist', type=int)
|
||||
else:
|
||||
machinist_setting = 0
|
||||
|
||||
|
||||
class ObjectUI(QtWidgets.QWidget):
|
||||
"""
|
||||
|
@ -385,7 +391,7 @@ class GerberObjectUI(ObjectUI):
|
|||
"- conventional / useful when there is no backlash compensation")
|
||||
)
|
||||
self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
|
||||
{'label': _('Conv.'), 'value': 'cv'}])
|
||||
{'label': _('Conventional'), 'value': 'cv'}])
|
||||
grid1.addWidget(self.milling_type_label, 7, 0)
|
||||
grid1.addWidget(self.milling_type_radio, 7, 1, 1, 2)
|
||||
|
||||
|
@ -754,7 +760,12 @@ class ExcellonObjectUI(ObjectUI):
|
|||
grid1.addWidget(cutzlabel, 0, 0)
|
||||
self.cutz_entry = FCDoubleSpinner()
|
||||
self.cutz_entry.set_precision(self.decimals)
|
||||
self.cutz_entry.setRange(-9999.9999, -0.000001)
|
||||
|
||||
if machinist_setting == 0:
|
||||
self.cutz_entry.setRange(-9999.9999, -0.000001)
|
||||
else:
|
||||
self.cutz_entry.setRange(-9999.9999, 9999.9999)
|
||||
|
||||
self.cutz_entry.setSingleStep(0.1)
|
||||
|
||||
grid1.addWidget(self.cutz_entry, 0, 1)
|
||||
|
@ -768,7 +779,12 @@ class ExcellonObjectUI(ObjectUI):
|
|||
grid1.addWidget(travelzlabel, 1, 0)
|
||||
self.travelz_entry = FCDoubleSpinner()
|
||||
self.travelz_entry.set_precision(self.decimals)
|
||||
self.travelz_entry.setRange(0.0, 9999.9999)
|
||||
|
||||
if machinist_setting == 0:
|
||||
self.travelz_entry.setRange(0.00001, 9999.9999)
|
||||
else:
|
||||
self.travelz_entry.setRange(-9999.9999, 9999.9999)
|
||||
|
||||
self.travelz_entry.setSingleStep(0.1)
|
||||
|
||||
grid1.addWidget(self.travelz_entry, 1, 1)
|
||||
|
@ -790,7 +806,12 @@ class ExcellonObjectUI(ObjectUI):
|
|||
grid1.addWidget(toolchzlabel, 3, 0)
|
||||
self.toolchangez_entry = FCDoubleSpinner()
|
||||
self.toolchangez_entry.set_precision(self.decimals)
|
||||
self.toolchangez_entry.setRange(0.0, 9999.9999)
|
||||
|
||||
if machinist_setting == 0:
|
||||
self.toolchangez_entry.setRange(0.0, 9999.9999)
|
||||
else:
|
||||
self.toolchangez_entry.setRange(-9999.9999, 9999.9999)
|
||||
|
||||
self.toolchangez_entry.setSingleStep(0.1)
|
||||
|
||||
grid1.addWidget(self.toolchangez_entry, 3, 1)
|
||||
|
@ -815,7 +836,12 @@ class ExcellonObjectUI(ObjectUI):
|
|||
grid1.addWidget(self.eendz_label, 5, 0)
|
||||
self.eendz_entry = FCDoubleSpinner()
|
||||
self.eendz_entry.set_precision(self.decimals)
|
||||
self.eendz_entry.setRange(0.0, 9999.9999)
|
||||
|
||||
if machinist_setting == 0:
|
||||
self.eendz_entry.setRange(0.0, 9999.9999)
|
||||
else:
|
||||
self.eendz_entry.setRange(-9999.9999, 9999.9999)
|
||||
|
||||
self.eendz_entry.setSingleStep(0.1)
|
||||
|
||||
grid1.addWidget(self.eendz_entry, 5, 1)
|
||||
|
@ -934,12 +960,11 @@ class ExcellonObjectUI(ObjectUI):
|
|||
grid2.setColumnStretch(0, 0)
|
||||
grid2.setColumnStretch(1, 1)
|
||||
|
||||
choose_tools_label = QtWidgets.QLabel(
|
||||
_("Select from the Tools Table above\n"
|
||||
"the hole dias that are to be drilled.\n"
|
||||
"Use the # column to make the selection.")
|
||||
)
|
||||
grid2.addWidget(choose_tools_label, 0, 0, 1, 3)
|
||||
# choose_tools_label = QtWidgets.QLabel(
|
||||
# _("Select from the Tools Table above the hole dias to be\n"
|
||||
# "drilled. Use the # column to make the selection.")
|
||||
# )
|
||||
# grid2.addWidget(choose_tools_label, 0, 0, 1, 3)
|
||||
|
||||
# ### Choose what to use for Gcode creation: Drills, Slots or Both
|
||||
gcode_type_label = QtWidgets.QLabel('<b>%s</b>' % _('Gcode'))
|
||||
|
@ -967,17 +992,12 @@ class ExcellonObjectUI(ObjectUI):
|
|||
# ### Milling Holes Drills ####
|
||||
self.mill_hole_label = QtWidgets.QLabel('<b>%s</b>' % _('Mill Holes'))
|
||||
self.mill_hole_label.setToolTip(
|
||||
_("Create Geometry for milling holes.")
|
||||
_("Create Geometry for milling holes.\n"
|
||||
"Select from the Tools Table above the hole dias to be\n"
|
||||
"milled. Use the # column to make the selection.")
|
||||
)
|
||||
grid2.addWidget(self.mill_hole_label, 3, 0, 1, 3)
|
||||
|
||||
self.choose_tools_label2 = QtWidgets.QLabel(
|
||||
_("Select from the Tools Table above\n"
|
||||
"the hole dias that are to be milled.\n"
|
||||
"Use the # column to make the selection.")
|
||||
)
|
||||
grid2.addWidget(self.choose_tools_label2, 4, 0, 1, 3)
|
||||
|
||||
self.tdlabel = QtWidgets.QLabel('%s:' % _('Drill Tool dia'))
|
||||
self.tdlabel.setToolTip(
|
||||
_("Diameter of the cutting tool.")
|
||||
|
@ -993,9 +1013,9 @@ class ExcellonObjectUI(ObjectUI):
|
|||
"for milling DRILLS toolpaths.")
|
||||
)
|
||||
|
||||
grid2.addWidget(self.tdlabel, 5, 0)
|
||||
grid2.addWidget(self.tooldia_entry, 5, 1)
|
||||
grid2.addWidget(self.generate_milling_button, 5, 2)
|
||||
grid2.addWidget(self.tdlabel, 4, 0)
|
||||
grid2.addWidget(self.tooldia_entry, 4, 1)
|
||||
grid2.addWidget(self.generate_milling_button, 4, 2)
|
||||
|
||||
self.stdlabel = QtWidgets.QLabel('%s:' % _('Slot Tool dia'))
|
||||
self.stdlabel.setToolTip(
|
||||
|
@ -1014,9 +1034,9 @@ class ExcellonObjectUI(ObjectUI):
|
|||
"for milling SLOTS toolpaths.")
|
||||
)
|
||||
|
||||
grid2.addWidget(self.stdlabel, 6, 0)
|
||||
grid2.addWidget(self.slot_tooldia_entry, 6, 1)
|
||||
grid2.addWidget(self.generate_milling_slots_button, 6, 2)
|
||||
grid2.addWidget(self.stdlabel, 5, 0)
|
||||
grid2.addWidget(self.slot_tooldia_entry, 5, 1)
|
||||
grid2.addWidget(self.generate_milling_slots_button, 5, 2)
|
||||
|
||||
def hide_drills(self, state=True):
|
||||
if state is True:
|
||||
|
@ -1152,6 +1172,8 @@ class GeometryObjectUI(ObjectUI):
|
|||
# Tool Offset
|
||||
self.grid1 = QtWidgets.QGridLayout()
|
||||
self.geo_tools_box.addLayout(self.grid1)
|
||||
self.grid1.setColumnStretch(0, 0)
|
||||
self.grid1.setColumnStretch(1, 1)
|
||||
|
||||
self.tool_offset_lbl = QtWidgets.QLabel('%s:' % _('Tool Offset'))
|
||||
self.tool_offset_lbl.setToolTip(
|
||||
|
@ -1162,70 +1184,57 @@ class GeometryObjectUI(ObjectUI):
|
|||
"cut and negative for 'inside' cut."
|
||||
)
|
||||
)
|
||||
self.grid1.addWidget(self.tool_offset_lbl, 0, 0)
|
||||
self.tool_offset_entry = FCDoubleSpinner()
|
||||
self.tool_offset_entry.set_precision(self.decimals)
|
||||
self.tool_offset_entry.setRange(-9999.9999, 9999.9999)
|
||||
self.tool_offset_entry.setSingleStep(0.1)
|
||||
|
||||
spacer_lbl = QtWidgets.QLabel(" ")
|
||||
spacer_lbl.setMinimumWidth(80)
|
||||
self.grid1.addWidget(self.tool_offset_lbl, 0, 0)
|
||||
self.grid1.addWidget(self.tool_offset_entry, 0, 1, 1, 2)
|
||||
|
||||
self.grid1.addWidget(self.tool_offset_entry, 0, 1)
|
||||
self.grid1.addWidget(spacer_lbl, 0, 2)
|
||||
|
||||
# ### Add a new Tool ####
|
||||
hlay = QtWidgets.QHBoxLayout()
|
||||
self.geo_tools_box.addLayout(hlay)
|
||||
|
||||
# self.addtool_label = QtWidgets.QLabel('<b>Tool</b>')
|
||||
# self.addtool_label.setToolTip(
|
||||
# "Add/Copy/Delete a tool to the tool list."
|
||||
# )
|
||||
self.addtool_entry_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('Tool Dia'))
|
||||
self.addtool_entry_lbl.setToolTip(
|
||||
_(
|
||||
"Diameter for the new tool"
|
||||
)
|
||||
_("Diameter for the new tool")
|
||||
)
|
||||
self.addtool_entry = FCDoubleSpinner()
|
||||
self.addtool_entry.set_precision(self.decimals)
|
||||
self.addtool_entry.setRange(0.00001, 9999.9999)
|
||||
self.addtool_entry.setSingleStep(0.1)
|
||||
|
||||
hlay.addWidget(self.addtool_entry_lbl)
|
||||
hlay.addWidget(self.addtool_entry)
|
||||
self.addtool_btn = QtWidgets.QPushButton(_('Add'))
|
||||
self.addtool_btn.setToolTip(
|
||||
_("Add a new tool to the Tool Table\n"
|
||||
"with the specified diameter.")
|
||||
)
|
||||
|
||||
self.grid1.addWidget(self.addtool_entry_lbl, 1, 0)
|
||||
self.grid1.addWidget(self.addtool_entry, 1, 1)
|
||||
self.grid1.addWidget(self.addtool_btn, 1, 2)
|
||||
|
||||
self.addtool_from_db_btn = QtWidgets.QPushButton(_('Add Tool from DataBase'))
|
||||
self.addtool_from_db_btn.setToolTip(
|
||||
_("Add a new tool to the Tool Table\n"
|
||||
"from the Tool DataBase.")
|
||||
)
|
||||
self.grid1.addWidget(self.addtool_from_db_btn, 2, 0, 1, 3)
|
||||
|
||||
grid2 = QtWidgets.QGridLayout()
|
||||
self.geo_tools_box.addLayout(grid2)
|
||||
|
||||
self.addtool_btn = QtWidgets.QPushButton(_('Add'))
|
||||
self.addtool_btn.setToolTip(
|
||||
_(
|
||||
"Add a new tool to the Tool Table\n"
|
||||
"with the diameter specified above."
|
||||
)
|
||||
)
|
||||
|
||||
self.copytool_btn = QtWidgets.QPushButton(_('Copy'))
|
||||
self.copytool_btn.setToolTip(
|
||||
_(
|
||||
"Copy a selection of tools in the Tool Table\n"
|
||||
"by first selecting a row in the Tool Table."
|
||||
)
|
||||
_("Copy a selection of tools in the Tool Table\n"
|
||||
"by first selecting a row in the Tool Table.")
|
||||
)
|
||||
|
||||
self.deltool_btn = QtWidgets.QPushButton(_('Delete'))
|
||||
self.deltool_btn.setToolTip(
|
||||
_(
|
||||
"Delete a selection of tools in the Tool Table\n"
|
||||
"by first selecting a row in the Tool Table."
|
||||
)
|
||||
_("Delete a selection of tools in the Tool Table\n"
|
||||
"by first selecting a row in the Tool Table.")
|
||||
)
|
||||
|
||||
grid2.addWidget(self.addtool_btn, 0, 0)
|
||||
grid2.addWidget(self.copytool_btn, 0, 1)
|
||||
grid2.addWidget(self.deltool_btn, 0, 2)
|
||||
grid2.addWidget(self.copytool_btn, 0, 0)
|
||||
grid2.addWidget(self.deltool_btn, 0, 1)
|
||||
|
||||
self.empty_label = QtWidgets.QLabel('')
|
||||
self.geo_tools_box.addWidget(self.empty_label)
|
||||
|
@ -1295,7 +1304,12 @@ class GeometryObjectUI(ObjectUI):
|
|||
)
|
||||
self.cutz_entry = FCDoubleSpinner()
|
||||
self.cutz_entry.set_precision(self.decimals)
|
||||
self.cutz_entry.setRange(-9999.9999, -0.00001)
|
||||
|
||||
if machinist_setting == 0:
|
||||
self.cutz_entry.setRange(-9999.9999, -0.00001)
|
||||
else:
|
||||
self.cutz_entry.setRange(-9999.9999, 9999.9999)
|
||||
|
||||
self.cutz_entry.setSingleStep(0.1)
|
||||
|
||||
self.grid3.addWidget(cutzlabel, 3, 0)
|
||||
|
@ -1335,7 +1349,12 @@ class GeometryObjectUI(ObjectUI):
|
|||
)
|
||||
self.travelz_entry = FCDoubleSpinner()
|
||||
self.travelz_entry.set_precision(self.decimals)
|
||||
self.travelz_entry.setRange(0, 9999.9999)
|
||||
|
||||
if machinist_setting == 0:
|
||||
self.travelz_entry.setRange(0.00001, 9999.9999)
|
||||
else:
|
||||
self.travelz_entry.setRange(-9999.9999, 9999.9999)
|
||||
|
||||
self.travelz_entry.setSingleStep(0.1)
|
||||
|
||||
self.grid3.addWidget(travelzlabel, 5, 0)
|
||||
|
@ -1358,7 +1377,12 @@ class GeometryObjectUI(ObjectUI):
|
|||
)
|
||||
self.toolchangez_entry = FCDoubleSpinner()
|
||||
self.toolchangez_entry.set_precision(self.decimals)
|
||||
self.toolchangez_entry.setRange(0, 9999.9999)
|
||||
|
||||
if machinist_setting == 0:
|
||||
self.toolchangez_entry.setRange(0, 9999.9999)
|
||||
else:
|
||||
self.toolchangez_entry.setRange(-9999.9999, 9999.9999)
|
||||
|
||||
self.toolchangez_entry.setSingleStep(0.1)
|
||||
|
||||
self.grid3.addWidget(self.toolchangeg_cb, 6, 0, 1, 2)
|
||||
|
@ -1385,7 +1409,12 @@ class GeometryObjectUI(ObjectUI):
|
|||
)
|
||||
self.gendz_entry = FCDoubleSpinner()
|
||||
self.gendz_entry.set_precision(self.decimals)
|
||||
self.gendz_entry.setRange(0, 9999.9999)
|
||||
|
||||
if machinist_setting == 0:
|
||||
self.gendz_entry.setRange(0, 9999.9999)
|
||||
else:
|
||||
self.gendz_entry.setRange(-9999.9999, 9999.9999)
|
||||
|
||||
self.gendz_entry.setSingleStep(0.1)
|
||||
|
||||
self.grid3.addWidget(self.endzlabel, 9, 0)
|
||||
|
|
|
@ -120,7 +120,7 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
|
|||
a3p_mm = np.array([(0, 0), (297, 0), (297, 420), (0, 420)])
|
||||
a3l_mm = np.array([(0, 0), (420, 0), (420, 297), (0, 297)])
|
||||
|
||||
if self.fcapp.ui.general_defaults_form.general_app_group.units_radio.get_value().upper() == 'MM':
|
||||
if self.fcapp.defaults['units'].upper() == 'MM':
|
||||
if self.fcapp.defaults['global_workspaceT'] == 'A4P':
|
||||
a = a4p_mm
|
||||
elif self.fcapp.defaults['global_workspaceT'] == 'A4L':
|
||||
|
|
|
@ -18,6 +18,12 @@ fcTranslate.apply_language('strings')
|
|||
if '_' not in builtins.__dict__:
|
||||
_ = gettext.gettext
|
||||
|
||||
settings = QtCore.QSettings("Open Source", "FlatCAM")
|
||||
if settings.contains("machinist"):
|
||||
machinist_setting = settings.value('machinist', type=int)
|
||||
else:
|
||||
machinist_setting = 0
|
||||
|
||||
|
||||
class OptionsGroupUI(QtWidgets.QGroupBox):
|
||||
def __init__(self, title, parent=None):
|
||||
|
@ -1166,6 +1172,7 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
|
|||
|
||||
self.proj_ois = OptionalInputSection(self.save_type_cb, [self.compress_label, self.compress_spinner], True)
|
||||
|
||||
# Bookmarks Limit in the Help Menu
|
||||
self.bm_limit_spinner = FCSpinner()
|
||||
self.bm_limit_label = QtWidgets.QLabel('%s:' % _('Bookmarks limit'))
|
||||
self.bm_limit_label.setToolTip(
|
||||
|
@ -1177,6 +1184,18 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.bm_limit_label, 18, 0)
|
||||
grid0.addWidget(self.bm_limit_spinner, 18, 1)
|
||||
|
||||
# Machinist settings that allow unsafe settings
|
||||
self.machinist_cb = FCCheckBox(_("Allow Machinist Unsafe Settings"))
|
||||
self.machinist_cb.setToolTip(
|
||||
_("If checked, some of the application settings will be allowed\n"
|
||||
"to have values that are usually unsafe to use.\n"
|
||||
"Like Z travel negative values or Z Cut positive values.\n"
|
||||
"It will applied at the next application start.\n"
|
||||
"<<WARNING>>: Don't change this unless you know what you are doing !!!")
|
||||
)
|
||||
|
||||
grid0.addWidget(self.machinist_cb, 19, 0, 1, 2)
|
||||
|
||||
self.layout.addStretch()
|
||||
|
||||
if sys.platform != 'win32':
|
||||
|
@ -2154,7 +2173,12 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI):
|
|||
)
|
||||
grid2.addWidget(cutzlabel, 0, 0)
|
||||
self.cutz_entry = FCDoubleSpinner()
|
||||
self.cutz_entry.set_range(-9999, -0.000001)
|
||||
|
||||
if machinist_setting == 0:
|
||||
self.cutz_entry.set_range(-9999.9999, -0.000001)
|
||||
else:
|
||||
self.cutz_entry.set_range(-9999.9999, 9999.9999)
|
||||
|
||||
self.cutz_entry.setSingleStep(0.1)
|
||||
self.cutz_entry.set_precision(4)
|
||||
grid2.addWidget(self.cutz_entry, 0, 1)
|
||||
|
@ -2168,7 +2192,11 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI):
|
|||
grid2.addWidget(travelzlabel, 1, 0)
|
||||
self.travelz_entry = FCDoubleSpinner()
|
||||
self.travelz_entry.set_precision(4)
|
||||
self.travelz_entry.set_range(0, 999)
|
||||
|
||||
if machinist_setting == 0:
|
||||
self.travelz_entry.set_range(0.0001, 9999.9999)
|
||||
else:
|
||||
self.travelz_entry.set_range(-9999.9999, 9999.9999)
|
||||
|
||||
grid2.addWidget(self.travelz_entry, 1, 1)
|
||||
|
||||
|
@ -2190,7 +2218,11 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI):
|
|||
grid2.addWidget(toolchangezlabel, 3, 0)
|
||||
self.toolchangez_entry = FCDoubleSpinner()
|
||||
self.toolchangez_entry.set_precision(4)
|
||||
self.toolchangez_entry.set_range(0, 999)
|
||||
|
||||
if machinist_setting == 0:
|
||||
self.toolchangez_entry.set_range(0.0001, 9999.9999)
|
||||
else:
|
||||
self.toolchangez_entry.set_range(-9999.9999, 9999.9999)
|
||||
|
||||
grid2.addWidget(self.toolchangez_entry, 3, 1)
|
||||
|
||||
|
@ -2202,7 +2234,11 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI):
|
|||
)
|
||||
self.eendz_entry = FCDoubleSpinner()
|
||||
self.eendz_entry.set_precision(4)
|
||||
self.eendz_entry.set_range(0, 999)
|
||||
|
||||
if machinist_setting == 0:
|
||||
self.eendz_entry.set_range(0.0000, 9999.9999)
|
||||
else:
|
||||
self.eendz_entry.set_range(-9999.9999, 9999.9999)
|
||||
|
||||
grid2.addWidget(endzlabel, 4, 0)
|
||||
grid2.addWidget(self.eendz_entry, 4, 1)
|
||||
|
@ -2975,7 +3011,12 @@ class GeometryOptPrefGroupUI(OptionsGroupUI):
|
|||
"below the copper surface.")
|
||||
)
|
||||
self.cutz_entry = FCDoubleSpinner()
|
||||
self.cutz_entry.set_range(-999.999, -0.000001)
|
||||
|
||||
if machinist_setting == 0:
|
||||
self.cutz_entry.set_range(-9999.9999, -0.000001)
|
||||
else:
|
||||
self.cutz_entry.set_range(-9999.9999, 9999.9999)
|
||||
|
||||
self.cutz_entry.set_precision(4)
|
||||
self.cutz_entry.setSingleStep(0.1)
|
||||
self.cutz_entry.setWrapping(True)
|
||||
|
@ -3023,7 +3064,12 @@ class GeometryOptPrefGroupUI(OptionsGroupUI):
|
|||
"moving without cutting.")
|
||||
)
|
||||
self.travelz_entry = FCDoubleSpinner()
|
||||
self.travelz_entry.set_range(0, 99999)
|
||||
|
||||
if machinist_setting == 0:
|
||||
self.travelz_entry.set_range(0.0001, 9999.9999)
|
||||
else:
|
||||
self.travelz_entry.set_range(-9999.9999, 9999.9999)
|
||||
|
||||
self.travelz_entry.set_precision(4)
|
||||
self.travelz_entry.setSingleStep(0.1)
|
||||
self.travelz_entry.setWrapping(True)
|
||||
|
@ -3052,7 +3098,12 @@ class GeometryOptPrefGroupUI(OptionsGroupUI):
|
|||
)
|
||||
)
|
||||
self.toolchangez_entry = FCDoubleSpinner()
|
||||
self.toolchangez_entry.set_range(0, 99999)
|
||||
|
||||
if machinist_setting == 0:
|
||||
self.toolchangez_entry.set_range(0.000, 9999.9999)
|
||||
else:
|
||||
self.toolchangez_entry.set_range(-9999.9999, 9999.9999)
|
||||
|
||||
self.toolchangez_entry.set_precision(4)
|
||||
self.toolchangez_entry.setSingleStep(0.1)
|
||||
self.toolchangez_entry.setWrapping(True)
|
||||
|
@ -3067,7 +3118,12 @@ class GeometryOptPrefGroupUI(OptionsGroupUI):
|
|||
"the last move at the end of the job.")
|
||||
)
|
||||
self.gendz_entry = FCDoubleSpinner()
|
||||
self.gendz_entry.set_range(0, 99999)
|
||||
|
||||
if machinist_setting == 0:
|
||||
self.gendz_entry.set_range(0.000, 9999.9999)
|
||||
else:
|
||||
self.gendz_entry.set_range(-9999.9999, 9999.9999)
|
||||
|
||||
self.gendz_entry.set_precision(4)
|
||||
self.gendz_entry.setSingleStep(0.1)
|
||||
self.gendz_entry.setWrapping(True)
|
||||
|
@ -3402,18 +3458,15 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(QtWidgets.QLabel(''), 1, 2)
|
||||
|
||||
# Display Annotation
|
||||
self.annotation_label = QtWidgets.QLabel('%s:' % _("Display Annotation"))
|
||||
self.annotation_label.setToolTip(
|
||||
self.annotation_cb = FCCheckBox(_("Display Annotation"))
|
||||
self.annotation_cb.setToolTip(
|
||||
_("This selects if to display text annotation on the plot.\n"
|
||||
"When checked it will display numbers in order for each end\n"
|
||||
"of a travel line."
|
||||
)
|
||||
)
|
||||
self.annotation_cb = FCCheckBox()
|
||||
|
||||
grid0.addWidget(self.annotation_label, 2, 0)
|
||||
grid0.addWidget(self.annotation_cb, 2, 1)
|
||||
grid0.addWidget(QtWidgets.QLabel(''), 2, 2)
|
||||
grid0.addWidget(self.annotation_cb, 2, 0, 1, 3)
|
||||
|
||||
# ###################################################################
|
||||
# Number of circle steps for circular aperture linear approximation #
|
||||
|
@ -3491,6 +3544,15 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI):
|
|||
coords_type_label.hide()
|
||||
self.coords_type_radio.hide()
|
||||
|
||||
# Line Endings
|
||||
self.line_ending_cb = FCCheckBox(_("Force Windows style line-ending"))
|
||||
self.line_ending_cb.setToolTip(
|
||||
_("When checked will force a Windows style line-ending\n"
|
||||
"(\\r\\n) on non-Windows OS's.")
|
||||
)
|
||||
|
||||
grid0.addWidget(self.line_ending_cb, 9, 0, 1, 3)
|
||||
|
||||
self.layout.addStretch()
|
||||
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ class Distance(FlatCAMTool):
|
|||
|
||||
self.app = app
|
||||
self.canvas = self.app.plotcanvas
|
||||
self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower()
|
||||
self.units = self.app.defaults['units'].lower()
|
||||
|
||||
# ## Title
|
||||
title_label = QtWidgets.QLabel("<font size=4><b>%s</b></font><br>" % self.toolName)
|
||||
|
|
|
@ -35,7 +35,7 @@ class DistanceMin(FlatCAMTool):
|
|||
|
||||
self.app = app
|
||||
self.canvas = self.app.plotcanvas
|
||||
self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower()
|
||||
self.units = self.app.defaults['units'].lower()
|
||||
|
||||
# ## Title
|
||||
title_label = QtWidgets.QLabel("<font size=4><b>%s</b></font><br>" % self.toolName)
|
||||
|
|
|
@ -421,7 +421,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
|||
self.ncc_offset_spinner.set_precision(4)
|
||||
self.ncc_offset_spinner.setWrapping(True)
|
||||
|
||||
units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper()
|
||||
units = self.app.defaults['units'].upper()
|
||||
if units == 'MM':
|
||||
self.ncc_offset_spinner.setSingleStep(0.1)
|
||||
else:
|
||||
|
|
|
@ -39,7 +39,7 @@ class ToolOptimal(FlatCAMTool):
|
|||
def __init__(self, app):
|
||||
FlatCAMTool.__init__(self, app)
|
||||
|
||||
self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper()
|
||||
self.units = self.app.defaults['units'].upper()
|
||||
self.decimals = 4
|
||||
|
||||
# ############################################################################
|
||||
|
|
|
@ -1281,21 +1281,14 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
obj.solid_geometry = obj.solid_geometry.buffer(0)
|
||||
|
||||
poly = self.find_polygon(point=inside_pt, geoset=obj.solid_geometry)
|
||||
paint_method = method if method is None else self.paintmethod_combo.get_value()
|
||||
|
||||
paint_method = method if method is not None else self.paintmethod_combo.get_value()
|
||||
|
||||
if margin is not None:
|
||||
paint_margin = margin
|
||||
else:
|
||||
try:
|
||||
paint_margin = float(self.paintmargin_entry.get_value())
|
||||
except ValueError:
|
||||
# try to convert comma to decimal point. if it's still not working error message and return
|
||||
try:
|
||||
paint_margin = float(self.paintmargin_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_("Wrong value format entered, use a number."))
|
||||
return
|
||||
paint_margin = float(self.paintmargin_entry.get_value())
|
||||
|
||||
# determine if to use the progressive plotting
|
||||
if self.app.defaults["tools_paint_plotting"] == 'progressive':
|
||||
prog_plot = True
|
||||
|
@ -1558,21 +1551,12 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
Usage of the different one is related to when this function is called from a TcL command.
|
||||
:return:
|
||||
"""
|
||||
paint_method = method if method is None else self.paintmethod_combo.get_value()
|
||||
paint_method = method if method is not None else self.paintmethod_combo.get_value()
|
||||
|
||||
if margin is not None:
|
||||
paint_margin = margin
|
||||
else:
|
||||
try:
|
||||
paint_margin = float(self.paintmargin_entry.get_value())
|
||||
except ValueError:
|
||||
# try to convert comma to decimal point. if it's still not working error message and return
|
||||
try:
|
||||
paint_margin = float(self.paintmargin_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_("Wrong value format entered, use a number."))
|
||||
return
|
||||
paint_margin = float(self.paintmargin_entry.get_value())
|
||||
|
||||
# determine if to use the progressive plotting
|
||||
if self.app.defaults["tools_paint_plotting"] == 'progressive':
|
||||
|
@ -2035,7 +2019,7 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
Usage of the different one is related to when this function is called from a TcL command.
|
||||
:return:
|
||||
"""
|
||||
paint_method = method if method is None else self.paintmethod_combo.get_value()
|
||||
paint_method = method if method is not None else self.paintmethod_combo.get_value()
|
||||
|
||||
if margin is not None:
|
||||
paint_margin = margin
|
||||
|
|
|
@ -89,7 +89,19 @@ else:
|
|||
|
||||
print("INCLUDE_FILES", include_files)
|
||||
|
||||
|
||||
def getTargetName():
|
||||
my_OS = platform.system()
|
||||
if my_OS == 'Linux':
|
||||
return "FlatCAM"
|
||||
elif my_OS == 'Windows':
|
||||
return "FlatCAM.exe"
|
||||
else:
|
||||
return "FlatCAM.dmg"
|
||||
|
||||
|
||||
# execfile('clean.py')
|
||||
exe = Executable("FlatCAM.py", icon='share/flatcam_icon48.ico', base=base, targetName=getTargetName())
|
||||
|
||||
setup(
|
||||
name="FlatCAM",
|
||||
|
@ -97,5 +109,5 @@ setup(
|
|||
version="8.9",
|
||||
description="FlatCAM: 2D Computer Aided PCB Manufacturing",
|
||||
options=dict(build_exe=buildOptions),
|
||||
executables=[Executable("FlatCAM.py", icon='share/flatcam_icon48.ico', base=base)]
|
||||
executables=[exe]
|
||||
)
|
Binary file not shown.
After Width: | Height: | Size: 888 B |
Loading…
Reference in New Issue