jpcgt/flatcam/Beta слито с Beta
This commit is contained in:
commit
07b4046d4f
|
@ -47,6 +47,14 @@ if __name__ == '__main__':
|
|||
else:
|
||||
os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "0"
|
||||
|
||||
# if hdpi_support == 2:
|
||||
# tst_screen = QtWidgets.QApplication(sys.argv)
|
||||
# if tst_screen.screens()[0].geometry().width() > 1930 or tst_screen.screens()[1].geometry().width() > 1930:
|
||||
# QGuiApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
|
||||
# del tst_screen
|
||||
# else:
|
||||
# QGuiApplication.setAttribute(Qt.AA_EnableHighDpiScaling, False)
|
||||
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
|
||||
# apply style
|
||||
|
|
298
FlatCAMApp.py
298
FlatCAMApp.py
|
@ -31,6 +31,7 @@ from reportlab.pdfgen import canvas
|
|||
from reportlab.graphics import renderPM
|
||||
from reportlab.lib.units import inch, mm
|
||||
from reportlab.lib.pagesizes import landscape, portrait
|
||||
from svglib.svglib import svg2rlg
|
||||
|
||||
from contextlib import contextmanager
|
||||
import gc
|
||||
|
@ -415,6 +416,13 @@ class App(QtCore.QObject):
|
|||
"global_serial": 0,
|
||||
"global_stats": dict(),
|
||||
"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_app_level": 'b',
|
||||
"global_portable": False,
|
||||
|
@ -511,6 +519,9 @@ class App(QtCore.QObject):
|
|||
"gerber_multicolored": False,
|
||||
"gerber_circle_steps": 64,
|
||||
"gerber_use_buffer_for_union": True,
|
||||
"gerber_clean_apertures": True,
|
||||
"gerber_extra_buffering": True,
|
||||
|
||||
"gerber_def_units": 'IN',
|
||||
"gerber_def_zeros": 'L',
|
||||
"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']
|
||||
|
||||
# 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 ###########################
|
||||
# #############################################################################
|
||||
|
@ -1079,6 +1094,11 @@ class App(QtCore.QObject):
|
|||
"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_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
|
||||
"global_gridx": self.ui.general_defaults_form.general_gui_group.gridx_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_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_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_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.mdc = None
|
||||
self.mp_zc = None
|
||||
self.kp = None
|
||||
|
||||
# Matplotlib axis
|
||||
self.axes = None
|
||||
|
@ -1791,10 +1814,11 @@ class App(QtCore.QObject):
|
|||
|
||||
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.menufilesaveprojectas.triggered.connect(self.on_file_saveprojectas)
|
||||
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.menufileexportpref.triggered.connect(self.on_export_preferences)
|
||||
|
@ -2931,17 +2955,25 @@ class App(QtCore.QObject):
|
|||
except Exception as 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
|
||||
self.defaults dictionary.
|
||||
|
||||
: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 source_dict: the repository of options, usually is the self.defaults
|
||||
: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:
|
||||
# self.defaults_form_fields[option].set_value(self.defaults[option])
|
||||
# except KeyError:
|
||||
|
@ -2949,7 +2981,7 @@ class App(QtCore.QObject):
|
|||
# # TODO: Rethink this?
|
||||
# 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()
|
||||
|
||||
|
@ -2958,21 +2990,23 @@ class App(QtCore.QObject):
|
|||
:param units: current FLatCAM measuring units
|
||||
:return: None, it updates GUI elements
|
||||
"""
|
||||
|
||||
def_dict = self.defaults if defaults_dict is None else defaults_dict
|
||||
try:
|
||||
if factor 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'):
|
||||
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'):
|
||||
self.defaults_form_fields[field].set_value(self.defaults[field])
|
||||
self.defaults_form_fields[field].set_value(def_dict[field])
|
||||
else:
|
||||
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'):
|
||||
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'):
|
||||
self.defaults_form_fields[field].set_value((self.defaults[field] * factor))
|
||||
self.defaults_form_fields[field].set_value((def_dict[field] * factor))
|
||||
except KeyError:
|
||||
# self.log.debug("defaults_write_form(): No field for: %s" % option)
|
||||
# TODO: Rethink this?
|
||||
|
@ -3882,6 +3916,10 @@ class App(QtCore.QObject):
|
|||
_("Failed to parse defaults file."))
|
||||
return
|
||||
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.inform.emit('[success] %s: %s' %
|
||||
(_("Imported Defaults from"), filename))
|
||||
|
@ -4161,16 +4199,10 @@ class App(QtCore.QObject):
|
|||
try:
|
||||
return_value = initialize(obj, self)
|
||||
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 += traceback.format_exc()
|
||||
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"
|
||||
|
||||
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_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_fiducials_mode", "tools_fiducials_second_pos", "tools_fiducials_type",
|
||||
"tools_fiducials_line_thickness",
|
||||
"tools_copper_thieving_clearance", "tools_copper_thieving_margin",
|
||||
"tools_copper_thieving_dots_dia", "tools_copper_thieving_dots_spacing",
|
||||
"tools_copper_thieving_squares_size", "tools_copper_thieving_squares_spacing",
|
||||
"tools_copper_thieving_lines_size", "tools_copper_thieving_lines_spacing",
|
||||
"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):
|
||||
for dim in dimensions:
|
||||
|
@ -5793,6 +5826,7 @@ class App(QtCore.QObject):
|
|||
tools_diameters = [eval(a) for a in tools_string if a != '']
|
||||
except Exception as e:
|
||||
log.debug("App.on_toggle_units().scale_options() --> %s" % str(e))
|
||||
continue
|
||||
|
||||
self.defaults['geometry_cnctooldia'] = ''
|
||||
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 != '']
|
||||
except Exception as e:
|
||||
log.debug("App.on_toggle_units().scale_options() --> %s" % str(e))
|
||||
continue
|
||||
|
||||
self.defaults['tools_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 != '']
|
||||
except Exception as e:
|
||||
log.debug("App.on_toggle_units().scale_options() --> %s" % str(e))
|
||||
continue
|
||||
|
||||
self.defaults['tools_solderpaste_tools'] = ""
|
||||
for t in range(len(sptools)):
|
||||
|
@ -5836,6 +5872,7 @@ class App(QtCore.QObject):
|
|||
val = float(self.defaults[dim]) * sfactor
|
||||
except Exception as e:
|
||||
log.debug('App.on_toggle_units().scale_defaults() --> %s' % str(e))
|
||||
continue
|
||||
|
||||
self.defaults[dim] = float('%.*f' % (self.decimals, val))
|
||||
else:
|
||||
|
@ -5844,6 +5881,7 @@ class App(QtCore.QObject):
|
|||
val = float(self.defaults[dim]) * sfactor
|
||||
except Exception as e:
|
||||
log.debug('App.on_toggle_units().scale_defaults() --> %s' % str(e))
|
||||
continue
|
||||
|
||||
self.defaults[dim] = float('%.*f' % (self.decimals, val))
|
||||
else:
|
||||
|
@ -5852,7 +5890,8 @@ class App(QtCore.QObject):
|
|||
try:
|
||||
val = float(self.defaults[dim]) * sfactor
|
||||
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
|
||||
|
||||
|
@ -7026,6 +7065,9 @@ class App(QtCore.QObject):
|
|||
|
||||
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:
|
||||
self.save_defaults(silent=False)
|
||||
# load the defaults so they are updated into the app
|
||||
|
@ -7064,7 +7106,18 @@ class App(QtCore.QObject):
|
|||
except TypeError:
|
||||
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
|
||||
for idx in range(self.ui.plot_tab_area.count()):
|
||||
|
@ -7379,7 +7432,8 @@ class App(QtCore.QObject):
|
|||
dia_box = DialogBoxRadio(title=_("Jump to ..."),
|
||||
label=_("Enter the coordinates in format X,Y:"),
|
||||
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:
|
||||
try:
|
||||
|
@ -7393,7 +7447,7 @@ class App(QtCore.QObject):
|
|||
rel_x = self.mouse[0] + location[0]
|
||||
rel_y = self.mouse[1] + location[1]
|
||||
location = (rel_x, rel_y)
|
||||
|
||||
self.defaults['global_jump_ref'] = dia_box.reference
|
||||
except Exception:
|
||||
return
|
||||
else:
|
||||
|
@ -7793,8 +7847,7 @@ class App(QtCore.QObject):
|
|||
pass
|
||||
|
||||
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()):
|
||||
if self.ui.plot_tab_area.tabText(idx) == _("Preferences"):
|
||||
|
@ -9930,7 +9983,7 @@ class App(QtCore.QObject):
|
|||
|
||||
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
|
||||
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.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 = ''.join(c for c in self.date if c not in ':-')
|
||||
self.date = self.date.replace(' ', '_')
|
||||
|
||||
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:
|
||||
log.debug("App.on_file_save_object_pdf() --> %s" % str(err))
|
||||
self.inform.emit('[ERROR_NOTCL] %s' % _("No object selected."))
|
||||
return
|
||||
|
||||
if not obj_selection:
|
||||
self.inform.emit('[ERROR_NOTCL] %s' % _("No object selected."))
|
||||
return
|
||||
|
||||
filter_ = "PDF File (*.PDF);; All Files (*.*)"
|
||||
try:
|
||||
filename, _f = QtWidgets.QFileDialog.getSaveFileName(
|
||||
|
@ -10303,17 +10363,185 @@ class App(QtCore.QObject):
|
|||
return
|
||||
|
||||
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:
|
||||
self.save_pdf(filename, obj_name)
|
||||
self.save_pdf(filename, obj_selection)
|
||||
|
||||
# self.save_project(filename)
|
||||
if self.defaults["global_open_style"] is False:
|
||||
self.file_opened.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):
|
||||
"""
|
||||
|
@ -11261,6 +11489,7 @@ class App(QtCore.QObject):
|
|||
|
||||
# Project options
|
||||
self.options.update(d['options'])
|
||||
|
||||
self.project_filename = filename
|
||||
|
||||
# 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']:
|
||||
def obj_init(obj_inst, app_inst):
|
||||
|
||||
obj_inst.from_dict(obj)
|
||||
|
||||
App.log.debug("Recreating from opened project an %s object: %s" %
|
||||
|
|
|
@ -981,6 +981,9 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
|
||||
def geo_init(geo_obj, app_obj):
|
||||
assert isinstance(geo_obj, FlatCAMGeometry)
|
||||
if isinstance(self.solid_geometry, list):
|
||||
self.solid_geometry = cascaded_union(self.solid_geometry)
|
||||
|
||||
bounding_box = self.solid_geometry.envelope.buffer(float(self.options["noncoppermargin"]))
|
||||
if not self.options["noncopperrounded"]:
|
||||
bounding_box = bounding_box.envelope
|
||||
|
@ -5173,8 +5176,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
if self.tools[tooluid_key]['solid_geometry'] is None:
|
||||
a += 1
|
||||
if a == len(self.tools):
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s...' %
|
||||
_('Cancelled. Empty file, it has no geometry'))
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s...' % _('Cancelled. Empty file, it has no geometry'))
|
||||
return 'fail'
|
||||
|
||||
for tooluid_key in list(tools_dict.keys()):
|
||||
|
@ -5292,10 +5294,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
|
||||
if use_thread:
|
||||
# To be run in separate thread
|
||||
# The idea is that if there is a solid_geometry in the file "root" then most likely thare are no
|
||||
# separate solid_geometry in the self.tools dictionary
|
||||
def job_thread(app_obj):
|
||||
if self.solid_geometry:
|
||||
if self.multigeo is False:
|
||||
with self.app.proc_container.new(_("Generating CNC Code")):
|
||||
if app_obj.new_object("cncjob", outname, job_init_single_geometry, plot=plot) != 'fail':
|
||||
app_obj.inform.emit('[success] %s: %s' % (_("CNCjob created"), outname))
|
||||
|
|
26
README.md
26
README.md
|
@ -9,6 +9,31 @@ CAD program, and create G-Code for Isolation routing.
|
|||
|
||||
=================================================
|
||||
|
||||
20.12.2019
|
||||
|
||||
- fixed a rare issue in the generation of non-copper-region geometry started from the Gerber Object UI (selected tab)
|
||||
- Print function is now printing a PDF file for a selection of objects in the colors from canvas
|
||||
|
||||
19.12.2019
|
||||
|
||||
- in 2-Sided Tool added a way to calculate the bounding box values for a selection of objects, and also the centroid
|
||||
- in 2-Sided Tool fixed the Reset Tool button handler to reset the bounds value too; changed a string
|
||||
- added Preferences values for PDF margins when saving text in Code Editor as PDF
|
||||
- when clicking Cancel in Preferences now the values are reverted to what they used to be before opening Preferences tab and start changing values
|
||||
- starting to work to a general Print function; for now it will generate PDF files; currently it works only for one object not for a selection
|
||||
- added shortcut key CTRL+P for printing to PDF method
|
||||
|
||||
18.12.2019
|
||||
|
||||
- added new parameters to improve Gerber parsing
|
||||
- small optimizations in the Preferences UI
|
||||
- the Jump To function reference is now saving it's last used value
|
||||
- added the ability to use the Jump To method in the Gerber Editor
|
||||
- improved the loading of Config File by using the advanced code editor
|
||||
- fixed a bug in the new feature 'extra buffering'
|
||||
- fixed the creation of CNCJob objects out of multigeo Geometry objects (objects with multiple tools)
|
||||
- optimized the NCC Tool
|
||||
|
||||
17.12.2019
|
||||
|
||||
- more optimizations in NCC Tool
|
||||
|
@ -21,6 +46,7 @@ CAD program, and create G-Code for Isolation routing.
|
|||
- added ability to save the Source File as PDF - fixed page size and added line breaks
|
||||
- more mods to generate_from_geometry_2() method
|
||||
- fixed bug saving the FlatCAM project saying the file is used by another application
|
||||
- fixed issue #347 - a Gerber generated by Sprint Layout with copper pour ON will not have rendered the copper pour
|
||||
|
||||
16.12.2019
|
||||
|
||||
|
|
|
@ -1880,7 +1880,6 @@ class DrawTool(object):
|
|||
return ""
|
||||
|
||||
def on_key(self, key):
|
||||
|
||||
# Jump to coords
|
||||
if key == QtCore.Qt.Key_J or key == 'J':
|
||||
self.draw_app.app.on_jump_to()
|
||||
|
|
|
@ -139,7 +139,9 @@ class DrawTool(object):
|
|||
return ""
|
||||
|
||||
def on_key(self, key):
|
||||
return None
|
||||
# Jump to coords
|
||||
if key == QtCore.Qt.Key_J or key == 'J':
|
||||
self.draw_app.app.on_jump_to()
|
||||
|
||||
def utility_geometry(self, data=None):
|
||||
return None
|
||||
|
@ -874,9 +876,11 @@ class FCRegion(FCShapeTool):
|
|||
except Exception as e:
|
||||
log.debug("FlatCAMGrbEditor.FCRegion --> %s" % str(e))
|
||||
|
||||
self.cursor = QtGui.QCursor(QtGui.QPixmap(self.app.resource_location + '/aero.png'))
|
||||
self.cursor = QtGui.QCursor(QtGui.QPixmap(self.draw_app.app.resource_location + '/aero.png'))
|
||||
QtGui.QGuiApplication.setOverrideCursor(self.cursor)
|
||||
|
||||
self.draw_app.app.jump_signal.connect(lambda x: self.draw_app.update_utility_geometry(data=x))
|
||||
|
||||
self.draw_app.app.inform.emit(_('Corner Mode 1: 45 degrees ...'))
|
||||
|
||||
self.start_msg = _("Click on 1st point ...")
|
||||
|
@ -1064,8 +1068,10 @@ class FCRegion(FCShapeTool):
|
|||
self.geometry = DrawToolShape(new_geo_el)
|
||||
self.draw_app.in_action = False
|
||||
self.complete = True
|
||||
self.draw_app.app.inform.emit('[success] %s' %
|
||||
_("Done."))
|
||||
|
||||
self.draw_app.app.jump_signal.disconnect()
|
||||
|
||||
self.draw_app.app.inform.emit('[success] %s' % _("Done."))
|
||||
|
||||
def clean_up(self):
|
||||
self.draw_app.selected = []
|
||||
|
@ -1073,6 +1079,10 @@ class FCRegion(FCShapeTool):
|
|||
self.draw_app.plot_all()
|
||||
|
||||
def on_key(self, key):
|
||||
# Jump to coords
|
||||
if key == QtCore.Qt.Key_J or key == 'J':
|
||||
self.draw_app.app.on_jump_to()
|
||||
|
||||
if key == 'Backspace' or key == QtCore.Qt.Key_Backspace:
|
||||
if len(self.points) > 0:
|
||||
if self.draw_app.bend_mode == 5:
|
||||
|
@ -1148,9 +1158,12 @@ class FCTrack(FCRegion):
|
|||
except Exception as e:
|
||||
log.debug("FlatCAMGrbEditor.FCTrack.__init__() --> %s" % str(e))
|
||||
|
||||
self.cursor = QtGui.QCursor(QtGui.QPixmap(self.app.resource_location + '/aero_path%s.png' % self.draw_app.bend_mode))
|
||||
self.cursor = QtGui.QCursor(QtGui.QPixmap(self.draw_app.app.resource_location +
|
||||
'/aero_path%s.png' % self.draw_app.bend_mode))
|
||||
QtGui.QGuiApplication.setOverrideCursor(self.cursor)
|
||||
|
||||
self.draw_app.app.jump_signal.connect(lambda x: self.draw_app.update_utility_geometry(data=x))
|
||||
|
||||
self.draw_app.app.inform.emit(_('Track Mode 1: 45 degrees ...'))
|
||||
|
||||
def make(self):
|
||||
|
@ -1168,8 +1181,10 @@ class FCTrack(FCRegion):
|
|||
|
||||
self.draw_app.in_action = False
|
||||
self.complete = True
|
||||
self.draw_app.app.inform.emit('[success] %s' %
|
||||
_("Done."))
|
||||
|
||||
self.draw_app.app.jump_signal.disconnect()
|
||||
|
||||
self.draw_app.app.inform.emit('[success] %s' % _("Done."))
|
||||
|
||||
def clean_up(self):
|
||||
self.draw_app.selected = []
|
||||
|
@ -1287,6 +1302,10 @@ class FCTrack(FCRegion):
|
|||
self.draw_app.draw_utility_geometry(geo=geo)
|
||||
return _("Backtracked one point ...")
|
||||
|
||||
# Jump to coords
|
||||
if key == QtCore.Qt.Key_J or key == 'J':
|
||||
self.draw_app.app.on_jump_to()
|
||||
|
||||
if key == 'T' or key == QtCore.Qt.Key_T:
|
||||
try:
|
||||
QtGui.QGuiApplication.restoreOverrideCursor()
|
||||
|
@ -1396,6 +1415,8 @@ class FCDisc(FCShapeTool):
|
|||
|
||||
self.draw_app.app.inform.emit(_("Click on Center point ..."))
|
||||
|
||||
self.draw_app.app.jump_signal.connect(lambda x: self.draw_app.update_utility_geometry(data=x))
|
||||
|
||||
self.steps_per_circ = self.draw_app.app.defaults["gerber_circle_steps"]
|
||||
|
||||
def click(self, point):
|
||||
|
@ -1442,8 +1463,10 @@ class FCDisc(FCShapeTool):
|
|||
|
||||
self.draw_app.in_action = False
|
||||
self.complete = True
|
||||
self.draw_app.app.inform.emit('[success] %s' %
|
||||
_("Done."))
|
||||
|
||||
self.draw_app.app.jump_signal.disconnect()
|
||||
|
||||
self.draw_app.app.inform.emit('[success] %s' % _("Done."))
|
||||
|
||||
def clean_up(self):
|
||||
self.draw_app.selected = []
|
||||
|
@ -1490,6 +1513,7 @@ class FCSemiDisc(FCShapeTool):
|
|||
self.storage_obj = self.draw_app.storage_dict['0']['geometry']
|
||||
|
||||
self.steps_per_circ = self.draw_app.app.defaults["gerber_circle_steps"]
|
||||
self.draw_app.app.jump_signal.connect(lambda x: self.draw_app.update_utility_geometry(data=x))
|
||||
|
||||
def click(self, point):
|
||||
self.points.append(point)
|
||||
|
@ -1523,6 +1547,10 @@ class FCSemiDisc(FCShapeTool):
|
|||
self.direction = 'cw' if self.direction == 'ccw' else 'ccw'
|
||||
return '%s: %s' % (_('Direction'), self.direction.upper())
|
||||
|
||||
# Jump to coords
|
||||
if key == QtCore.Qt.Key_J or key == 'J':
|
||||
self.draw_app.app.on_jump_to()
|
||||
|
||||
if key == 'M' or key == QtCore.Qt.Key_M:
|
||||
# delete the possible points made before this action; we want to start anew
|
||||
self.points = []
|
||||
|
@ -1700,8 +1728,10 @@ class FCSemiDisc(FCShapeTool):
|
|||
|
||||
self.draw_app.in_action = False
|
||||
self.complete = True
|
||||
self.draw_app.app.inform.emit('[success] %s' %
|
||||
_("Done."))
|
||||
|
||||
self.draw_app.app.jump_signal.disconnect()
|
||||
|
||||
self.draw_app.app.inform.emit('[success] %s' % _("Done."))
|
||||
|
||||
def clean_up(self):
|
||||
self.draw_app.selected = []
|
||||
|
@ -4517,6 +4547,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
self.snap_x = x
|
||||
self.snap_y = y
|
||||
|
||||
self.app.mouse = [x, y]
|
||||
|
||||
# update the position label in the infobar since the APP mouse event handlers are disconnected
|
||||
self.app.ui.position_label.setText(" <b>X</b>: %.4f "
|
||||
"<b>Y</b>: %.4f" % (x, y))
|
||||
|
@ -4594,19 +4626,21 @@ class FlatCAMGrbEditor(QtCore.QObject):
|
|||
self.shapes.clear(update=True)
|
||||
|
||||
for storage in self.storage_dict:
|
||||
for elem in self.storage_dict[storage]['geometry']:
|
||||
if 'solid' in elem.geo:
|
||||
geometric_data = elem.geo['solid']
|
||||
if geometric_data is None:
|
||||
continue
|
||||
# fix for apertures with now geometry inside
|
||||
if 'geometry' in self.storage_dict[storage]:
|
||||
for elem in self.storage_dict[storage]['geometry']:
|
||||
if 'solid' in elem.geo:
|
||||
geometric_data = elem.geo['solid']
|
||||
if geometric_data is None:
|
||||
continue
|
||||
|
||||
if elem in self.selected:
|
||||
self.plot_shape(geometry=geometric_data,
|
||||
color=self.app.defaults['global_sel_draw_color'] + 'FF',
|
||||
linewidth=2)
|
||||
else:
|
||||
self.plot_shape(geometry=geometric_data,
|
||||
color=self.app.defaults['global_draw_color'] + 'FF')
|
||||
if elem in self.selected:
|
||||
self.plot_shape(geometry=geometric_data,
|
||||
color=self.app.defaults['global_sel_draw_color'] + 'FF',
|
||||
linewidth=2)
|
||||
else:
|
||||
self.plot_shape(geometry=geometric_data,
|
||||
color=self.app.defaults['global_draw_color'] + 'FF')
|
||||
|
||||
if self.utility:
|
||||
for elem in self.utility:
|
||||
|
|
|
@ -196,7 +196,7 @@ class TextEditor(QtWidgets.QWidget):
|
|||
_filter_ = filt
|
||||
else:
|
||||
_filter_ = "G-Code Files (*.nc);; G-Code Files (*.txt);; G-Code Files (*.tap);; G-Code Files (*.cnc);; " \
|
||||
"All Files (*.*)"
|
||||
"PDF Files (*.pdf);;All Files (*.*)"
|
||||
|
||||
if name:
|
||||
obj_name = name
|
||||
|
@ -206,7 +206,7 @@ class TextEditor(QtWidgets.QWidget):
|
|||
except AttributeError:
|
||||
obj_name = 'file'
|
||||
if filt is None:
|
||||
_filter_ = "FlatConfig Files (*.FlatConfig);;All Files (*.*)"
|
||||
_filter_ = "FlatConfig Files (*.FlatConfig);;PDF Files (*.pdf);;All Files (*.*)"
|
||||
|
||||
try:
|
||||
filename = str(QtWidgets.QFileDialog.getSaveFileName(
|
||||
|
@ -237,13 +237,24 @@ class TextEditor(QtWidgets.QWidget):
|
|||
styleH = styles['Heading1']
|
||||
story = []
|
||||
|
||||
if self.app.defaults['units'].lower() == 'mm':
|
||||
bmargin = self.app.defaults['global_tpdf_bmargin'] * mm
|
||||
tmargin = self.app.defaults['global_tpdf_tmargin'] * mm
|
||||
rmargin = self.app.defaults['global_tpdf_rmargin'] * mm
|
||||
lmargin = self.app.defaults['global_tpdf_lmargin'] * mm
|
||||
else:
|
||||
bmargin = self.app.defaults['global_tpdf_bmargin'] * inch
|
||||
tmargin = self.app.defaults['global_tpdf_tmargin'] * inch
|
||||
rmargin = self.app.defaults['global_tpdf_rmargin'] * inch
|
||||
lmargin = self.app.defaults['global_tpdf_lmargin'] * inch
|
||||
|
||||
doc = SimpleDocTemplate(
|
||||
filename,
|
||||
pagesize=page_size,
|
||||
bottomMargin=0.4 * inch,
|
||||
topMargin=0.6 * inch,
|
||||
rightMargin=0.8 * inch,
|
||||
leftMargin=0.8 * inch)
|
||||
bottomMargin=bmargin,
|
||||
topMargin=tmargin,
|
||||
rightMargin=rmargin,
|
||||
leftMargin=lmargin)
|
||||
|
||||
P = Paragraph(lined_gcode, styleN)
|
||||
story.append(P)
|
||||
|
|
|
@ -240,6 +240,9 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
|
||||
# Separator
|
||||
self.menufile.addSeparator()
|
||||
self.menufile_print = QtWidgets.QAction(
|
||||
QtGui.QIcon(self.app.resource_location + '/printer32.png'), '%s\tCTRL+P' % _('Print (PDF)'))
|
||||
self.menufile.addAction(self.menufile_print)
|
||||
|
||||
self.menufile_save = self.menufile.addMenu(QtGui.QIcon(self.app.resource_location + '/save_as.png'), _('Save'))
|
||||
|
||||
|
@ -260,11 +263,6 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
|
||||
self.menufile_save.addSeparator()
|
||||
|
||||
# Save Object PDF
|
||||
self.menufilesave_object_pdf = QtWidgets.QAction(QtGui.QIcon(self.app.resource_location + '/pdf32.png'),
|
||||
_('Save Object as PDF ...'), self)
|
||||
self.menufile_save.addAction(self.menufilesave_object_pdf)
|
||||
|
||||
# Separator
|
||||
self.menufile.addSeparator()
|
||||
|
||||
|
@ -1381,18 +1379,22 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
<td height="20"><strong>CTRL+G</strong></td>
|
||||
<td> %s</td>
|
||||
</tr>
|
||||
<tr height="20">
|
||||
<td height="20"><strong>CTRL+N</strong></td>
|
||||
<td> %s</td>
|
||||
</tr>
|
||||
<tr height="20">
|
||||
<td height="20"><strong>CTRL+M</strong></td>
|
||||
<td> %s</td>
|
||||
</tr>
|
||||
<tr height="20">
|
||||
<td height="20"><strong>CTRL+N</strong></td>
|
||||
<td> %s</td>
|
||||
</tr>
|
||||
<tr height="20">
|
||||
<td height="20"><strong>CTRL+O</strong></td>
|
||||
<td> %s</td>
|
||||
</tr>
|
||||
<tr height="20">
|
||||
<td height="20"><strong>CTRL+P</strong></td>
|
||||
<td> %s</td>
|
||||
</tr>
|
||||
<tr height="20">
|
||||
<td height="20"><strong>CTRL+Q</strong></td>
|
||||
<td> %s</td>
|
||||
|
@ -1579,8 +1581,8 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
|
||||
# CTRL section
|
||||
_("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"),
|
||||
_("Open Excellon File"), _("Open Gerber File"), _("Distance Tool"), _("New Project"),
|
||||
_("Open Project"), _("Print (PDF)"), _("PDF Import Tool"), _("Save Project As"), _("Toggle Plot Area"),
|
||||
|
||||
# SHIFT section
|
||||
_("Copy Obj_Name"),
|
||||
|
@ -2671,18 +2673,22 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
if key == QtCore.Qt.Key_G:
|
||||
self.app.on_fileopengerber()
|
||||
|
||||
# Create New Project
|
||||
if key == QtCore.Qt.Key_N:
|
||||
self.app.on_file_new_click()
|
||||
|
||||
# Distance Tool
|
||||
if key == QtCore.Qt.Key_M:
|
||||
self.app.distance_tool.run()
|
||||
|
||||
# Create New Project
|
||||
if key == QtCore.Qt.Key_N:
|
||||
self.app.on_file_new_click()
|
||||
|
||||
# Open Project
|
||||
if key == QtCore.Qt.Key_O:
|
||||
self.app.on_file_openproject()
|
||||
|
||||
# Open Project
|
||||
if key == QtCore.Qt.Key_P:
|
||||
self.app.on_file_save_objects_pdf(use_thread=True)
|
||||
|
||||
# PDF Import
|
||||
if key == QtCore.Qt.Key_Q:
|
||||
self.app.pdf_tool.run()
|
||||
|
|
|
@ -2239,7 +2239,7 @@ class Dialog_box(QtWidgets.QWidget):
|
|||
|
||||
|
||||
class DialogBoxRadio(QtWidgets.QDialog):
|
||||
def __init__(self, title=None, label=None, icon=None, initial_text=None):
|
||||
def __init__(self, title=None, label=None, icon=None, initial_text=None, reference='abs'):
|
||||
"""
|
||||
|
||||
:param title: string with the window title
|
||||
|
@ -2258,11 +2258,6 @@ class DialogBoxRadio(QtWidgets.QDialog):
|
|||
|
||||
self.form = QtWidgets.QFormLayout(self)
|
||||
|
||||
self.form.addRow(QtWidgets.QLabel(''))
|
||||
|
||||
self.wdg_label = QtWidgets.QLabel('<b>%s</b>' % str(label))
|
||||
self.form.addRow(self.wdg_label)
|
||||
|
||||
self.ref_label = QtWidgets.QLabel('%s:' % _("Reference"))
|
||||
self.ref_label.setToolTip(
|
||||
_("The reference can be:\n"
|
||||
|
@ -2273,10 +2268,15 @@ class DialogBoxRadio(QtWidgets.QDialog):
|
|||
{"label": _("Abs"), "value": "abs"},
|
||||
{"label": _("Relative"), "value": "rel"}
|
||||
], orientation='horizontal', stretch=False)
|
||||
self.ref_radio.set_value('abs')
|
||||
self.ref_radio.set_value(reference)
|
||||
self.form.addRow(self.ref_label, self.ref_radio)
|
||||
|
||||
self.loc_label = QtWidgets.QLabel('<b>%s:</b>' % _("Location"))
|
||||
self.form.addRow(QtWidgets.QLabel(''))
|
||||
|
||||
self.wdg_label = QtWidgets.QLabel('<b>%s</b>' % str(label))
|
||||
self.form.addRow(self.wdg_label)
|
||||
|
||||
self.loc_label = QtWidgets.QLabel('%s:' % _("Location"))
|
||||
self.loc_label.setToolTip(
|
||||
_("The Location value is a tuple (x,y).\n"
|
||||
"If the reference is Absolute then the Jump will be at the position (x,y).\n"
|
||||
|
|
|
@ -1330,6 +1330,76 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
|
|||
|
||||
grid0.addWidget(self.machinist_cb, 21, 0, 1, 2)
|
||||
|
||||
separator_line = QtWidgets.QFrame()
|
||||
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
self.layout.addWidget(separator_line)
|
||||
|
||||
self.layout.addWidget(QtWidgets.QLabel(''))
|
||||
|
||||
grid1 = QtWidgets.QGridLayout()
|
||||
self.layout.addLayout(grid1)
|
||||
grid1.setColumnStretch(0, 0)
|
||||
grid1.setColumnStretch(1, 1)
|
||||
|
||||
self.pdf_param_label = QtWidgets.QLabel('<B>%s:</b>' % _("Text to PDF parameters"))
|
||||
self.pdf_param_label.setToolTip(
|
||||
_("Used when saving text in Code Editor or in FlatCAM Document objects.")
|
||||
)
|
||||
grid1.addWidget(self.pdf_param_label, 0, 0, 1, 2)
|
||||
|
||||
# Top Margin value
|
||||
self.tmargin_entry = FCDoubleSpinner()
|
||||
self.tmargin_entry.set_precision(self.decimals)
|
||||
self.tmargin_entry.set_range(0.0000, 9999.9999)
|
||||
|
||||
self.tmargin_label = QtWidgets.QLabel('%s:' % _("Top Margin"))
|
||||
self.tmargin_label.setToolTip(
|
||||
_("Distance between text body and the top of the PDF file.")
|
||||
)
|
||||
|
||||
grid1.addWidget(self.tmargin_label, 1, 0)
|
||||
grid1.addWidget(self.tmargin_entry, 1, 1)
|
||||
|
||||
# Bottom Margin value
|
||||
self.bmargin_entry = FCDoubleSpinner()
|
||||
self.bmargin_entry.set_precision(self.decimals)
|
||||
self.bmargin_entry.set_range(0.0000, 9999.9999)
|
||||
|
||||
self.bmargin_label = QtWidgets.QLabel('%s:' % _("Bottom Margin"))
|
||||
self.bmargin_label.setToolTip(
|
||||
_("Distance between text body and the bottom of the PDF file.")
|
||||
)
|
||||
|
||||
grid1.addWidget(self.bmargin_label, 2, 0)
|
||||
grid1.addWidget(self.bmargin_entry, 2, 1)
|
||||
|
||||
# Left Margin value
|
||||
self.lmargin_entry = FCDoubleSpinner()
|
||||
self.lmargin_entry.set_precision(self.decimals)
|
||||
self.lmargin_entry.set_range(0.0000, 9999.9999)
|
||||
|
||||
self.lmargin_label = QtWidgets.QLabel('%s:' % _("Left Margin"))
|
||||
self.lmargin_label.setToolTip(
|
||||
_("Distance between text body and the left of the PDF file.")
|
||||
)
|
||||
|
||||
grid1.addWidget(self.lmargin_label, 3, 0)
|
||||
grid1.addWidget(self.lmargin_entry, 3, 1)
|
||||
|
||||
# Right Margin value
|
||||
self.rmargin_entry = FCDoubleSpinner()
|
||||
self.rmargin_entry.set_precision(self.decimals)
|
||||
self.rmargin_entry.set_range(0.0000, 9999.9999)
|
||||
|
||||
self.rmargin_label = QtWidgets.QLabel('%s:' % _("Right Margin"))
|
||||
self.rmargin_label.setToolTip(
|
||||
_("Distance between text body and the right of the PDF file.")
|
||||
)
|
||||
|
||||
grid1.addWidget(self.rmargin_label, 4, 0)
|
||||
grid1.addWidget(self.rmargin_entry, 4, 1)
|
||||
|
||||
self.layout.addStretch()
|
||||
|
||||
if sys.platform != 'win32':
|
||||
|
@ -1435,6 +1505,29 @@ class GerberGenPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.gerber_zeros_label, 5, 0)
|
||||
grid0.addWidget(self.gerber_zeros_radio, 5, 1, 1, 2)
|
||||
|
||||
separator_line = QtWidgets.QFrame()
|
||||
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
grid0.addWidget(separator_line, 6, 0, 1, 3)
|
||||
|
||||
# Apertures Cleaning
|
||||
self.gerber_clean_cb = FCCheckBox(label='%s' % _('Clean Apertures'))
|
||||
self.gerber_clean_cb.setToolTip(
|
||||
_("Will remove apertures that do not have geometry\n"
|
||||
"thus lowering the number of apertures in the Gerber object.")
|
||||
)
|
||||
grid0.addWidget(self.gerber_clean_cb, 7, 0, 1, 3)
|
||||
|
||||
# Apply Extra Buffering
|
||||
self.gerber_extra_buffering = FCCheckBox(label='%s' % _('Polarity change buffer'))
|
||||
self.gerber_extra_buffering.setToolTip(
|
||||
_("Will apply extra buffering for the\n"
|
||||
"solid geometry when we have polarity changes.\n"
|
||||
"May help loading Gerber files that otherwise\n"
|
||||
"do not load correctly.")
|
||||
)
|
||||
grid0.addWidget(self.gerber_extra_buffering, 8, 0, 1, 3)
|
||||
|
||||
self.layout.addStretch()
|
||||
|
||||
|
||||
|
@ -1528,6 +1621,11 @@ class GerberOptPrefGroupUI(OptionsGroupUI):
|
|||
)
|
||||
grid0.addWidget(self.combine_passes_cb, 5, 0, 1, 2)
|
||||
|
||||
separator_line = QtWidgets.QFrame()
|
||||
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
grid0.addWidget(separator_line, 6, 0, 1, 2)
|
||||
|
||||
# ## Clear non-copper regions
|
||||
self.clearcopper_label = QtWidgets.QLabel("<b>%s:</b>" % _("Non-copper regions"))
|
||||
self.clearcopper_label.setToolTip(
|
||||
|
@ -1564,6 +1662,11 @@ class GerberOptPrefGroupUI(OptionsGroupUI):
|
|||
)
|
||||
grid1.addWidget(self.noncopper_rounded_cb, 1, 0, 1, 2)
|
||||
|
||||
separator_line = QtWidgets.QFrame()
|
||||
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
grid1.addWidget(separator_line, 2, 0, 1, 2)
|
||||
|
||||
# ## Bounding box
|
||||
self.boundingbox_label = QtWidgets.QLabel('<b>%s:</b>' % _('Bounding Box'))
|
||||
self.layout.addWidget(self.boundingbox_label)
|
||||
|
@ -1634,6 +1737,11 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI):
|
|||
)
|
||||
grid0.addWidget(self.aperture_table_visibility_cb, 1, 0, 1, 2)
|
||||
|
||||
separator_line = QtWidgets.QFrame()
|
||||
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
grid0.addWidget(separator_line, 2, 0, 1, 2)
|
||||
|
||||
# Tool Type
|
||||
self.tool_type_label = QtWidgets.QLabel('<b>%s</b>' % _('Tool Type'))
|
||||
self.tool_type_label.setToolTip(
|
||||
|
@ -1645,8 +1753,8 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI):
|
|||
self.tool_type_radio = RadioSet([{'label': 'Circular', 'value': 'circular'},
|
||||
{'label': 'V-Shape', 'value': 'v'}])
|
||||
|
||||
grid0.addWidget(self.tool_type_label, 2, 0)
|
||||
grid0.addWidget(self.tool_type_radio, 2, 1, 1, 2)
|
||||
grid0.addWidget(self.tool_type_label, 3, 0)
|
||||
grid0.addWidget(self.tool_type_radio, 3, 1, 1, 2)
|
||||
|
||||
# Tip Dia
|
||||
self.tipdialabel = QtWidgets.QLabel('%s:' % _('V-Tip Dia'))
|
||||
|
@ -1658,8 +1766,8 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI):
|
|||
self.tipdia_spinner.set_range(-99.9999, 99.9999)
|
||||
self.tipdia_spinner.setSingleStep(0.1)
|
||||
self.tipdia_spinner.setWrapping(True)
|
||||
grid0.addWidget(self.tipdialabel, 3, 0)
|
||||
grid0.addWidget(self.tipdia_spinner, 3, 1, 1, 2)
|
||||
grid0.addWidget(self.tipdialabel, 4, 0)
|
||||
grid0.addWidget(self.tipdia_spinner, 4, 1, 1, 2)
|
||||
|
||||
# Tip Angle
|
||||
self.tipanglelabel = QtWidgets.QLabel('%s:' % _('V-Tip Angle'))
|
||||
|
@ -1671,8 +1779,8 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI):
|
|||
self.tipangle_spinner.set_range(0, 180)
|
||||
self.tipangle_spinner.setSingleStep(5)
|
||||
self.tipangle_spinner.setWrapping(True)
|
||||
grid0.addWidget(self.tipanglelabel, 4, 0)
|
||||
grid0.addWidget(self.tipangle_spinner, 4, 1, 1, 2)
|
||||
grid0.addWidget(self.tipanglelabel, 5, 0)
|
||||
grid0.addWidget(self.tipangle_spinner, 5, 1, 1, 2)
|
||||
|
||||
# Cut Z
|
||||
self.cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
|
||||
|
@ -1686,8 +1794,8 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI):
|
|||
self.cutz_spinner.setSingleStep(0.1)
|
||||
self.cutz_spinner.setWrapping(True)
|
||||
|
||||
grid0.addWidget(self.cutzlabel, 5, 0)
|
||||
grid0.addWidget(self.cutz_spinner, 5, 1, 1, 2)
|
||||
grid0.addWidget(self.cutzlabel, 6, 0)
|
||||
grid0.addWidget(self.cutz_spinner, 6, 1, 1, 2)
|
||||
|
||||
# Isolation Type
|
||||
self.iso_type_label = QtWidgets.QLabel('%s:' % _('Isolation Type'))
|
||||
|
@ -1705,8 +1813,13 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI):
|
|||
{'label': _('Exterior'), 'value': 'ext'},
|
||||
{'label': _('Interior'), 'value': 'int'}])
|
||||
|
||||
grid0.addWidget(self.iso_type_label, 6, 0,)
|
||||
grid0.addWidget(self.iso_type_radio, 6, 1, 1, 2)
|
||||
grid0.addWidget(self.iso_type_label, 7, 0,)
|
||||
grid0.addWidget(self.iso_type_radio, 7, 1, 1, 2)
|
||||
|
||||
separator_line = QtWidgets.QFrame()
|
||||
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
grid0.addWidget(separator_line, 8, 0, 1, 2)
|
||||
|
||||
# Buffering Type
|
||||
buffering_label = QtWidgets.QLabel('%s:' % _('Buffering'))
|
||||
|
@ -1718,8 +1831,8 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI):
|
|||
)
|
||||
self.buffering_radio = RadioSet([{'label': _('None'), 'value': 'no'},
|
||||
{'label': _('Full'), 'value': 'full'}])
|
||||
grid0.addWidget(buffering_label, 7, 0)
|
||||
grid0.addWidget(self.buffering_radio, 7, 1)
|
||||
grid0.addWidget(buffering_label, 9, 0)
|
||||
grid0.addWidget(self.buffering_radio, 9, 1)
|
||||
|
||||
# Simplification
|
||||
self.simplify_cb = FCCheckBox(label=_('Simplify'))
|
||||
|
@ -1728,7 +1841,7 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI):
|
|||
"loaded with simplification having a set tolerance.\n"
|
||||
"<<WARNING>>: Don't change this unless you know what you are doing !!!")
|
||||
)
|
||||
grid0.addWidget(self.simplify_cb, 8, 0, 1, 2)
|
||||
grid0.addWidget(self.simplify_cb, 10, 0, 1, 2)
|
||||
|
||||
# Simplification tolerance
|
||||
self.simplification_tol_label = QtWidgets.QLabel(_('Tolerance'))
|
||||
|
@ -1740,8 +1853,8 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI):
|
|||
self.simplification_tol_spinner.setRange(0.00000, 0.01000)
|
||||
self.simplification_tol_spinner.setSingleStep(0.0001)
|
||||
|
||||
grid0.addWidget(self.simplification_tol_label, 9, 0)
|
||||
grid0.addWidget(self.simplification_tol_spinner, 9, 1)
|
||||
grid0.addWidget(self.simplification_tol_label, 11, 0)
|
||||
grid0.addWidget(self.simplification_tol_spinner, 11, 1)
|
||||
self.ois_simplif = OptionalInputSection(
|
||||
self.simplify_cb,
|
||||
[
|
||||
|
|
|
@ -72,6 +72,8 @@ class Gerber(Geometry):
|
|||
# "use_buffer_for_union": True
|
||||
# }
|
||||
|
||||
app = None
|
||||
|
||||
def __init__(self, steps_per_circle=None):
|
||||
"""
|
||||
The constructor takes no parameters. Use ``gerber.parse_files()``
|
||||
|
@ -465,11 +467,12 @@ class Gerber(Geometry):
|
|||
geo_dict['follow'] = geo_f
|
||||
|
||||
geo_s = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4))
|
||||
if not geo_s.is_empty:
|
||||
if not geo_s.is_empty and geo_s.is_valid:
|
||||
if self.app.defaults['gerber_simplification']:
|
||||
poly_buffer.append(geo_s.simplify(s_tol))
|
||||
else:
|
||||
poly_buffer.append(geo_s)
|
||||
|
||||
if self.is_lpc is True:
|
||||
geo_dict['clear'] = geo_s
|
||||
else:
|
||||
|
@ -1430,6 +1433,18 @@ class Gerber(Geometry):
|
|||
|
||||
self.solid_geometry = final_poly
|
||||
|
||||
# FIX for issue #347 - Sprint Layout generate strange Gerber files when the copper pour is enabled
|
||||
# it use a filled bounding box polygon to which add clear polygons (negative) to isolate the copper
|
||||
# features
|
||||
if self.app.defaults['gerber_extra_buffering']:
|
||||
candidate_geo = list()
|
||||
try:
|
||||
for p in self.solid_geometry:
|
||||
candidate_geo.append(p.buffer(0.0000001))
|
||||
except TypeError:
|
||||
candidate_geo.append(self.solid_geometry.buffer(0.0000001))
|
||||
self.solid_geometry = candidate_geo
|
||||
|
||||
# try:
|
||||
# self.solid_geometry = self.solid_geometry.union(new_poly)
|
||||
# except Exception as e:
|
||||
|
@ -1442,6 +1457,12 @@ class Gerber(Geometry):
|
|||
else:
|
||||
self.solid_geometry = self.solid_geometry.difference(new_poly)
|
||||
|
||||
if self.app.defaults['gerber_clean_apertures']:
|
||||
# clean the Gerber file of apertures with no geometry
|
||||
for apid, apvalue in list(self.apertures.items()):
|
||||
if 'geometry' not in apvalue:
|
||||
self.apertures.pop(apid)
|
||||
|
||||
# init this for the following operations
|
||||
self.conversion_done = False
|
||||
except Exception as err:
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
from PyQt5 import QtWidgets, QtCore
|
||||
|
||||
from FlatCAMTool import FlatCAMTool
|
||||
from flatcamGUI.GUIElements import RadioSet, FCDoubleSpinner, EvalEntry
|
||||
from flatcamGUI.GUIElements import RadioSet, FCDoubleSpinner, EvalEntry, FCEntry
|
||||
from FlatCAMObj import FlatCAMGerber, FlatCAMExcellon, FlatCAMGeometry
|
||||
|
||||
from numpy import Inf
|
||||
|
||||
from shapely.geometry import Point
|
||||
from shapely import affinity
|
||||
|
||||
|
@ -219,6 +221,11 @@ class DblSidedTool(FlatCAMTool):
|
|||
self.box_combo.hide()
|
||||
self.box_combo_type.hide()
|
||||
|
||||
separator_line = QtWidgets.QFrame()
|
||||
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
grid_lay2.addWidget(separator_line, 12, 0, 1, 2)
|
||||
|
||||
# ## Alignment holes
|
||||
self.ah_label = QtWidgets.QLabel("<b>%s:</b>" % _('Alignment Drill Coordinates'))
|
||||
self.ah_label.setToolTip(
|
||||
|
@ -272,14 +279,14 @@ class DblSidedTool(FlatCAMTool):
|
|||
# Drill diameter value
|
||||
self.drill_dia = FCDoubleSpinner()
|
||||
self.drill_dia.set_precision(self.decimals)
|
||||
self.drill_dia.set_range(0.0000, 9999.9999)
|
||||
|
||||
self.dd_label = QtWidgets.QLabel('%s:' % _("Drill dia"))
|
||||
self.dd_label.setToolTip(
|
||||
self.drill_dia.setToolTip(
|
||||
_("Diameter of the drill for the "
|
||||
"alignment holes.")
|
||||
)
|
||||
grid0.addWidget(self.dd_label, 1, 0)
|
||||
grid0.addWidget(self.drill_dia, 1, 1)
|
||||
|
||||
grid0.addWidget(self.drill_dia, 1, 0, 1, 2)
|
||||
|
||||
# ## Buttons
|
||||
self.create_alignment_hole_button = QtWidgets.QPushButton(_("Create Excellon Object"))
|
||||
|
@ -296,6 +303,102 @@ class DblSidedTool(FlatCAMTool):
|
|||
""")
|
||||
self.layout.addWidget(self.create_alignment_hole_button)
|
||||
|
||||
separator_line = QtWidgets.QFrame()
|
||||
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
self.layout.addWidget(separator_line)
|
||||
|
||||
self.layout.addWidget(QtWidgets.QLabel(''))
|
||||
|
||||
grid1 = QtWidgets.QGridLayout()
|
||||
self.layout.addLayout(grid1)
|
||||
grid1.setColumnStretch(0, 0)
|
||||
grid1.setColumnStretch(1, 1)
|
||||
|
||||
# Xmin value
|
||||
self.xmin_entry = FCDoubleSpinner()
|
||||
self.xmin_entry.set_precision(self.decimals)
|
||||
self.xmin_entry.set_range(-9999.9999, 9999.9999)
|
||||
|
||||
self.xmin_label = QtWidgets.QLabel('%s:' % _("X min"))
|
||||
self.xmin_label.setToolTip(
|
||||
_("Minimum location.")
|
||||
)
|
||||
self.xmin_entry.setReadOnly(True)
|
||||
|
||||
grid1.addWidget(self.xmin_label, 1, 0)
|
||||
grid1.addWidget(self.xmin_entry, 1, 1)
|
||||
|
||||
# Ymin value
|
||||
self.ymin_entry = FCDoubleSpinner()
|
||||
self.ymin_entry.set_precision(self.decimals)
|
||||
self.ymin_entry.set_range(-9999.9999, 9999.9999)
|
||||
|
||||
self.ymin_label = QtWidgets.QLabel('%s:' % _("Y min"))
|
||||
self.ymin_label.setToolTip(
|
||||
_("Minimum location.")
|
||||
)
|
||||
self.ymin_entry.setReadOnly(True)
|
||||
|
||||
grid1.addWidget(self.ymin_label, 2, 0)
|
||||
grid1.addWidget(self.ymin_entry, 2, 1)
|
||||
|
||||
# Xmax value
|
||||
self.xmax_entry = FCDoubleSpinner()
|
||||
self.xmax_entry.set_precision(self.decimals)
|
||||
self.xmax_entry.set_range(-9999.9999, 9999.9999)
|
||||
|
||||
self.xmax_label = QtWidgets.QLabel('%s:' % _("X max"))
|
||||
self.xmax_label.setToolTip(
|
||||
_("Maximum location.")
|
||||
)
|
||||
self.xmax_entry.setReadOnly(True)
|
||||
|
||||
grid1.addWidget(self.xmax_label, 3, 0)
|
||||
grid1.addWidget(self.xmax_entry, 3, 1)
|
||||
|
||||
# Ymax value
|
||||
self.ymax_entry = FCDoubleSpinner()
|
||||
self.ymax_entry.set_precision(self.decimals)
|
||||
self.ymax_entry.set_range(-9999.9999, 9999.9999)
|
||||
|
||||
self.ymax_label = QtWidgets.QLabel('%s:' % _("Y max"))
|
||||
self.ymax_label.setToolTip(
|
||||
_("Maximum location.")
|
||||
)
|
||||
self.ymax_entry.setReadOnly(True)
|
||||
|
||||
grid1.addWidget(self.ymax_label, 4, 0)
|
||||
grid1.addWidget(self.ymax_entry, 4, 1)
|
||||
|
||||
# Center point value
|
||||
self.center_entry = FCEntry()
|
||||
|
||||
self.center_label = QtWidgets.QLabel('%s:' % _("Centroid"))
|
||||
self.center_label.setToolTip(
|
||||
_("The center point location for the rectangular\n"
|
||||
"bounding shape. Centroid. Format is (x, y).")
|
||||
)
|
||||
self.center_entry.setReadOnly(True)
|
||||
|
||||
grid1.addWidget(self.center_label, 5, 0)
|
||||
grid1.addWidget(self.center_entry, 5, 1)
|
||||
|
||||
# Calculate Bounding box
|
||||
self.calculate_bb_button = QtWidgets.QPushButton(_("Calculate Bounds Values"))
|
||||
self.calculate_bb_button.setToolTip(
|
||||
_("Calculate the enveloping rectangular shape coordinates,\n"
|
||||
"for the selection of objects.\n"
|
||||
"The envelope shape is parallel with the X, Y axis.")
|
||||
)
|
||||
self.calculate_bb_button.setStyleSheet("""
|
||||
QPushButton
|
||||
{
|
||||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
self.layout.addWidget(self.calculate_bb_button)
|
||||
|
||||
self.layout.addStretch()
|
||||
|
||||
# ## Reset Tool
|
||||
|
@ -312,18 +415,20 @@ class DblSidedTool(FlatCAMTool):
|
|||
self.layout.addWidget(self.reset_button)
|
||||
|
||||
# ## Signals
|
||||
self.create_alignment_hole_button.clicked.connect(self.on_create_alignment_holes)
|
||||
self.mirror_gerber_button.clicked.connect(self.on_mirror_gerber)
|
||||
self.mirror_exc_button.clicked.connect(self.on_mirror_exc)
|
||||
self.mirror_geo_button.clicked.connect(self.on_mirror_geo)
|
||||
self.add_point_button.clicked.connect(self.on_point_add)
|
||||
self.add_drill_point_button.clicked.connect(self.on_drill_add)
|
||||
self.reset_button.clicked.connect(self.reset_fields)
|
||||
|
||||
self.box_combo_type.currentIndexChanged.connect(self.on_combo_box_type)
|
||||
|
||||
self.axis_location.group_toggle_fn = self.on_toggle_pointbox
|
||||
|
||||
self.create_alignment_hole_button.clicked.connect(self.on_create_alignment_holes)
|
||||
self.calculate_bb_button.clicked.connect(self.on_bbox_coordinates)
|
||||
|
||||
self.reset_button.clicked.connect(self.set_tool_ui)
|
||||
|
||||
self.drill_values = ""
|
||||
|
||||
def install(self, icon=None, separator=None, **kwargs):
|
||||
|
@ -366,6 +471,12 @@ class DblSidedTool(FlatCAMTool):
|
|||
self.axis_location.set_value(self.app.defaults["tools_2sided_axis_loc"])
|
||||
self.drill_dia.set_value(self.app.defaults["tools_2sided_drilldia"])
|
||||
|
||||
self.xmin_entry.set_value(0.0)
|
||||
self.ymin_entry.set_value(0.0)
|
||||
self.xmax_entry.set_value(0.0)
|
||||
self.ymax_entry.set_value(0.0)
|
||||
self.center_entry.set_value('')
|
||||
|
||||
def on_combo_box_type(self):
|
||||
obj_type = self.box_combo_type.currentIndex()
|
||||
self.box_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
|
||||
|
@ -589,6 +700,41 @@ class DblSidedTool(FlatCAMTool):
|
|||
self.box_combo_type.show()
|
||||
self.add_point_button.setDisabled(True)
|
||||
|
||||
def on_bbox_coordinates(self):
|
||||
|
||||
xmin = Inf
|
||||
ymin = Inf
|
||||
xmax = -Inf
|
||||
ymax = -Inf
|
||||
|
||||
obj_list = self.app.collection.get_selected()
|
||||
|
||||
if not obj_list:
|
||||
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed. No object(s) selected..."))
|
||||
return
|
||||
|
||||
for obj in obj_list:
|
||||
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 DblSidedTool. %s" % str(e))
|
||||
|
||||
self.xmin_entry.set_value(xmin)
|
||||
self.ymin_entry.set_value(ymin)
|
||||
self.xmax_entry.set_value(xmax)
|
||||
self.ymax_entry.set_value(ymax)
|
||||
cx = '%.*f' % (self.decimals, (((xmax - xmin) / 2.0) + xmin))
|
||||
cy = '%.*f' % (self.decimals, (((ymax - ymin) / 2.0) + ymin))
|
||||
val_txt = '(%s, %s)' % (cx, cy)
|
||||
|
||||
self.center_entry.set_value(val_txt)
|
||||
self.axis_location.set_value('point')
|
||||
self.point_entry.set_value(val_txt)
|
||||
|
||||
def reset_fields(self):
|
||||
self.gerber_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
|
||||
self.exc_object_combo.setRootModelIndex(self.app.collection.index(1, 0, QtCore.QModelIndex()))
|
||||
|
|
|
@ -1723,11 +1723,9 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
|||
|
||||
sol_geo = cascaded_union(isolated_geo)
|
||||
if has_offset is True:
|
||||
app_obj.inform.emit('[WARNING_NOTCL] %s ...' %
|
||||
_("Buffering"))
|
||||
app_obj.inform.emit('[WARNING_NOTCL] %s ...' % _("Buffering"))
|
||||
sol_geo = sol_geo.buffer(distance=ncc_offset)
|
||||
app_obj.inform.emit('[success] %s ...' %
|
||||
_("Buffering finished"))
|
||||
app_obj.inform.emit('[success] %s ...' % _("Buffering finished"))
|
||||
empty = self.get_ncc_empty_area(target=sol_geo, boundary=bounding_box)
|
||||
if empty == 'fail':
|
||||
return 'fail'
|
||||
|
@ -1760,6 +1758,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
|||
log.debug("NCC Tool. Finished calculation of 'empty' area.")
|
||||
self.app.inform.emit(_("NCC Tool. Finished calculation of 'empty' area."))
|
||||
|
||||
# COPPER CLEARING #
|
||||
cp = None
|
||||
for tool in sorted_tools:
|
||||
log.debug("Starting geometry processing for tool: %s" % str(tool))
|
||||
|
@ -1916,17 +1915,27 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
|||
if self.app.defaults["tools_ncc_plotting"] == 'progressive':
|
||||
self.temp_shapes.clear(update=True)
|
||||
|
||||
# delete tools with empty geometry
|
||||
keys_to_delete = []
|
||||
# look for keys in the tools_storage dict that have 'solid_geometry' values empty
|
||||
for uid in tools_storage:
|
||||
# if the solid_geometry (type=list) is empty
|
||||
if not tools_storage[uid]['solid_geometry']:
|
||||
keys_to_delete.append(uid)
|
||||
# # delete tools with empty geometry
|
||||
# keys_to_delete = []
|
||||
# # look for keys in the tools_storage dict that have 'solid_geometry' values empty
|
||||
# for uid in tools_storage:
|
||||
# # if the solid_geometry (type=list) is empty
|
||||
# if not tools_storage[uid]['solid_geometry']:
|
||||
# keys_to_delete.append(uid)
|
||||
#
|
||||
# # actual delete of keys from the tools_storage dict
|
||||
# for k in keys_to_delete:
|
||||
# tools_storage.pop(k, None)
|
||||
|
||||
# actual delete of keys from the tools_storage dict
|
||||
for k in keys_to_delete:
|
||||
tools_storage.pop(k, None)
|
||||
# delete tools with empty geometry
|
||||
# look for keys in the tools_storage dict that have 'solid_geometry' values empty
|
||||
for uid, uid_val in list(tools_storage.items()):
|
||||
try:
|
||||
# if the solid_geometry (type=list) is empty
|
||||
if not uid_val['solid_geometry']:
|
||||
tools_storage.pop(uid, None)
|
||||
except KeyError:
|
||||
tools_storage.pop(uid, None)
|
||||
|
||||
geo_obj.options["cnctooldia"] = str(tool)
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 559 B |
Binary file not shown.
After Width: | Height: | Size: 818 B |
Loading…
Reference in New Issue