|
@@ -31,6 +31,7 @@ from reportlab.pdfgen import canvas
|
|
|
from reportlab.graphics import renderPM
|
|
from reportlab.graphics import renderPM
|
|
|
from reportlab.lib.units import inch, mm
|
|
from reportlab.lib.units import inch, mm
|
|
|
from reportlab.lib.pagesizes import landscape, portrait
|
|
from reportlab.lib.pagesizes import landscape, portrait
|
|
|
|
|
+from svglib.svglib import svg2rlg
|
|
|
|
|
|
|
|
from contextlib import contextmanager
|
|
from contextlib import contextmanager
|
|
|
import gc
|
|
import gc
|
|
@@ -415,6 +416,13 @@ class App(QtCore.QObject):
|
|
|
"global_serial": 0,
|
|
"global_serial": 0,
|
|
|
"global_stats": dict(),
|
|
"global_stats": dict(),
|
|
|
"global_tabs_detachable": True,
|
|
"global_tabs_detachable": True,
|
|
|
|
|
+ "global_jump_ref": 'abs',
|
|
|
|
|
+ "global_tpdf_tmargin": 15.0,
|
|
|
|
|
+ "global_tpdf_bmargin": 10.0,
|
|
|
|
|
+ "global_tpdf_lmargin": 20.0,
|
|
|
|
|
+ "global_tpdf_rmargin": 20.0,
|
|
|
|
|
+
|
|
|
|
|
+ # General
|
|
|
"global_graphic_engine": '3D',
|
|
"global_graphic_engine": '3D',
|
|
|
"global_app_level": 'b',
|
|
"global_app_level": 'b',
|
|
|
"global_portable": False,
|
|
"global_portable": False,
|
|
@@ -511,6 +519,9 @@ class App(QtCore.QObject):
|
|
|
"gerber_multicolored": False,
|
|
"gerber_multicolored": False,
|
|
|
"gerber_circle_steps": 64,
|
|
"gerber_circle_steps": 64,
|
|
|
"gerber_use_buffer_for_union": True,
|
|
"gerber_use_buffer_for_union": True,
|
|
|
|
|
+ "gerber_clean_apertures": True,
|
|
|
|
|
+ "gerber_extra_buffering": True,
|
|
|
|
|
+
|
|
|
"gerber_def_units": 'IN',
|
|
"gerber_def_units": 'IN',
|
|
|
"gerber_def_zeros": 'L',
|
|
"gerber_def_zeros": 'L',
|
|
|
"gerber_save_filters": "Gerber File (*.gbr);;Gerber File (*.bot);;Gerber File (*.bsm);;"
|
|
"gerber_save_filters": "Gerber File (*.gbr);;Gerber File (*.bot);;Gerber File (*.bsm);;"
|
|
@@ -972,6 +983,10 @@ class App(QtCore.QObject):
|
|
|
|
|
|
|
|
self.current_units = self.defaults['units']
|
|
self.current_units = self.defaults['units']
|
|
|
|
|
|
|
|
|
|
+ # store here the current self.defaults so it can be restored if Preferences changes are cancelled
|
|
|
|
|
+ self.current_defaults = dict()
|
|
|
|
|
+ self.current_defaults.update(self.defaults)
|
|
|
|
|
+
|
|
|
# #############################################################################
|
|
# #############################################################################
|
|
|
# ##################### CREATE MULTIPROCESSING POOL ###########################
|
|
# ##################### CREATE MULTIPROCESSING POOL ###########################
|
|
|
# #############################################################################
|
|
# #############################################################################
|
|
@@ -1079,6 +1094,11 @@ class App(QtCore.QObject):
|
|
|
"global_bookmarks_limit": self.ui.general_defaults_form.general_app_group.bm_limit_spinner,
|
|
"global_bookmarks_limit": self.ui.general_defaults_form.general_app_group.bm_limit_spinner,
|
|
|
"global_machinist_setting": self.ui.general_defaults_form.general_app_group.machinist_cb,
|
|
"global_machinist_setting": self.ui.general_defaults_form.general_app_group.machinist_cb,
|
|
|
|
|
|
|
|
|
|
+ "global_tpdf_tmargin": self.ui.general_defaults_form.general_app_group.tmargin_entry,
|
|
|
|
|
+ "global_tpdf_bmargin": self.ui.general_defaults_form.general_app_group.bmargin_entry,
|
|
|
|
|
+ "global_tpdf_lmargin": self.ui.general_defaults_form.general_app_group.lmargin_entry,
|
|
|
|
|
+ "global_tpdf_rmargin": self.ui.general_defaults_form.general_app_group.rmargin_entry,
|
|
|
|
|
+
|
|
|
# General GUI Preferences
|
|
# General GUI Preferences
|
|
|
"global_gridx": self.ui.general_defaults_form.general_gui_group.gridx_entry,
|
|
"global_gridx": self.ui.general_defaults_form.general_gui_group.gridx_entry,
|
|
|
"global_gridy": self.ui.general_defaults_form.general_gui_group.gridy_entry,
|
|
"global_gridy": self.ui.general_defaults_form.general_gui_group.gridy_entry,
|
|
@@ -1121,6 +1141,8 @@ class App(QtCore.QObject):
|
|
|
"gerber_circle_steps": self.ui.gerber_defaults_form.gerber_gen_group.circle_steps_entry,
|
|
"gerber_circle_steps": self.ui.gerber_defaults_form.gerber_gen_group.circle_steps_entry,
|
|
|
"gerber_def_units": self.ui.gerber_defaults_form.gerber_gen_group.gerber_units_radio,
|
|
"gerber_def_units": self.ui.gerber_defaults_form.gerber_gen_group.gerber_units_radio,
|
|
|
"gerber_def_zeros": self.ui.gerber_defaults_form.gerber_gen_group.gerber_zeros_radio,
|
|
"gerber_def_zeros": self.ui.gerber_defaults_form.gerber_gen_group.gerber_zeros_radio,
|
|
|
|
|
+ "gerber_clean_apertures": self.ui.gerber_defaults_form.gerber_gen_group.gerber_clean_cb,
|
|
|
|
|
+ "gerber_extra_buffering": self.ui.gerber_defaults_form.gerber_gen_group.gerber_extra_buffering,
|
|
|
|
|
|
|
|
# Gerber Options
|
|
# Gerber Options
|
|
|
"gerber_isotooldia": self.ui.gerber_defaults_form.gerber_opt_group.iso_tool_dia_entry,
|
|
"gerber_isotooldia": self.ui.gerber_defaults_form.gerber_opt_group.iso_tool_dia_entry,
|
|
@@ -1673,6 +1695,7 @@ class App(QtCore.QObject):
|
|
|
self.mr = None
|
|
self.mr = None
|
|
|
self.mdc = None
|
|
self.mdc = None
|
|
|
self.mp_zc = None
|
|
self.mp_zc = None
|
|
|
|
|
+ self.kp = None
|
|
|
|
|
|
|
|
# Matplotlib axis
|
|
# Matplotlib axis
|
|
|
self.axes = None
|
|
self.axes = None
|
|
@@ -1791,10 +1814,11 @@ class App(QtCore.QObject):
|
|
|
|
|
|
|
|
self.ui.menufileexportdxf.triggered.connect(self.on_file_exportdxf)
|
|
self.ui.menufileexportdxf.triggered.connect(self.on_file_exportdxf)
|
|
|
|
|
|
|
|
|
|
+ self.ui.menufile_print.triggered.connect(lambda: self.on_file_save_objects_pdf(use_thread=True))
|
|
|
|
|
+
|
|
|
self.ui.menufilesaveproject.triggered.connect(self.on_file_saveproject)
|
|
self.ui.menufilesaveproject.triggered.connect(self.on_file_saveproject)
|
|
|
self.ui.menufilesaveprojectas.triggered.connect(self.on_file_saveprojectas)
|
|
self.ui.menufilesaveprojectas.triggered.connect(self.on_file_saveprojectas)
|
|
|
self.ui.menufilesaveprojectcopy.triggered.connect(lambda: self.on_file_saveprojectas(make_copy=True))
|
|
self.ui.menufilesaveprojectcopy.triggered.connect(lambda: self.on_file_saveprojectas(make_copy=True))
|
|
|
- self.ui.menufilesave_object_pdf.triggered.connect(self.on_file_save_object_pdf)
|
|
|
|
|
self.ui.menufilesavedefaults.triggered.connect(self.on_file_savedefaults)
|
|
self.ui.menufilesavedefaults.triggered.connect(self.on_file_savedefaults)
|
|
|
|
|
|
|
|
self.ui.menufileexportpref.triggered.connect(self.on_export_preferences)
|
|
self.ui.menufileexportpref.triggered.connect(self.on_export_preferences)
|
|
@@ -2931,17 +2955,25 @@ class App(QtCore.QObject):
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
log.debug("App.defaults_read_form() --> %s" % str(e))
|
|
log.debug("App.defaults_read_form() --> %s" % str(e))
|
|
|
|
|
|
|
|
- def defaults_write_form(self, factor=None, fl_units=None):
|
|
|
|
|
|
|
+ def defaults_write_form(self, factor=None, fl_units=None, source_dict=None):
|
|
|
"""
|
|
"""
|
|
|
Will set the values for all the GUI elements in Preferences GUI based on the values found in the
|
|
Will set the values for all the GUI elements in Preferences GUI based on the values found in the
|
|
|
self.defaults dictionary.
|
|
self.defaults dictionary.
|
|
|
|
|
|
|
|
:param factor: will apply a factor to the values that written in the GUI elements
|
|
:param factor: will apply a factor to the values that written in the GUI elements
|
|
|
:param fl_units: current measuring units in FlatCAM: Metric or Inch
|
|
:param fl_units: current measuring units in FlatCAM: Metric or Inch
|
|
|
|
|
+ :param source_dict: the repository of options, usually is the self.defaults
|
|
|
:return: None
|
|
:return: None
|
|
|
"""
|
|
"""
|
|
|
- for option in self.defaults:
|
|
|
|
|
- self.defaults_write_form_field(option, factor=factor, units=fl_units)
|
|
|
|
|
|
|
+
|
|
|
|
|
+ options_storage = self.defaults if source_dict is None else source_dict
|
|
|
|
|
+
|
|
|
|
|
+ for option in options_storage:
|
|
|
|
|
+ if source_dict:
|
|
|
|
|
+ self.defaults_write_form_field(option, factor=factor, units=fl_units, defaults_dict=source_dict)
|
|
|
|
|
+ else:
|
|
|
|
|
+ self.defaults_write_form_field(option, factor=factor, units=fl_units)
|
|
|
|
|
+
|
|
|
# try:
|
|
# try:
|
|
|
# self.defaults_form_fields[option].set_value(self.defaults[option])
|
|
# self.defaults_form_fields[option].set_value(self.defaults[option])
|
|
|
# except KeyError:
|
|
# except KeyError:
|
|
@@ -2949,7 +2981,7 @@ class App(QtCore.QObject):
|
|
|
# # TODO: Rethink this?
|
|
# # TODO: Rethink this?
|
|
|
# pass
|
|
# pass
|
|
|
|
|
|
|
|
- def defaults_write_form_field(self, field, factor=None, units=None):
|
|
|
|
|
|
|
+ def defaults_write_form_field(self, field, factor=None, units=None, defaults_dict=None):
|
|
|
"""
|
|
"""
|
|
|
Basically it is the worker in the self.defaults_write_form()
|
|
Basically it is the worker in the self.defaults_write_form()
|
|
|
|
|
|
|
@@ -2958,21 +2990,23 @@ class App(QtCore.QObject):
|
|
|
:param units: current FLatCAM measuring units
|
|
:param units: current FLatCAM measuring units
|
|
|
:return: None, it updates GUI elements
|
|
:return: None, it updates GUI elements
|
|
|
"""
|
|
"""
|
|
|
|
|
+
|
|
|
|
|
+ def_dict = self.defaults if defaults_dict is None else defaults_dict
|
|
|
try:
|
|
try:
|
|
|
if factor is None:
|
|
if factor is None:
|
|
|
if units is None:
|
|
if units is None:
|
|
|
- self.defaults_form_fields[field].set_value(self.defaults[field])
|
|
|
|
|
|
|
+ self.defaults_form_fields[field].set_value(def_dict[field])
|
|
|
elif units == 'IN' and (field == 'global_gridx' or field == 'global_gridy'):
|
|
elif units == 'IN' and (field == 'global_gridx' or field == 'global_gridy'):
|
|
|
- self.defaults_form_fields[field].set_value(self.defaults[field])
|
|
|
|
|
|
|
+ self.defaults_form_fields[field].set_value(def_dict[field])
|
|
|
elif units == 'MM' and (field == 'global_gridx' or field == 'global_gridy'):
|
|
elif units == 'MM' and (field == 'global_gridx' or field == 'global_gridy'):
|
|
|
- self.defaults_form_fields[field].set_value(self.defaults[field])
|
|
|
|
|
|
|
+ self.defaults_form_fields[field].set_value(def_dict[field])
|
|
|
else:
|
|
else:
|
|
|
if units is None:
|
|
if units is None:
|
|
|
- self.defaults_form_fields[field].set_value(self.defaults[field] * factor)
|
|
|
|
|
|
|
+ self.defaults_form_fields[field].set_value(def_dict[field] * factor)
|
|
|
elif units == 'IN' and (field == 'global_gridx' or field == 'global_gridy'):
|
|
elif units == 'IN' and (field == 'global_gridx' or field == 'global_gridy'):
|
|
|
- self.defaults_form_fields[field].set_value((self.defaults[field] * factor))
|
|
|
|
|
|
|
+ self.defaults_form_fields[field].set_value((def_dict[field] * factor))
|
|
|
elif units == 'MM' and (field == 'global_gridx' or field == 'global_gridy'):
|
|
elif units == 'MM' and (field == 'global_gridx' or field == 'global_gridy'):
|
|
|
- self.defaults_form_fields[field].set_value((self.defaults[field] * factor))
|
|
|
|
|
|
|
+ self.defaults_form_fields[field].set_value((def_dict[field] * factor))
|
|
|
except KeyError:
|
|
except KeyError:
|
|
|
# self.log.debug("defaults_write_form(): No field for: %s" % option)
|
|
# self.log.debug("defaults_write_form(): No field for: %s" % option)
|
|
|
# TODO: Rethink this?
|
|
# TODO: Rethink this?
|
|
@@ -3882,6 +3916,10 @@ class App(QtCore.QObject):
|
|
|
_("Failed to parse defaults file."))
|
|
_("Failed to parse defaults file."))
|
|
|
return
|
|
return
|
|
|
self.defaults.update(defaults_from_file)
|
|
self.defaults.update(defaults_from_file)
|
|
|
|
|
+ # update the dict that is used to restore the values in the defaults form if Cancel is clicked in the
|
|
|
|
|
+ # Preferences window
|
|
|
|
|
+ self.current_defaults.update(defaults_from_file)
|
|
|
|
|
+
|
|
|
self.on_preferences_edited()
|
|
self.on_preferences_edited()
|
|
|
self.inform.emit('[success] %s: %s' %
|
|
self.inform.emit('[success] %s: %s' %
|
|
|
(_("Imported Defaults from"), filename))
|
|
(_("Imported Defaults from"), filename))
|
|
@@ -4161,16 +4199,10 @@ class App(QtCore.QObject):
|
|
|
try:
|
|
try:
|
|
|
return_value = initialize(obj, self)
|
|
return_value = initialize(obj, self)
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
- msg = '[ERROR_NOTCL] %s' % \
|
|
|
|
|
- _("An internal error has ocurred. See shell.\n")
|
|
|
|
|
|
|
+ msg = '[ERROR_NOTCL] %s' % _("An internal error has occurred. See shell.\n")
|
|
|
msg += _("Object ({kind}) failed because: {error} \n\n").format(kind=kind, error=str(e))
|
|
msg += _("Object ({kind}) failed because: {error} \n\n").format(kind=kind, error=str(e))
|
|
|
msg += traceback.format_exc()
|
|
msg += traceback.format_exc()
|
|
|
self.inform.emit(msg)
|
|
self.inform.emit(msg)
|
|
|
-
|
|
|
|
|
- # if str(e) == "Empty Geometry":
|
|
|
|
|
- # self.inform.emit("[ERROR_NOTCL] )
|
|
|
|
|
- # else:
|
|
|
|
|
- # self.inform.emit("[ERROR] Object (%s) failed because: %s" % (kind, str(e)))
|
|
|
|
|
return "fail"
|
|
return "fail"
|
|
|
|
|
|
|
|
t2 = time.time()
|
|
t2 = time.time()
|
|
@@ -5761,14 +5793,15 @@ class App(QtCore.QObject):
|
|
|
"tools_cr_trace_size_val", "tools_cr_c2c_val", "tools_cr_c2o_val", "tools_cr_s2s_val",
|
|
"tools_cr_trace_size_val", "tools_cr_c2c_val", "tools_cr_c2o_val", "tools_cr_s2s_val",
|
|
|
"tools_cr_s2sm_val", "tools_cr_s2o_val", "tools_cr_sm2sm_val", "tools_cr_ri_val",
|
|
"tools_cr_s2sm_val", "tools_cr_s2o_val", "tools_cr_sm2sm_val", "tools_cr_ri_val",
|
|
|
"tools_cr_h2h_val", "tools_cr_dh_val", "tools_fiducials_dia", "tools_fiducials_margin",
|
|
"tools_cr_h2h_val", "tools_cr_dh_val", "tools_fiducials_dia", "tools_fiducials_margin",
|
|
|
- "tools_fiducials_mode", "tools_fiducials_second_pos", "tools_fiducials_type",
|
|
|
|
|
"tools_fiducials_line_thickness",
|
|
"tools_fiducials_line_thickness",
|
|
|
"tools_copper_thieving_clearance", "tools_copper_thieving_margin",
|
|
"tools_copper_thieving_clearance", "tools_copper_thieving_margin",
|
|
|
"tools_copper_thieving_dots_dia", "tools_copper_thieving_dots_spacing",
|
|
"tools_copper_thieving_dots_dia", "tools_copper_thieving_dots_spacing",
|
|
|
"tools_copper_thieving_squares_size", "tools_copper_thieving_squares_spacing",
|
|
"tools_copper_thieving_squares_size", "tools_copper_thieving_squares_spacing",
|
|
|
"tools_copper_thieving_lines_size", "tools_copper_thieving_lines_spacing",
|
|
"tools_copper_thieving_lines_size", "tools_copper_thieving_lines_spacing",
|
|
|
"tools_copper_thieving_rb_margin", "tools_copper_thieving_rb_thickness",
|
|
"tools_copper_thieving_rb_margin", "tools_copper_thieving_rb_thickness",
|
|
|
- 'global_gridx', 'global_gridy', 'global_snap_max', "global_tolerance"]
|
|
|
|
|
|
|
+
|
|
|
|
|
+ 'global_gridx', 'global_gridy', 'global_snap_max', "global_tolerance",
|
|
|
|
|
+ 'global_tpdf_bmargin', 'global_tpdf_tmargin', 'global_tpdf_rmargin', 'global_tpdf_lmargin']
|
|
|
|
|
|
|
|
def scale_defaults(sfactor):
|
|
def scale_defaults(sfactor):
|
|
|
for dim in dimensions:
|
|
for dim in dimensions:
|
|
@@ -5793,6 +5826,7 @@ class App(QtCore.QObject):
|
|
|
tools_diameters = [eval(a) for a in tools_string if a != '']
|
|
tools_diameters = [eval(a) for a in tools_string if a != '']
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
log.debug("App.on_toggle_units().scale_options() --> %s" % str(e))
|
|
log.debug("App.on_toggle_units().scale_options() --> %s" % str(e))
|
|
|
|
|
+ continue
|
|
|
|
|
|
|
|
self.defaults['geometry_cnctooldia'] = ''
|
|
self.defaults['geometry_cnctooldia'] = ''
|
|
|
for t in range(len(tools_diameters)):
|
|
for t in range(len(tools_diameters)):
|
|
@@ -5805,6 +5839,7 @@ class App(QtCore.QObject):
|
|
|
ncctools = [eval(a) for a in tools_string if a != '']
|
|
ncctools = [eval(a) for a in tools_string if a != '']
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
log.debug("App.on_toggle_units().scale_options() --> %s" % str(e))
|
|
log.debug("App.on_toggle_units().scale_options() --> %s" % str(e))
|
|
|
|
|
+ continue
|
|
|
|
|
|
|
|
self.defaults['tools_ncctools'] = ''
|
|
self.defaults['tools_ncctools'] = ''
|
|
|
for t in range(len(ncctools)):
|
|
for t in range(len(ncctools)):
|
|
@@ -5817,6 +5852,7 @@ class App(QtCore.QObject):
|
|
|
sptools = [eval(a) for a in tools_string if a != '']
|
|
sptools = [eval(a) for a in tools_string if a != '']
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
log.debug("App.on_toggle_units().scale_options() --> %s" % str(e))
|
|
log.debug("App.on_toggle_units().scale_options() --> %s" % str(e))
|
|
|
|
|
+ continue
|
|
|
|
|
|
|
|
self.defaults['tools_solderpaste_tools'] = ""
|
|
self.defaults['tools_solderpaste_tools'] = ""
|
|
|
for t in range(len(sptools)):
|
|
for t in range(len(sptools)):
|
|
@@ -5836,6 +5872,7 @@ class App(QtCore.QObject):
|
|
|
val = float(self.defaults[dim]) * sfactor
|
|
val = float(self.defaults[dim]) * sfactor
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
log.debug('App.on_toggle_units().scale_defaults() --> %s' % str(e))
|
|
log.debug('App.on_toggle_units().scale_defaults() --> %s' % str(e))
|
|
|
|
|
+ continue
|
|
|
|
|
|
|
|
self.defaults[dim] = float('%.*f' % (self.decimals, val))
|
|
self.defaults[dim] = float('%.*f' % (self.decimals, val))
|
|
|
else:
|
|
else:
|
|
@@ -5844,6 +5881,7 @@ class App(QtCore.QObject):
|
|
|
val = float(self.defaults[dim]) * sfactor
|
|
val = float(self.defaults[dim]) * sfactor
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
log.debug('App.on_toggle_units().scale_defaults() --> %s' % str(e))
|
|
log.debug('App.on_toggle_units().scale_defaults() --> %s' % str(e))
|
|
|
|
|
+ continue
|
|
|
|
|
|
|
|
self.defaults[dim] = float('%.*f' % (self.decimals, val))
|
|
self.defaults[dim] = float('%.*f' % (self.decimals, val))
|
|
|
else:
|
|
else:
|
|
@@ -5852,7 +5890,8 @@ class App(QtCore.QObject):
|
|
|
try:
|
|
try:
|
|
|
val = float(self.defaults[dim]) * sfactor
|
|
val = float(self.defaults[dim]) * sfactor
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
- log.debug('App.on_toggle_units().scale_defaults() --> %s' % str(e))
|
|
|
|
|
|
|
+ log.debug('App.on_toggle_units().scale_defaults() --> Value: %s %s' % (str(dim), str(e)))
|
|
|
|
|
+ continue
|
|
|
|
|
|
|
|
self.defaults[dim] = val
|
|
self.defaults[dim] = val
|
|
|
|
|
|
|
@@ -7026,6 +7065,9 @@ class App(QtCore.QObject):
|
|
|
|
|
|
|
|
self.inform.emit('%s' % _("Preferences applied."))
|
|
self.inform.emit('%s' % _("Preferences applied."))
|
|
|
|
|
|
|
|
|
|
+ # make sure we update the self.current_defaults dict used to undo changes to self.defaults
|
|
|
|
|
+ self.current_defaults.update(self.defaults)
|
|
|
|
|
+
|
|
|
if save_to_file:
|
|
if save_to_file:
|
|
|
self.save_defaults(silent=False)
|
|
self.save_defaults(silent=False)
|
|
|
# load the defaults so they are updated into the app
|
|
# load the defaults so they are updated into the app
|
|
@@ -7064,7 +7106,18 @@ class App(QtCore.QObject):
|
|
|
except TypeError:
|
|
except TypeError:
|
|
|
pass
|
|
pass
|
|
|
|
|
|
|
|
- self.defaults_write_form()
|
|
|
|
|
|
|
+ try:
|
|
|
|
|
+ self.ui.general_defaults_form.general_app_group.units_radio.activated_custom.disconnect()
|
|
|
|
|
+ except (TypeError, AttributeError):
|
|
|
|
|
+ pass
|
|
|
|
|
+ self.defaults_write_form(source_dict=self.current_defaults)
|
|
|
|
|
+ self.ui.general_defaults_form.general_app_group.units_radio.activated_custom.connect(
|
|
|
|
|
+ lambda: self.on_toggle_units(no_pref=False))
|
|
|
|
|
+ self.defaults.update(self.current_defaults)
|
|
|
|
|
+
|
|
|
|
|
+ # shared_items = {k: self.defaults[k] for k in self.defaults if k in self.current_defaults and
|
|
|
|
|
+ # self.defaults[k] == self.current_defaults[k]}
|
|
|
|
|
+ # print(len(self.defaults), len(shared_items))
|
|
|
|
|
|
|
|
# Preferences save, update the color of the Preferences Tab text
|
|
# Preferences save, update the color of the Preferences Tab text
|
|
|
for idx in range(self.ui.plot_tab_area.count()):
|
|
for idx in range(self.ui.plot_tab_area.count()):
|
|
@@ -7379,7 +7432,8 @@ class App(QtCore.QObject):
|
|
|
dia_box = DialogBoxRadio(title=_("Jump to ..."),
|
|
dia_box = DialogBoxRadio(title=_("Jump to ..."),
|
|
|
label=_("Enter the coordinates in format X,Y:"),
|
|
label=_("Enter the coordinates in format X,Y:"),
|
|
|
icon=QtGui.QIcon(self.resource_location + '/jump_to16.png'),
|
|
icon=QtGui.QIcon(self.resource_location + '/jump_to16.png'),
|
|
|
- initial_text=dia_box_location)
|
|
|
|
|
|
|
+ initial_text=dia_box_location,
|
|
|
|
|
+ reference=self.defaults['global_jump_ref'])
|
|
|
|
|
|
|
|
if dia_box.ok is True:
|
|
if dia_box.ok is True:
|
|
|
try:
|
|
try:
|
|
@@ -7393,7 +7447,7 @@ class App(QtCore.QObject):
|
|
|
rel_x = self.mouse[0] + location[0]
|
|
rel_x = self.mouse[0] + location[0]
|
|
|
rel_y = self.mouse[1] + location[1]
|
|
rel_y = self.mouse[1] + location[1]
|
|
|
location = (rel_x, rel_y)
|
|
location = (rel_x, rel_y)
|
|
|
-
|
|
|
|
|
|
|
+ self.defaults['global_jump_ref'] = dia_box.reference
|
|
|
except Exception:
|
|
except Exception:
|
|
|
return
|
|
return
|
|
|
else:
|
|
else:
|
|
@@ -7793,8 +7847,7 @@ class App(QtCore.QObject):
|
|
|
pass
|
|
pass
|
|
|
|
|
|
|
|
def on_preferences_edited(self):
|
|
def on_preferences_edited(self):
|
|
|
- self.inform.emit('[WARNING_NOTCL] %s' %
|
|
|
|
|
- _("Preferences edited but not saved."))
|
|
|
|
|
|
|
+ self.inform.emit('[WARNING_NOTCL] %s' % _("Preferences edited but not saved."))
|
|
|
|
|
|
|
|
for idx in range(self.ui.plot_tab_area.count()):
|
|
for idx in range(self.ui.plot_tab_area.count()):
|
|
|
if self.ui.plot_tab_area.tabText(idx) == _("Preferences"):
|
|
if self.ui.plot_tab_area.tabText(idx) == _("Preferences"):
|
|
@@ -9930,7 +9983,7 @@ class App(QtCore.QObject):
|
|
|
|
|
|
|
|
def init_code_editor(self, name):
|
|
def init_code_editor(self, name):
|
|
|
|
|
|
|
|
- self.text_editor_tab = TextEditor(app=self)
|
|
|
|
|
|
|
+ self.text_editor_tab = TextEditor(app=self, plain_text=True)
|
|
|
|
|
|
|
|
# add the tab if it was closed
|
|
# add the tab if it was closed
|
|
|
self.ui.plot_tab_area.addTab(self.text_editor_tab, '%s' % name)
|
|
self.ui.plot_tab_area.addTab(self.text_editor_tab, '%s' % name)
|
|
@@ -10271,19 +10324,26 @@ class App(QtCore.QObject):
|
|
|
self.set_ui_title(name=self.project_filename)
|
|
self.set_ui_title(name=self.project_filename)
|
|
|
self.should_we_save = False
|
|
self.should_we_save = False
|
|
|
|
|
|
|
|
- def on_file_save_object_pdf(self, use_thread=True):
|
|
|
|
|
|
|
+ def on_file_save_objects_pdf(self, use_thread=True):
|
|
|
self.date = str(datetime.today()).rpartition('.')[0]
|
|
self.date = str(datetime.today()).rpartition('.')[0]
|
|
|
self.date = ''.join(c for c in self.date if c not in ':-')
|
|
self.date = ''.join(c for c in self.date if c not in ':-')
|
|
|
self.date = self.date.replace(' ', '_')
|
|
self.date = self.date.replace(' ', '_')
|
|
|
|
|
|
|
|
try:
|
|
try:
|
|
|
- obj_active = self.collection.get_active()
|
|
|
|
|
- obj_name = _(str(obj_active.options['name']))
|
|
|
|
|
|
|
+ obj_selection = self.collection.get_selected()
|
|
|
|
|
+ if len(obj_selection) == 1:
|
|
|
|
|
+ obj_name = str(obj_selection[0].options['name'])
|
|
|
|
|
+ else:
|
|
|
|
|
+ obj_name = _("FlatCAM objects print")
|
|
|
except AttributeError as err:
|
|
except AttributeError as err:
|
|
|
log.debug("App.on_file_save_object_pdf() --> %s" % str(err))
|
|
log.debug("App.on_file_save_object_pdf() --> %s" % str(err))
|
|
|
self.inform.emit('[ERROR_NOTCL] %s' % _("No object selected."))
|
|
self.inform.emit('[ERROR_NOTCL] %s' % _("No object selected."))
|
|
|
return
|
|
return
|
|
|
|
|
|
|
|
|
|
+ if not obj_selection:
|
|
|
|
|
+ self.inform.emit('[ERROR_NOTCL] %s' % _("No object selected."))
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
filter_ = "PDF File (*.PDF);; All Files (*.*)"
|
|
filter_ = "PDF File (*.PDF);; All Files (*.*)"
|
|
|
try:
|
|
try:
|
|
|
filename, _f = QtWidgets.QFileDialog.getSaveFileName(
|
|
filename, _f = QtWidgets.QFileDialog.getSaveFileName(
|
|
@@ -10303,17 +10363,185 @@ class App(QtCore.QObject):
|
|
|
return
|
|
return
|
|
|
|
|
|
|
|
if use_thread is True:
|
|
if use_thread is True:
|
|
|
- self.worker_task.emit({'fcn': self.save_pdf, 'params': [filename, obj_name]})
|
|
|
|
|
|
|
+ proc = self.proc_container.new(_("Printing PDF ... Please wait."))
|
|
|
|
|
+ self.worker_task.emit({'fcn': self.save_pdf, 'params': [filename, obj_selection]})
|
|
|
else:
|
|
else:
|
|
|
- self.save_pdf(filename, obj_name)
|
|
|
|
|
|
|
+ self.save_pdf(filename, obj_selection)
|
|
|
|
|
|
|
|
# self.save_project(filename)
|
|
# self.save_project(filename)
|
|
|
if self.defaults["global_open_style"] is False:
|
|
if self.defaults["global_open_style"] is False:
|
|
|
self.file_opened.emit("pdf", filename)
|
|
self.file_opened.emit("pdf", filename)
|
|
|
self.file_saved.emit("pdf", filename)
|
|
self.file_saved.emit("pdf", filename)
|
|
|
|
|
|
|
|
- def save_pdf(self, file_name, obj_name):
|
|
|
|
|
- self.film_tool.export_positive(obj_name=obj_name, box_name=obj_name, filename=file_name, ftype='pdf')
|
|
|
|
|
|
|
+ def save_pdf(self, file_name, obj_selection):
|
|
|
|
|
+
|
|
|
|
|
+ p_size = self.defaults['global_workspaceT']
|
|
|
|
|
+ orientation = self.defaults['global_workspace_orientation']
|
|
|
|
|
+ color = 'black'
|
|
|
|
|
+ transparency_level = 1.0
|
|
|
|
|
+
|
|
|
|
|
+ self.pagesize = dict()
|
|
|
|
|
+ self.pagesize.update(
|
|
|
|
|
+ {
|
|
|
|
|
+ 'Bounds': None,
|
|
|
|
|
+ 'A0': (841*mm, 1189*mm),
|
|
|
|
|
+ 'A1': (594*mm, 841*mm),
|
|
|
|
|
+ 'A2': (420*mm, 594*mm),
|
|
|
|
|
+ 'A3': (297*mm, 420*mm),
|
|
|
|
|
+ 'A4': (210*mm, 297*mm),
|
|
|
|
|
+ 'A5': (148*mm, 210*mm),
|
|
|
|
|
+ 'A6': (105*mm, 148*mm),
|
|
|
|
|
+ 'A7': (74*mm, 105*mm),
|
|
|
|
|
+ 'A8': (52*mm, 74*mm),
|
|
|
|
|
+ 'A9': (37*mm, 52*mm),
|
|
|
|
|
+ 'A10': (26*mm, 37*mm),
|
|
|
|
|
+
|
|
|
|
|
+ 'B0': (1000*mm, 1414*mm),
|
|
|
|
|
+ 'B1': (707*mm, 1000*mm),
|
|
|
|
|
+ 'B2': (500*mm, 707*mm),
|
|
|
|
|
+ 'B3': (353*mm, 500*mm),
|
|
|
|
|
+ 'B4': (250*mm, 353*mm),
|
|
|
|
|
+ 'B5': (176*mm, 250*mm),
|
|
|
|
|
+ 'B6': (125*mm, 176*mm),
|
|
|
|
|
+ 'B7': (88*mm, 125*mm),
|
|
|
|
|
+ 'B8': (62*mm, 88*mm),
|
|
|
|
|
+ 'B9': (44*mm, 62*mm),
|
|
|
|
|
+ 'B10': (31*mm, 44*mm),
|
|
|
|
|
+
|
|
|
|
|
+ 'C0': (917*mm, 1297*mm),
|
|
|
|
|
+ 'C1': (648*mm, 917*mm),
|
|
|
|
|
+ 'C2': (458*mm, 648*mm),
|
|
|
|
|
+ 'C3': (324*mm, 458*mm),
|
|
|
|
|
+ 'C4': (229*mm, 324*mm),
|
|
|
|
|
+ 'C5': (162*mm, 229*mm),
|
|
|
|
|
+ 'C6': (114*mm, 162*mm),
|
|
|
|
|
+ 'C7': (81*mm, 114*mm),
|
|
|
|
|
+ 'C8': (57*mm, 81*mm),
|
|
|
|
|
+ 'C9': (40*mm, 57*mm),
|
|
|
|
|
+ 'C10': (28*mm, 40*mm),
|
|
|
|
|
+
|
|
|
|
|
+ # American paper sizes
|
|
|
|
|
+ 'LETTER': (8.5*inch, 11*inch),
|
|
|
|
|
+ 'LEGAL': (8.5*inch, 14*inch),
|
|
|
|
|
+ 'ELEVENSEVENTEEN': (11*inch, 17*inch),
|
|
|
|
|
+
|
|
|
|
|
+ # From https://en.wikipedia.org/wiki/Paper_size
|
|
|
|
|
+ 'JUNIOR_LEGAL': (5*inch, 8*inch),
|
|
|
|
|
+ 'HALF_LETTER': (5.5*inch, 8*inch),
|
|
|
|
|
+ 'GOV_LETTER': (8*inch, 10.5*inch),
|
|
|
|
|
+ 'GOV_LEGAL': (8.5*inch, 13*inch),
|
|
|
|
|
+ 'LEDGER': (17*inch, 11*inch),
|
|
|
|
|
+ }
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ exported_svg = list()
|
|
|
|
|
+ for obj in obj_selection:
|
|
|
|
|
+ svg_obj = obj.export_svg(scale_stroke_factor=0.0,
|
|
|
|
|
+ scale_factor_x=None, scale_factor_y=None,
|
|
|
|
|
+ skew_factor_x=None, skew_factor_y=None,
|
|
|
|
|
+ mirror=None)
|
|
|
|
|
+
|
|
|
|
|
+ if obj.kind.lower() == 'gerber':
|
|
|
|
|
+ color = self.defaults["global_plot_fill"][:-2]
|
|
|
|
|
+ elif obj.kind.lower() == 'excellon':
|
|
|
|
|
+ color = '#C40000'
|
|
|
|
|
+ elif obj.kind.lower() == 'geometry':
|
|
|
|
|
+ color = self.defaults["global_draw_color"]
|
|
|
|
|
+
|
|
|
|
|
+ # Change the attributes of the exported SVG
|
|
|
|
|
+ # We don't need stroke-width
|
|
|
|
|
+ # We set opacity to maximum
|
|
|
|
|
+ # We set the colour to WHITE
|
|
|
|
|
+ root = ET.fromstring(svg_obj)
|
|
|
|
|
+ for child in root:
|
|
|
|
|
+ child.set('fill', str(color))
|
|
|
|
|
+ child.set('opacity', str(transparency_level))
|
|
|
|
|
+ child.set('stroke', str(color))
|
|
|
|
|
+
|
|
|
|
|
+ exported_svg.append(ET.tostring(root))
|
|
|
|
|
+
|
|
|
|
|
+ xmin = Inf
|
|
|
|
|
+ ymin = Inf
|
|
|
|
|
+ xmax = -Inf
|
|
|
|
|
+ ymax = -Inf
|
|
|
|
|
+
|
|
|
|
|
+ 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])
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ log.warning("DEV WARNING: Tried to get bounds of empty geometry in App.save_pdf(). %s" % str(e))
|
|
|
|
|
+
|
|
|
|
|
+ # Determine bounding area for svg export
|
|
|
|
|
+ bounds = [xmin, ymin, xmax, ymax]
|
|
|
|
|
+ size = bounds[2] - bounds[0], bounds[3] - bounds[1]
|
|
|
|
|
+
|
|
|
|
|
+ # This contain the measure units
|
|
|
|
|
+ uom = obj_selection[0].units.lower()
|
|
|
|
|
+
|
|
|
|
|
+ # Define a boundary around SVG of about 1.0mm (~39mils)
|
|
|
|
|
+ if uom in "mm":
|
|
|
|
|
+ boundary = 1.0
|
|
|
|
|
+ else:
|
|
|
|
|
+ boundary = 0.0393701
|
|
|
|
|
+
|
|
|
|
|
+ # Convert everything to strings for use in the xml doc
|
|
|
|
|
+ svgwidth = str(size[0] + (2 * boundary))
|
|
|
|
|
+ svgheight = str(size[1] + (2 * boundary))
|
|
|
|
|
+ minx = str(bounds[0] - boundary)
|
|
|
|
|
+ miny = str(bounds[1] + boundary + size[1])
|
|
|
|
|
+
|
|
|
|
|
+ # Add a 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 += '>'
|
|
|
|
|
+ svg_header += '<g transform="scale(1,-1)">'
|
|
|
|
|
+ svg_footer = '</g> </svg>'
|
|
|
|
|
+
|
|
|
|
|
+ svg_elem = str(svg_header)
|
|
|
|
|
+ for svg_item in exported_svg:
|
|
|
|
|
+ svg_elem += str(svg_item)
|
|
|
|
|
+ svg_elem += str(svg_footer)
|
|
|
|
|
+
|
|
|
|
|
+ # Parse the xml through a xml parser just to add line feeds
|
|
|
|
|
+ # and to make it look more pretty for the output
|
|
|
|
|
+ doc = parse_xml_string(svg_elem)
|
|
|
|
|
+ doc_final = doc.toprettyxml()
|
|
|
|
|
+
|
|
|
|
|
+ try:
|
|
|
|
|
+ if self.defaults['units'].upper() == 'IN':
|
|
|
|
|
+ unit = inch
|
|
|
|
|
+ else:
|
|
|
|
|
+ unit = mm
|
|
|
|
|
+
|
|
|
|
|
+ doc_final = StringIO(doc_final)
|
|
|
|
|
+ drawing = svg2rlg(doc_final)
|
|
|
|
|
+
|
|
|
|
|
+ if p_size == 'Bounds':
|
|
|
|
|
+ renderPDF.drawToFile(drawing, file_name)
|
|
|
|
|
+ else:
|
|
|
|
|
+ if orientation == 'p':
|
|
|
|
|
+ page_size = portrait(self.pagesize[p_size])
|
|
|
|
|
+ else:
|
|
|
|
|
+ page_size = landscape(self.pagesize[p_size])
|
|
|
|
|
+
|
|
|
|
|
+ my_canvas = canvas.Canvas(file_name, pagesize=page_size)
|
|
|
|
|
+ my_canvas.translate(bounds[0] * unit, bounds[1] * unit)
|
|
|
|
|
+ renderPDF.draw(drawing, my_canvas, 0, 0)
|
|
|
|
|
+ my_canvas.save()
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ log.debug("App.save_pdf() --> PDF output --> %s" % str(e))
|
|
|
|
|
+ return 'fail'
|
|
|
|
|
+
|
|
|
|
|
+ self.inform.emit('[success] %s: %s' % (_("PDF file saved to"), file_name))
|
|
|
|
|
|
|
|
def export_svg(self, obj_name, filename, scale_stroke_factor=0.00):
|
|
def export_svg(self, obj_name, filename, scale_stroke_factor=0.00):
|
|
|
"""
|
|
"""
|
|
@@ -11261,6 +11489,7 @@ class App(QtCore.QObject):
|
|
|
|
|
|
|
|
# Project options
|
|
# Project options
|
|
|
self.options.update(d['options'])
|
|
self.options.update(d['options'])
|
|
|
|
|
+
|
|
|
self.project_filename = filename
|
|
self.project_filename = filename
|
|
|
|
|
|
|
|
# for some reason, setting ui_title does not work when this method is called from Tcl Shell
|
|
# for some reason, setting ui_title does not work when this method is called from Tcl Shell
|
|
@@ -11273,6 +11502,7 @@ class App(QtCore.QObject):
|
|
|
|
|
|
|
|
for obj in d['objs']:
|
|
for obj in d['objs']:
|
|
|
def obj_init(obj_inst, app_inst):
|
|
def obj_init(obj_inst, app_inst):
|
|
|
|
|
+
|
|
|
obj_inst.from_dict(obj)
|
|
obj_inst.from_dict(obj)
|
|
|
|
|
|
|
|
App.log.debug("Recreating from opened project an %s object: %s" %
|
|
App.log.debug("Recreating from opened project an %s object: %s" %
|