- some names are updated

- made sure that when a new project is created that the levelling grid shapes are deleted
- some code optimization and code refactoring
This commit is contained in:
Marius Stanciu
2023-11-25 14:40:51 +02:00
parent 4458249ee5
commit 6b4483044b
21 changed files with 275 additions and 264 deletions

View File

@@ -73,7 +73,7 @@ class AppIO(QtCore.QObject):
self.app.new_project_signal.connect(self.on_new_project_house_keeping)
def on_fileopengerber(self, name=None):
def on_file_open_gerber(self, name=None):
"""
File menu callback for opening a Gerber.
@@ -81,7 +81,7 @@ class AppIO(QtCore.QObject):
:return: None
"""
self.log.debug("on_fileopengerber()")
self.log.debug("on_file_open_gerber()")
_filter_ = "Gerber Files (*.gbr *.ger *.gtl *.gbl *.gts *.gbs *.gtp *.gbp *.gto *.gbo *.gm1 *.gml *.gm3 " \
"*.gko *.cmp *.sol *.stc *.sts *.plc *.pls *.crc *.crs *.tsm *.bsm *.ly2 *.ly15 *.dim *.mil *.grb " \
@@ -122,7 +122,7 @@ class AppIO(QtCore.QObject):
if filename != '':
self.worker_task.emit({'fcn': self.open_gerber, 'params': [filename]})
def on_fileopenexcellon(self, name=None):
def on_file_open_excellon(self, name=None):
"""
File menu callback for opening an Excellon file.
@@ -130,7 +130,7 @@ class AppIO(QtCore.QObject):
:return: None
"""
self.log.debug("on_fileopenexcellon()")
self.log.debug("on_file_open_excellon()")
_filter_ = "Excellon Files (*.drl *.txt *.xln *.drd *.tap *.exc *.ncd);;" \
"All Files (*.*)"
@@ -160,7 +160,7 @@ class AppIO(QtCore.QObject):
if filename != '':
self.worker_task.emit({'fcn': self.open_excellon, 'params': [filename]})
def on_fileopengcode(self, name=None):
def on_file_open_gcode(self, name=None):
"""
File menu call back for opening gcode.
@@ -169,7 +169,7 @@ class AppIO(QtCore.QObject):
:return:
"""
self.log.debug("on_fileopengcode()")
self.log.debug("on_file_open_gcode()")
# https://bobcadsupport.com/helpdesk/index.php?/Knowledgebase/Article/View/13/5/known-g-code-file-extensions
_filter_ = "G-Code Files (*.txt *.nc *.ncc *.tap *.gcode *.cnc *.ecs *.fnc *.dnc *.ncg *.gc *.fan *.fgc" \
@@ -203,14 +203,14 @@ class AppIO(QtCore.QObject):
if filename != '':
self.worker_task.emit({'fcn': self.open_gcode, 'params': [filename, None, True]})
def on_file_openproject(self):
def on_file_open_project(self):
"""
File menu callback for opening a project.
:return: None
"""
self.log.debug("on_file_openproject()")
self.log.debug("on_file_open_project()")
_filter_ = "FlatCAM Project (*.FlatPrj);;All Files (*.*)"
try:
@@ -230,14 +230,14 @@ class AppIO(QtCore.QObject):
# thread safe. The new_project()
self.open_project(filename)
def on_fileopenhpgl2(self, name=None):
def on_file_open_hpgl2(self, name=None):
"""
File menu callback for opening a HPGL2.
:param name:
:return: None
"""
self.log.debug("on_fileopenhpgl2()")
self.log.debug("on_file_open_hpgl2()")
_filter_ = "HPGL2 Files (*.plt);;" \
"All Files (*.*)"
@@ -267,14 +267,14 @@ class AppIO(QtCore.QObject):
if filename != '':
self.worker_task.emit({'fcn': self.open_hpgl2, 'params': [filename]})
def on_file_openconfig(self):
def on_file_open_config(self):
"""
File menu callback for opening a config file.
:return: None
"""
self.log.debug("on_file_openconfig()")
self.log.debug("on_file_open_config()")
_filter_ = "FlatCAM Config (*.FlatConfig);;FlatCAM Config (*.json);;All Files (*.*)"
try:
@@ -289,13 +289,13 @@ class AppIO(QtCore.QObject):
else:
self.open_config_file(filename)
def on_file_exportsvg(self):
def on_file_export_svg(self):
"""
Callback for menu item File->Export SVG.
:return: None
"""
self.log.debug("on_file_exportsvg()")
self.log.debug("on_file_export_svg()")
obj = self.app.collection.get_active()
if obj is None:
@@ -344,9 +344,9 @@ class AppIO(QtCore.QObject):
self.app.file_opened.emit("SVG", filename)
self.app.file_saved.emit("SVG", filename)
def on_file_exportpng(self):
def on_file_export_png(self):
self.log.debug("on_file_exportpng()")
self.log.debug("on_file_export_png()")
date = str(datetime.today()).rpartition('.')[0]
date = ''.join(c for c in date if c not in ':-')
@@ -386,13 +386,13 @@ class AppIO(QtCore.QObject):
self.app.file_opened.emit("png", filename)
self.app.file_saved.emit("png", filename)
def on_file_savegerber(self):
def on_file_save_gerber(self):
"""
Callback for menu item in Project context menu.
:return: None
"""
self.log.debug("on_file_savegerber()")
self.log.debug("on_file_save_gerber()")
obj = self.app.collection.get_active()
if obj is None:
@@ -428,13 +428,13 @@ class AppIO(QtCore.QObject):
self.app.file_opened.emit("Gerber", filename)
self.app.file_saved.emit("Gerber", filename)
def on_file_savescript(self):
def on_file_save_script(self):
"""
Callback for menu item in Project context menu.
:return: None
"""
self.log.debug("on_file_savescript()")
self.log.debug("on_file_save_script()")
obj = self.app.collection.get_active()
if obj is None:
@@ -470,13 +470,13 @@ class AppIO(QtCore.QObject):
self.app.file_opened.emit("Script", filename)
self.app.file_saved.emit("Script", filename)
def on_file_savedocument(self):
def on_file_save_document(self):
"""
Callback for menu item in Project context menu.
:return: None
"""
self.log.debug("on_file_savedocument()")
self.log.debug("on_file_save_document()")
obj = self.app.collection.get_active()
if obj is None:
@@ -512,13 +512,13 @@ class AppIO(QtCore.QObject):
self.app.file_opened.emit("Document", filename)
self.app.file_saved.emit("Document", filename)
def on_file_saveexcellon(self):
def on_file_save_excellon(self):
"""
Callback for menu item in project context menu.
:return: None
"""
self.log.debug("on_file_saveexcellon()")
self.log.debug("on_file_save_excellon()")
obj = self.app.collection.get_active()
if obj is None:
@@ -553,13 +553,13 @@ class AppIO(QtCore.QObject):
self.app.file_opened.emit("Excellon", filename)
self.app.file_saved.emit("Excellon", filename)
def on_file_exportexcellon(self):
def on_file_export_excellon(self):
"""
Callback for menu item File->Export->Excellon.
:return: None
"""
self.log.debug("on_file_exportexcellon()")
self.log.debug("on_file_export_excellon()")
obj = self.app.collection.get_active()
if obj is None:
@@ -598,13 +598,13 @@ class AppIO(QtCore.QObject):
self.app.file_opened.emit("Excellon", filename)
self.app.file_saved.emit("Excellon", filename)
def on_file_exportgerber(self):
def on_file_export_gerber(self):
"""
Callback for menu item File->Export->Gerber.
:return: None
"""
self.log.debug("on_file_exportgerber()")
self.log.debug("on_file_export_gerber()")
obj = self.app.collection.get_active()
if obj is None:
@@ -643,13 +643,13 @@ class AppIO(QtCore.QObject):
self.app.file_opened.emit("Gerber", filename)
self.app.file_saved.emit("Gerber", filename)
def on_file_exportdxf(self):
def on_file_export_dxf(self):
"""
Callback for menu item File->Export DXF.
:return: None
"""
self.log.debug("on_file_exportdxf()")
self.log.debug("on_file_export_dxf()")
obj = self.app.collection.get_active()
if obj is None:
@@ -698,14 +698,14 @@ class AppIO(QtCore.QObject):
self.app.file_opened.emit("DXF", filename)
self.app.file_saved.emit("DXF", filename)
def on_file_importsvg(self, type_of_obj):
def on_file_import_svg(self, type_of_obj):
"""
Callback for menu item File->Import SVG.
:param type_of_obj: to import the SVG as Geometry or as Gerber
:type type_of_obj: str
:return: None
"""
self.log.debug("on_file_importsvg()")
self.log.debug("on_file_import_svg()")
_filter_ = "SVG File .svg (*.svg);;All Files (*.*)"
try:
@@ -728,14 +728,14 @@ class AppIO(QtCore.QObject):
if filename != '':
self.worker_task.emit({'fcn': self.import_svg, 'params': [filename, type_of_obj]})
def on_file_importdxf(self, type_of_obj):
def on_file_import_dxf(self, type_of_obj):
"""
Callback for menu item File->Import DXF.
:param type_of_obj: to import the DXF as Geometry or as Gerber
:type type_of_obj: str
:return: None
"""
self.log.debug("on_file_importdxf()")
self.log.debug("on_file_import_dxf()")
_filter_ = "DXF File .dxf (*.DXF);;All Files (*.*)"
try:
@@ -788,15 +788,15 @@ class AppIO(QtCore.QObject):
response = msgbox.clickedButton()
if response == bt_yes:
self.on_file_saveprojectas(use_thread=True)
self.on_file_save_project_as(use_thread=True)
elif response == bt_cancel:
return
elif response == bt_no:
self.on_file_new_project(use_thread=True, silenced=True)
self.on_file_new_project(use_thread=True)
else:
self.on_file_new_project(use_thread=True, silenced=True)
self.on_file_new_project(use_thread=True)
def on_file_new_project(self, cli=None, reset_tcl=True, use_thread=None, silenced=None, keep_scripts=True):
def on_file_new_project(self, cli=None, reset_tcl=True, use_thread=None, keep_scripts=True):
"""
Returns the application to its startup state. This method is thread-safe.
@@ -804,7 +804,6 @@ class AppIO(QtCore.QObject):
:param reset_tcl: Boolean. If False, on new project creation the Tcl instance is not recreated, therefore it
will remember all the previous variables. If True then the Tcl is re-instantiated.
:param use_thread: Bool. If True some part of the initialization are done threaded
:param silenced: Bool or None. If True then the app will not ask to save the current parameters.
:param keep_scripts: Bool. If True the Script objects are not deleted when creating a new project
:return: None
"""
@@ -841,12 +840,30 @@ class AppIO(QtCore.QObject):
except AttributeError:
pass
# clear the possible drawn probing shapes for Levelling Tool
try:
self.app.levelling_tool.probing_shapes.clear(update=True)
except AttributeError:
pass
# clean possible tool shapes for Isolation, NCC, Paint, Punch Gerber Plugins
try:
self.app.tool_shapes.clear(update=True)
except AttributeError:
pass
# delete the exclusion areas
self.app.exc_areas.clear_shapes()
# delete any selection shape on canvas
self.app.delete_selection_shape()
# delete any hover shapes on canvas
try:
self.app.hover_shapes.clear(update=True)
except AttributeError:
pass
# delete all App objects
if keep_scripts is True:
for prj_obj in self.app.collection.get_list():
@@ -893,7 +910,7 @@ class AppIO(QtCore.QObject):
if cli is None:
# we need to go in reverse because once we remove a tab then the index changes
# meaning that removing the first tab (idx = 0) then the tab at former idx = 1 will assume idx = 0
# and so on. Therefore the deletion should be done in reverse
# and so on. Therefore, the deletion should be done in reverse
wdg_count = self.app.ui.plot_tab_area.tabBar.count() - 1
for index in range(wdg_count, -1, -1):
try:
@@ -931,14 +948,14 @@ class AppIO(QtCore.QObject):
self.app.init_tools(init_tcl=True)
self.log.debug('%s: %s %s.' % ("Initiated the MP pool and plugins in: ", str(time.time() - t0), _("seconds")))
def on_filenewscript(self, silent=False):
def on_file_new_script(self, silent=False):
"""
Will create a new script file and open it in the Code Editor
:param silent: if True will not display status messages
:return: None
"""
self.log.debug("on_filenewscript()")
self.log.debug("on_file_new_script()")
if silent is False:
self.inform.emit('[success] %s' % _("New TCL script file created in Code Editor."))
@@ -949,7 +966,7 @@ class AppIO(QtCore.QObject):
self.app.app_obj.new_script_object()
def on_fileopenscript(self, name=None, silent=False):
def on_file_open_script(self, name=None, silent=False):
"""
Will open a Tcl script file into the Code Editor
@@ -958,7 +975,7 @@ class AppIO(QtCore.QObject):
:return: None
"""
self.log.debug("on_fileopenscript()")
self.log.debug("on_file_open_script()")
_filter_ = "TCL script .FlatScript (*.FlatScript);;TCL script .tcl (*.TCL);;TCL script .txt (*.TXT);;" \
"All Files (*.*)"
@@ -980,7 +997,7 @@ class AppIO(QtCore.QObject):
if filename != '':
self.worker_task.emit({'fcn': self.open_script, 'params': [filename]})
def on_fileopenscript_example(self, name=None, silent=False):
def on_file_open_script_example(self, name=None, silent=False):
"""
Will open a Tcl script file into the Code Editor
@@ -989,7 +1006,7 @@ class AppIO(QtCore.QObject):
:return:
"""
self.log.debug("on_fileopenscript_example()")
self.log.debug("on_file_open_script_example()")
_filter_ = "TCL script .FlatScript (*.FlatScript);;TCL script .tcl (*.TCL);;TCL script .txt (*.TXT);;" \
"All Files (*.*)"
@@ -1017,7 +1034,7 @@ class AppIO(QtCore.QObject):
if filename != '':
self.worker_task.emit({'fcn': self.open_script, 'params': [filename]})
def on_filerunscript(self, name=None, silent=False):
def on_file_run_cript(self, name=None, silent=False):
"""
File menu callback for loading and running a TCL script.
@@ -1062,31 +1079,31 @@ class AppIO(QtCore.QObject):
try:
with open(filename, "r") as tcl_script:
cmd_line_shellfile_content = tcl_script.read()
cmd_line_shell_file_content = tcl_script.read()
if self.app.cmd_line_headless != 1:
self.app.shell.exec_command(cmd_line_shellfile_content)
self.app.shell.exec_command(cmd_line_shell_file_content)
else:
self.app.shell.exec_command(cmd_line_shellfile_content, no_echo=True)
self.app.shell.exec_command(cmd_line_shell_file_content, no_echo=True)
if silent is False:
self.inform.emit('[success] %s' % _("TCL script file opened in Code Editor and executed."))
except Exception as e:
self.app.error("App.on_filerunscript() -> %s" % str(e))
self.app.error("App.on_file_run_cript() -> %s" % str(e))
sys.exit(2)
def on_file_saveproject(self, silent=False):
def on_file_save_project(self, silent=False):
"""
Callback for menu item File->Save Project. Saves the project to
``self.project_filename`` or calls ``self.on_file_saveprojectas()``
``self.project_filename`` or calls ``self.on_file_save_project_as()``
if set to None. The project is saved by calling ``self.save_project()``.
:param silent: if True will not display status messages
:return: None
"""
self.log.debug("on_file_saveproject()")
self.log.debug("on_file_save_project()")
if self.app.project_filename is None:
self.on_file_saveprojectas()
self.on_file_save_project_as()
else:
self.worker_task.emit({'fcn': self.save_project, 'params': [self.app.project_filename, silent]})
if self.options["global_open_style"] is False:
@@ -1097,18 +1114,15 @@ class AppIO(QtCore.QObject):
self.app.should_we_save = False
def on_file_saveprojectas(self, make_copy=False, use_thread=True, quit_action=False):
def on_file_save_project_as(self, make_copy=False, use_thread=True, quit_action=False):
"""
Callback for menu item File->Save Project As... Opens a file
chooser and saves the project to the given file via
``self.save_project()``.
Save the project to a given file by opening a file chooser via self.save_project().
:param make_copy if to be create a copy of the project; boolean
:param use_thread: if to be run in a separate thread; boolean
:param quit_action: if to be followed by quiting the application; boolean
:return: None
"""
self.log.debug("on_file_saveprojectas()")
:param make_copy: boolean, whether to make a copy of the project
:param use_thread: boolean, whether to run in a separate thread
:param quit_action: boolean, whether to quit the application after
:return: None """
self.log.debug("on_file_save_project_as()")
date = str(datetime.today()).rpartition('.')[0]
date = ''.join(c for c in date if c not in ':-')
@@ -1264,7 +1278,7 @@ class AppIO(QtCore.QObject):
}
)
# make sure that the Excellon objeacts are drawn on top of everything
# make sure that the Excellon objects are drawn on top of everything
excellon_objs = [obj for obj in obj_selection if obj.kind == 'excellon']
cncjob_objs = [obj for obj in obj_selection if obj.kind == 'cncjob']
# reverse the object order such that the first selected is on top
@@ -1308,11 +1322,11 @@ class AppIO(QtCore.QObject):
for obj in obj_selection:
try:
gxmin, gymin, gxmax, gymax = obj.bounds()
xmin = min([xmin, gxmin])
ymin = min([ymin, gymin])
xmax = max([xmax, gxmax])
ymax = max([ymax, gymax])
g_xmin, g_ymin, g_xmax, g_ymax = obj.bounds()
xmin = min([xmin, g_xmin])
ymin = min([ymin, g_ymin])
xmax = max([xmax, g_xmax])
ymax = max([ymax, g_ymax])
except Exception as e:
self.log.error("Tried to get bounds of empty geometry in App.save_pdf(). %s" % str(e))
@@ -1320,7 +1334,7 @@ class AppIO(QtCore.QObject):
bounds = [xmin, ymin, xmax, ymax]
size = bounds[2] - bounds[0], bounds[3] - bounds[1]
# This contain the measure units
# This contains the measure units
uom = obj_selection[0].units.lower()
# Define a boundary around SVG of about 1.0mm (~39mils)
@@ -1335,7 +1349,7 @@ class AppIO(QtCore.QObject):
minx = str(bounds[0] - boundary)
miny = str(bounds[1] + boundary + size[1])
# Add a SVG Header and footer to the svg output from shapely
# Add an SVG Header and footer to the svg output from shapely
# The transform flips the Y Axis so that everything renders
# properly within svg apps such as inkscape
svg_header = '<svg xmlns="http://www.w3.org/2000/svg" ' \
@@ -1412,32 +1426,32 @@ class AppIO(QtCore.QObject):
size = obj.size()
# Convert everything to strings for use in the xml doc
svgwidth = str(size[0])
svgheight = str(size[1])
svg_width = str(size[0])
svg_height = str(size[1])
minx = str(bounds[0])
miny = str(bounds[1] - size[1])
uom = obj.units.lower()
# Add a SVG Header and footer to the svg output from shapely
# Add an SVG Header and footer to the svg output from shapely
# The transform flips the Y Axis so that everything renders
# properly within svg apps such as inkscape
svg_header = '<svg xmlns="http://www.w3.org/2000/svg" ' \
'version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" '
svg_header += 'width="' + svgwidth + uom + '" '
svg_header += 'height="' + svgheight + uom + '" '
svg_header += 'viewBox="' + minx + ' ' + miny + ' ' + svgwidth + ' ' + svgheight + '">'
svg_header += 'width="' + svg_width + uom + '" '
svg_header += 'height="' + svg_height + uom + '" '
svg_header += 'viewBox="' + minx + ' ' + miny + ' ' + svg_width + ' ' + svg_height + '">'
svg_header += '<g transform="scale(1,-1)">'
svg_footer = '</g> </svg>'
svg_elem = svg_header + exported_svg + svg_footer
# Parse the xml through a xml parser just to add line feeds
# and to make it look more pretty for the output
svgcode = parse_xml_string(svg_elem)
svgcode = svgcode.toprettyxml()
svg_code = parse_xml_string(svg_elem)
svg_code = svg_code.toprettyxml()
try:
with open(filename, 'w') as fp:
fp.write(svgcode)
fp.write(svg_code)
except PermissionError:
self.inform.emit('[WARNING] %s' %
_("Permission denied, saving not possible.\n"
@@ -1526,7 +1540,7 @@ class AppIO(QtCore.QObject):
def export_excellon(self, obj_name, filename, local_use=None, use_thread=True):
"""
Exports a Excellon Object to an Excellon file.
Exports an Excellon Object to an Excellon file.
:param obj_name: the name of the FlatCAM object to be saved as Excellon
:param filename: Path to the Excellon file to save to.
@@ -1561,18 +1575,18 @@ class AppIO(QtCore.QObject):
return
# updated units
eunits = self.options["excellon_exp_units"]
ewhole = self.options["excellon_exp_integer"]
efract = self.options["excellon_exp_decimals"]
ezeros = self.options["excellon_exp_zeros"]
eformat = self.options["excellon_exp_format"]
e_units = self.options["excellon_exp_units"]
e_whole = self.options["excellon_exp_integer"]
e_fract = self.options["excellon_exp_decimals"]
e_zeros = self.options["excellon_exp_zeros"]
e_format = self.options["excellon_exp_format"]
slot_type = self.options["excellon_exp_slot_type"]
fc_units = self.app_units.upper()
if fc_units == 'MM':
factor = 1 if eunits == 'METRIC' else 0.03937
factor = 1 if e_units == 'METRIC' else 0.03937
else:
factor = 25.4 if eunits == 'METRIC' else 1
factor = 25.4 if e_units == 'METRIC' else 1
def make_excellon():
try:
@@ -1585,12 +1599,12 @@ class AppIO(QtCore.QObject):
header += ';Filename: %s' % str(obj_name) + '\n'
header += ';Created on : %s' % time_str + '\n'
if eformat == 'dec':
has_slots, excellon_code = obj.export_excellon(ewhole, efract, factor=factor, slot_type=slot_type)
header += eunits + '\n'
if e_format == 'dec':
has_slots, excellon_code = obj.export_excellon(e_whole, e_fract, factor=factor, slot_type=slot_type)
header += e_units + '\n'
for tool in obj.tools:
if eunits == 'METRIC':
if e_units == 'METRIC':
header += "T{tool}F00S00C{:.{dec}f}\n".format(float(obj.tools[tool]['tooldia']) * factor,
tool=str(tool),
dec=2)
@@ -1599,15 +1613,15 @@ class AppIO(QtCore.QObject):
tool=str(tool),
dec=4)
else:
if ezeros == 'LZ':
has_slots, excellon_code = obj.export_excellon(ewhole, efract,
if e_zeros == 'LZ':
has_slots, excellon_code = obj.export_excellon(e_whole, e_fract,
form='ndec', e_zeros='LZ', factor=factor,
slot_type=slot_type)
header += '%s,%s\n' % (eunits, 'LZ')
header += '%s,%s\n' % (e_units, 'LZ')
header += format_exc
for tool in obj.tools:
if eunits == 'METRIC':
if e_units == 'METRIC':
header += "T{tool}F00S00C{:.{dec}f}\n".format(
float(obj.tools[tool]['tooldia']) * factor,
tool=str(tool),
@@ -1618,14 +1632,14 @@ class AppIO(QtCore.QObject):
tool=str(tool),
dec=4)
else:
has_slots, excellon_code = obj.export_excellon(ewhole, efract,
has_slots, excellon_code = obj.export_excellon(e_whole, e_fract,
form='ndec', e_zeros='TZ', factor=factor,
slot_type=slot_type)
header += '%s,%s\n' % (eunits, 'TZ')
header += '%s,%s\n' % (e_units, 'TZ')
header += format_exc
for tool in obj.tools:
if eunits == 'METRIC':
if e_units == 'METRIC':
header += "T{tool}F00S00C{:.{dec}f}\n".format(
float(obj.tools[tool]['tooldia']) * factor,
tool=str(tool),
@@ -1674,16 +1688,16 @@ class AppIO(QtCore.QObject):
self.worker_task.emit({'fcn': job_thread_exc, 'params': [self]})
else:
eret = make_excellon()
if eret == 'fail':
ret_val = make_excellon()
if ret_val == 'fail':
self.inform.emit('[ERROR_NOTCL] %s' % _('Could not export.'))
return 'fail'
if local_use is not None:
return eret
return ret_val
def export_gerber(self, obj_name, filename, local_use=None, use_thread=True):
"""
Exports a Gerber Object to an Gerber file.
Exports a Gerber Object to a Gerber file.
:param obj_name: the name of the FlatCAM object to be saved as Gerber
:param filename: Path to the Gerber file to save to.
@@ -1708,16 +1722,16 @@ class AppIO(QtCore.QObject):
obj = local_use
# updated units
gunits = self.options["gerber_exp_units"]
gwhole = self.options["gerber_exp_integer"]
gfract = self.options["gerber_exp_decimals"]
gzeros = self.options["gerber_exp_zeros"]
g_units = self.options["gerber_exp_units"]
g_whole = self.options["gerber_exp_integer"]
g_fract = self.options["gerber_exp_decimals"]
g_zeros = self.options["gerber_exp_zeros"]
fc_units = self.app_units.upper()
if fc_units == 'MM':
factor = 1 if gunits == 'MM' else 0.03937
factor = 1 if g_units == 'MM' else 0.03937
else:
factor = 25.4 if gunits == 'MM' else 1
factor = 25.4 if g_units == 'MM' else 1
def make_gerber():
try:
@@ -1729,8 +1743,8 @@ class AppIO(QtCore.QObject):
header += 'G04 Filename: %s*' % str(obj_name) + '\n'
header += 'G04 Created on : %s*' % time_str + '\n'
header += '%%FS%sAX%s%sY%s%s*%%\n' % (gzeros, gwhole, gfract, gwhole, gfract)
header += "%MO{units}*%\n".format(units=gunits)
header += '%%FS%sAX%s%sY%s%s*%%\n' % (g_zeros, g_whole, g_fract, g_whole, g_fract)
header += "%MO{units}*%\n".format(units=g_units)
for apid in obj.tools:
if obj.tools[apid]['type'] == 'C':
@@ -1757,7 +1771,7 @@ class AppIO(QtCore.QObject):
header += '\n'
# obsolete units but some software may need it
if gunits == 'IN':
if g_units == 'IN':
header += 'G70*\n'
else:
header += 'G71*\n'
@@ -1771,7 +1785,7 @@ class AppIO(QtCore.QObject):
footer = 'M02*\n'
gerber_code = obj.export_gerber(gwhole, gfract, g_zeros=gzeros, factor=factor)
gerber_code = obj.export_gerber(g_whole, g_fract, g_zeros=g_zeros, factor=factor)
exported_gerber = header
exported_gerber += gerber_code
@@ -2154,7 +2168,7 @@ class AppIO(QtCore.QObject):
:param filename: G-code file filename
:param outname: Name of the resulting object. None causes the name to be that of the file.
:param force_parsing:
:param plot: If True plot the object on canvas
:param plot: If True, then plot the object on canvas
:param from_tcl: True if run from Tcl Shell
:return: None
"""
@@ -2406,7 +2420,7 @@ class AppIO(QtCore.QObject):
Loads a config file from the specified file.
:param filename: Name of the file from which to load.
:param run_from_arg: if True the FlatConfig file will be open as an command line argument
:param run_from_arg: if True the FlatConfig file will be open as a command line argument
:return: None
"""
self.log.debug("Opening config file: " + filename)
@@ -2460,7 +2474,7 @@ class AppIO(QtCore.QObject):
:param run_from_arg: True if run for arguments
:param plot: If True plot all objects in the project
:param cli: Run from command line
:param from_tcl: True if run from Tcl Sehll
:param from_tcl: True if run from Tcl Shell
:return: None
"""
@@ -2471,7 +2485,7 @@ class AppIO(QtCore.QObject):
self.inform.emit('[ERROR_NOTCL] %s' % _("File no longer available."))
return
# block autosaving while a project is loaded
# block auto-saving while a project is loaded
self.app.block_autosave = True
# for some reason, setting ui_title does not work when this method is called from Tcl Shell
@@ -2616,38 +2630,36 @@ class AppIO(QtCore.QObject):
def worker_task():
with self.app.proc_container.new('%s' % _("Loading...")):
# Re create objects
# Re-create objects
self.log.debug(" **************** Started PROEJCT loading... **************** ")
for obj in proj_dict['objs']:
try:
msg = "Recreating from opened project an %s object: %s" % \
(obj['kind'].capitalize(), obj['obj_options']['name'])
obj_name = obj['obj_options']['name']
except KeyError:
# allowance for older projects
msg = "Recreating from opened project an %s object: %s" % \
(obj['kind'].capitalize(), obj['options']['name'])
self.app.log.debug(msg)
obj_name = obj['options']['name']
self.app.log.debug(
f"Recreating from opened project an {obj['kind'].capitalize()} object: {obj_name}")
def obj_init(new_obj, app_inst):
try:
new_obj.from_dict(obj)
except Exception as erro:
app_inst.log.error('AppIO.open_project() --> ' + str(erro))
except Exception as except_error:
app_inst.log.error('AppIO.open_project() --> ' + str(except_error))
return 'fail'
# make the 'obj_options' dict a LoudDict
new_obj_options = LoudDict()
try:
new_obj_options = LoudDict()
new_obj_options.update(new_obj.obj_options)
new_obj.obj_options = new_obj_options
except AttributeError:
new_obj_options = LoudDict()
new_obj_options.update(new_obj.options)
new_obj.obj_options = new_obj_options
except Exception as erro:
app_inst.log.error('AppIO.open_project() make a LoudDict--> ' + str(erro))
except Exception as except_error:
app_inst.log.error('AppIO.open_project() make a LoudDict--> ' + str(except_error))
return 'fail'
new_obj.obj_options = new_obj_options
# #############################################################################################
# for older projects loading try to convert the 'apertures' or 'cnc_tools' or 'exc_cnc_tools'
# attributes, if found, to 'tools'
@@ -2657,10 +2669,6 @@ class AppIO(QtCore.QObject):
new_obj.tools = obj['apertures']
if 'cnc_tools' in obj and obj['cnc_tools']:
new_obj.tools = obj['cnc_tools']
# new_obj.used_tools = [int(k) for k in new_obj.tools.keys()]
# first_key = list(obj['cnc_tools'].keys())[0]
# used_preprocessor = obj['cnc_tools'][first_key]['data']['ppname_g']
# new_obj.gc_start = new_obj.doformat(self.app.preprocessors[used_preprocessor].start_code)
if 'exc_cnc_tools' in obj and obj['exc_cnc_tools']:
new_obj.tools = obj['exc_cnc_tools']
# add the used_tools (all of them will be used)
@@ -2683,8 +2691,8 @@ class AppIO(QtCore.QObject):
new_obj.tools = {
float(tool): tool_dict for tool, tool_dict in list(new_obj.tools.items())
}
except Exception as erro:
app_inst.log.error('AppIO.open_project() keys to int--> ' + str(erro))
except Exception as other_error_msg:
app_inst.log.error('AppIO.open_project() keys to int--> ' + str(other_error_msg))
return 'fail'
# #############################################################################################
@@ -2697,7 +2705,7 @@ class AppIO(QtCore.QObject):
# #############################################################################################
if new_obj.kind == 'cncjob':
# some attributes are serialized so we need t otake this into consideration in
# some attributes are serialized, so we need to take this into consideration in
# CNCJob.set_ui()
new_obj.is_loaded_from_project = True
@@ -2706,16 +2714,16 @@ class AppIO(QtCore.QObject):
try:
if cli is None:
self.app.ui.set_ui_title(name="{} {}: {}".format(
_("Loading Project ... restoring"), obj['kind'].upper(), obj['obj_options']['name']))
_("Loading Project ... restoring"), obj['kind'].upper(), obj_name))
ret = self.app.app_obj.new_object(obj['kind'], obj['obj_options']['name'], obj_init, plot=plot)
except KeyError:
# allowance for older projects
if cli is None:
self.app.ui.set_ui_title(name="{} {}: {}".format(
_("Loading Project ... restoring"), obj['kind'].upper(), obj['options']['name']))
_("Loading Project ... restoring"), obj['kind'].upper(), obj_name))
try:
ret = self.app.app_obj.new_object(obj['kind'], obj['options']['name'], obj_init, plot=plot)
ret = self.app.app_obj.new_object(obj['kind'], obj_name, obj_init, plot=plot)
except Exception:
continue
if ret == 'fail':
@@ -2726,7 +2734,7 @@ class AppIO(QtCore.QObject):
self.app.should_we_save = False
self.app.file_opened.emit("project", filename)
# restore autosaving after a project was loaded
# restore auto-saving after a project was loaded
self.app.block_autosave = False
# for some reason, setting ui_title does not work when this method is called from Tcl Shell
@@ -2791,8 +2799,8 @@ class AppIO(QtCore.QObject):
out1 = compressor_obj.compress(project_as_json)
out2 = compressor_obj.flush()
project_zipped = b"".join([out1, out2])
except Exception as errrr:
self.log.error("Failed to save compressed file: %s because: %s" % (str(filename), str(errrr)))
except Exception as error_msg:
self.log.error("Failed to save compressed file: %s because: %s" % (str(filename), str(error_msg)))
self.inform.emit('[ERROR_NOTCL] %s' % _("Failed."))
self.app.save_in_progress = False
return
@@ -2877,46 +2885,42 @@ class AppIO(QtCore.QObject):
def save_source_file(self, obj_name, filename):
"""
Exports a FlatCAM Object to an Gerber/Excellon file.
Exports a FlatCAM Object to a Gerber/Excellon file.
:param obj_name: the name of the FlatCAM object for which to save it's embedded source file
:param obj_name: the name of the FlatCAM object for which to save its embedded source file
:param filename: Path to the Gerber file to save to.
:return:
"""
if filename is None:
filename = self.app.options["global_last_save_folder"] if \
self.app.options["global_last_save_folder"] is not None else self.app.options["global_last_folder"]
filename = self.app.options["global_last_save_folder"] or self.app.options["global_last_folder"]
self.log.debug("save_source_file()")
obj = self.app.collection.get_by_name(obj_name)
file_string = StringIO(obj.source_file)
time_string = "{:%A, %d %B %Y at %H:%M}".format(datetime.now())
if file_string.getvalue() == '':
if not obj.source_file:
msg = _("Save cancelled because source file is empty. Try to export the file.")
self.inform.emit('[ERROR_NOTCL] %s' % msg) # noqa
return 'fail'
time_string = "{:%A, %d %B %Y at %H:%M}".format(datetime.now())
try:
with open(filename, 'w') as file:
file.writelines('G04*\n')
file.writelines('G04 %s (RE)GENERATED BY FLATCAM v%s - www.flatcam.org - Version Date: %s*\n' %
(obj.kind.upper(), str(self.app.version), str(self.app.version_date)))
file.writelines('G04 Filename: %s*\n' % str(obj_name))
file.writelines('G04 Created on : %s*\n' % time_string)
for line in file_string:
file.writelines(line)
file.write('G04*\n')
file.write('G04 %s (RE)GENERATED BY FLATCAM v%s - www.flatcam.org - Version Date: %s*\n' %
(obj.kind.upper(), str(self.app.version), str(self.app.version_date)))
file.write('G04 Filename: %s*\n' % str(obj_name))
file.write('G04 Created on : %s*\n' % time_string)
file.write(obj.source_file)
except PermissionError:
self.inform.emit('[WARNING] %s' %
_("Permission denied, saving not possible.\n"
"Most likely another app is holding the file open and not accessible.")) # noqa
return 'fail'
def on_file_savedefaults(self):
def on_file_save_defaults(self):
"""
Callback for menu item File->Save Defaults. Saves application default options
``self.options`` to current_defaults.FlatConfig.