Merged in options_cleanup (pull request #3)

Options cleanup
This commit is contained in:
Marius Stanciu 2019-11-09 18:31:15 +00:00
commit fdad91f04e
22 changed files with 2742 additions and 2063 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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"))

View File

@ -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:

View File

@ -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
View File

@ -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

View File

@ -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:

View File

@ -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:

View File

@ -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

View File

@ -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."))

View File

@ -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>&nbsp;%s</td>
</tr>
<tr height="20">
<td height="20"><strong>CTRL+D</strong></td>
<td>&nbsp;%s</td>
</tr>
<tr height="20">
<td height="20"><strong>CTRL+E</strong></td>
<td>&nbsp;%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

View 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))

View File

@ -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)

View File

@ -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':

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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:

View File

@ -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
# ############################################################################

View File

@ -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

View File

@ -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]
)

BIN
share/database32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 888 B