1114
FlatCAMApp.py
315
FlatCAMObj.py
|
@ -178,7 +178,7 @@ class FlatCAMObj(QtCore.QObject):
|
|||
self.muted_ui = False
|
||||
|
||||
def on_name_activate(self):
|
||||
old_name = copy.copy(self.options["name"])
|
||||
old_name = copy(self.options["name"])
|
||||
new_name = self.ui.name_entry.get_value()
|
||||
|
||||
# update the SHELL auto-completer model data
|
||||
|
@ -186,11 +186,12 @@ class FlatCAMObj(QtCore.QObject):
|
|||
self.app.myKeywords.remove(old_name)
|
||||
self.app.myKeywords.append(new_name)
|
||||
self.app.shell._edit.set_model_data(self.app.myKeywords)
|
||||
self.app.ui.code_editor.set_model_data(self.app.myKeywords)
|
||||
except:
|
||||
log.debug("on_name_activate() --> Could not remove the old object name from auto-completer model list")
|
||||
|
||||
self.options["name"] = self.ui.name_entry.get_value()
|
||||
self.app.inform.emit(_("[success]Name changed from {old} to {new}").format(old=old_name, new=new_name))
|
||||
self.app.inform.emit(_("[success] Name changed from {old} to {new}").format(old=old_name, new=new_name))
|
||||
|
||||
def on_offset_button_click(self):
|
||||
self.app.report_usage("obj_on_offset_button")
|
||||
|
@ -410,18 +411,27 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
except:
|
||||
log.warning("Failed to copy option.", option)
|
||||
|
||||
for geos in grb.solid_geometry:
|
||||
grb_final.solid_geometry.append(geos)
|
||||
grb_final.follow_geometry.append(geos)
|
||||
try:
|
||||
for geos in grb.solid_geometry:
|
||||
grb_final.solid_geometry.append(geos)
|
||||
grb_final.follow_geometry.append(geos)
|
||||
except TypeError:
|
||||
grb_final.solid_geometry.append(grb.solid_geometry)
|
||||
grb_final.follow_geometry.append(grb.solid_geometry)
|
||||
|
||||
for ap in grb.apertures:
|
||||
if ap not in grb_final.apertures:
|
||||
grb_final.apertures[ap] = grb.apertures[ap]
|
||||
else:
|
||||
if 'solid_geometry' not in grb_final.apertures[ap]:
|
||||
grb_final.apertures[ap]['solid_geometry'] = []
|
||||
for geo in grb.apertures[ap]['solid_geometry']:
|
||||
grb_final.apertures[ap]['solid_geometry'].append(geo)
|
||||
# create a list of integers out of the grb.apertures keys and find the max of that value
|
||||
# then, the aperture duplicate is assigned an id value incremented with 1,
|
||||
# and finally made string because the apertures dict keys are strings
|
||||
max_ap = str(max([int(k) for k in grb_final.apertures.keys()]) + 1)
|
||||
grb_final.apertures[max_ap] = {}
|
||||
grb_final.apertures[max_ap]['solid_geometry'] = []
|
||||
|
||||
for k, v in grb.apertures[ap].items():
|
||||
grb_final.apertures[max_ap][k] = v
|
||||
|
||||
grb_final.solid_geometry = MultiPolygon(grb_final.solid_geometry)
|
||||
grb_final.follow_geometry = MultiPolygon(grb_final.follow_geometry)
|
||||
|
@ -448,8 +458,6 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
"bboxmargin": 0.0,
|
||||
"bboxrounded": False,
|
||||
"aperture_display": False,
|
||||
"aperture_scale_factor": 1.0,
|
||||
"aperture_buffer_factor": 0.0,
|
||||
"follow": False
|
||||
})
|
||||
|
||||
|
@ -501,8 +509,6 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
"bboxmargin": self.ui.bbmargin_entry,
|
||||
"bboxrounded": self.ui.bbrounded_cb,
|
||||
"aperture_display": self.ui.aperture_table_visibility_cb,
|
||||
"aperture_scale_factor": self.ui.scale_aperture_entry,
|
||||
"aperture_buffer_factor": self.ui.buffer_aperture_entry,
|
||||
"follow": self.ui.follow_cb
|
||||
})
|
||||
|
||||
|
@ -522,9 +528,6 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
self.ui.generate_noncopper_button.clicked.connect(self.on_generatenoncopper_button_click)
|
||||
self.ui.aperture_table_visibility_cb.stateChanged.connect(self.on_aperture_table_visibility_change)
|
||||
self.ui.follow_cb.stateChanged.connect(self.on_follow_cb_click)
|
||||
self.ui.scale_aperture_button.clicked.connect(self.on_scale_aperture_click)
|
||||
self.ui.buffer_aperture_button.clicked.connect(self.on_buffer_aperture_click)
|
||||
self.ui.new_grb_button.clicked.connect(self.on_new_modified_gerber)
|
||||
|
||||
# Show/Hide Advanced Options
|
||||
if self.app.defaults["global_app_level"] == 'b':
|
||||
|
@ -896,7 +899,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
for g in geo_obj.solid_geometry:
|
||||
if g:
|
||||
app_obj.inform.emit(_(
|
||||
"[success]Isolation geometry created: %s"
|
||||
"[success] Isolation geometry created: %s"
|
||||
) % geo_obj.options["name"])
|
||||
break
|
||||
else:
|
||||
|
@ -951,7 +954,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
for g in geo_obj.solid_geometry:
|
||||
if g:
|
||||
app_obj.inform.emit(_(
|
||||
"[success]Isolation geometry created: %s"
|
||||
"[success] Isolation geometry created: %s"
|
||||
) % geo_obj.options["name"])
|
||||
break
|
||||
else:
|
||||
|
@ -989,30 +992,12 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
def on_aperture_table_visibility_change(self):
|
||||
if self.ui.aperture_table_visibility_cb.isChecked():
|
||||
self.ui.apertures_table.setVisible(True)
|
||||
self.ui.scale_aperture_label.setVisible(True)
|
||||
self.ui.scale_aperture_entry.setVisible(True)
|
||||
self.ui.scale_aperture_button.setVisible(True)
|
||||
|
||||
self.ui.buffer_aperture_label.setVisible(True)
|
||||
self.ui.buffer_aperture_entry.setVisible(True)
|
||||
self.ui.buffer_aperture_button.setVisible(True)
|
||||
|
||||
self.ui.new_grb_label.setVisible(True)
|
||||
self.ui.new_grb_button.setVisible(True)
|
||||
self.ui.mark_all_cb.setVisible(True)
|
||||
self.ui.mark_all_cb.setChecked(False)
|
||||
else:
|
||||
self.ui.apertures_table.setVisible(False)
|
||||
self.ui.scale_aperture_label.setVisible(False)
|
||||
self.ui.scale_aperture_entry.setVisible(False)
|
||||
self.ui.scale_aperture_button.setVisible(False)
|
||||
|
||||
self.ui.buffer_aperture_label.setVisible(False)
|
||||
self.ui.buffer_aperture_entry.setVisible(False)
|
||||
self.ui.buffer_aperture_button.setVisible(False)
|
||||
|
||||
self.ui.new_grb_label.setVisible(False)
|
||||
self.ui.new_grb_button.setVisible(False)
|
||||
self.ui.mark_all_cb.setVisible(False)
|
||||
|
||||
# on hide disable all mark plots
|
||||
|
@ -1020,136 +1005,6 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
self.ui.apertures_table.cellWidget(row, 5).set_value(False)
|
||||
self.clear_plot_apertures()
|
||||
|
||||
def on_scale_aperture_click(self, signal):
|
||||
try:
|
||||
factor = self.ui.scale_aperture_entry.get_value()
|
||||
except Exception as e:
|
||||
log.debug("FlatCAMGerber.on_scale_aperture_click() --> %s" % str(e))
|
||||
self.app.inform.emit(_(
|
||||
"[ERROR_NOTCL] The aperture scale factor value is missing or wrong format."
|
||||
))
|
||||
return
|
||||
|
||||
def scale_recursion(geom):
|
||||
if type(geom) == list or type(geom) is MultiPolygon:
|
||||
geoms=list()
|
||||
for local_geom in geom:
|
||||
geoms.append(scale_recursion(local_geom))
|
||||
return geoms
|
||||
else:
|
||||
return affinity.scale(geom, factor, factor, origin='center')
|
||||
|
||||
if not self.ui.apertures_table.selectedItems():
|
||||
self.app.inform.emit(_(
|
||||
"[WARNING_NOTCL] No aperture to scale. Select at least one aperture and try again."
|
||||
))
|
||||
return
|
||||
|
||||
for x in self.ui.apertures_table.selectedItems():
|
||||
try:
|
||||
apid = self.ui.apertures_table.item(x.row(), 1).text()
|
||||
except Exception as e:
|
||||
log.debug("FlatCAMGerber.on_scale_aperture_click() --> %s" % str(e))
|
||||
|
||||
self.apertures[apid]['solid_geometry'] = scale_recursion(self.apertures[apid]['solid_geometry'])
|
||||
|
||||
self.on_mark_cb_click_table()
|
||||
|
||||
def on_buffer_aperture_click(self, signal):
|
||||
try:
|
||||
buff_value = self.ui.buffer_aperture_entry.get_value()
|
||||
except Exception as e:
|
||||
log.debug("FlatCAMGerber.on_scale_aperture_click() --> %s" % str(e))
|
||||
self.app.inform.emit(_(
|
||||
"[ERROR_NOTCL] The aperture buffer value is missing or wrong format."
|
||||
))
|
||||
return
|
||||
|
||||
def buffer_recursion(geom):
|
||||
if type(geom) == list or type(geom) is MultiPolygon:
|
||||
geoms=list()
|
||||
for local_geom in geom:
|
||||
geoms.append(buffer_recursion(local_geom))
|
||||
return geoms
|
||||
else:
|
||||
return geom.buffer(buff_value, join_style=2)
|
||||
|
||||
if not self.ui.apertures_table.selectedItems():
|
||||
self.app.inform.emit(_(
|
||||
"[WARNING_NOTCL] No aperture to scale. Select at least one aperture and try again."
|
||||
))
|
||||
return
|
||||
|
||||
for x in self.ui.apertures_table.selectedItems():
|
||||
try:
|
||||
apid = self.ui.apertures_table.item(x.row(), 1).text()
|
||||
except Exception as e:
|
||||
log.debug("FlatCAMGerber.on_scale_aperture_click() --> %s" % str(e))
|
||||
|
||||
self.apertures[apid]['solid_geometry'] = buffer_recursion(self.apertures[apid]['solid_geometry'])
|
||||
|
||||
self.on_mark_cb_click_table()
|
||||
|
||||
def on_new_modified_gerber(self, signal):
|
||||
|
||||
name = '%s_ap_mod' % str(self.options['name'])
|
||||
apertures = deepcopy(self.apertures)
|
||||
options = self.options
|
||||
|
||||
# geometry storage
|
||||
poly_buff = []
|
||||
|
||||
# How the object should be initialized
|
||||
def obj_init(gerber_obj, app_obj):
|
||||
assert isinstance(gerber_obj, FlatCAMGerber), \
|
||||
"Expected to initialize a FlatCAMGerber but got %s" % type(gerber_obj)
|
||||
|
||||
gerber_obj.source_file = self.source_file
|
||||
gerber_obj.multigeo = False
|
||||
gerber_obj.follow = False
|
||||
|
||||
gerber_obj.apertures = apertures
|
||||
for option in options:
|
||||
# we don't want to overwrite the new name and we don't want to share the 'plot' state
|
||||
# because the new object should ve visible even if the source is not visible
|
||||
if option != 'name' and option != 'plot':
|
||||
gerber_obj.options[option] = options[option]
|
||||
|
||||
# regenerate solid_geometry
|
||||
app_obj.log.debug("Creating new Gerber object. Joining %s polygons.")
|
||||
# for ap in apertures:
|
||||
# for geo in apertures[ap]['solid_geometry']:
|
||||
# poly_buff.append(geo)
|
||||
poly_buff = [geo for ap in apertures for geo in apertures[ap]['solid_geometry']]
|
||||
|
||||
# buffering the poly_buff
|
||||
new_geo = MultiPolygon(poly_buff)
|
||||
new_geo = new_geo.buffer(0.0000001)
|
||||
new_geo = new_geo.buffer(-0.0000001)
|
||||
|
||||
gerber_obj.solid_geometry = new_geo
|
||||
|
||||
app_obj.log.debug("Finished creation of a new Gerber object. Polygons joined.")
|
||||
|
||||
log.debug("on_new_modified_gerber()")
|
||||
|
||||
with self.app.proc_container.new(_("Generating Gerber")) as proc:
|
||||
|
||||
self.app.progress.emit(10)
|
||||
|
||||
### Object creation ###
|
||||
ret = self.app.new_object("gerber", name, obj_init, autoselected=False)
|
||||
if ret == 'fail':
|
||||
self.app.inform.emit(_(
|
||||
'[ERROR_NOTCL] Cretion of Gerber failed.'
|
||||
))
|
||||
return
|
||||
|
||||
self.app.progress.emit(100)
|
||||
|
||||
# GUI feedback
|
||||
self.app.inform.emit(_("[success] Created: %s") % name)
|
||||
|
||||
def convert_units(self, units):
|
||||
"""
|
||||
Converts the units of the object by scaling dimensions in all geometry
|
||||
|
@ -1317,7 +1172,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
|
||||
aperture = self.ui.apertures_table.item(row, 1).text()
|
||||
# self.plot_apertures(color='#2d4606bf', marked_aperture=aperture, visible=True)
|
||||
self.plot_apertures(color='#FD6A02', marked_aperture=aperture, visible=True)
|
||||
self.plot_apertures(color=self.app.defaults['global_sel_draw_color'], marked_aperture=aperture, visible=True)
|
||||
else:
|
||||
self.marked_rows.append(False)
|
||||
|
||||
|
@ -1354,7 +1209,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
if mark_all:
|
||||
for aperture in self.apertures:
|
||||
# self.plot_apertures(color='#2d4606bf', marked_aperture=aperture, visible=True)
|
||||
self.plot_apertures(color='#FD6A02', marked_aperture=aperture, visible=True)
|
||||
self.plot_apertures(color=self.app.defaults['global_sel_draw_color'], marked_aperture=aperture, visible=True)
|
||||
else:
|
||||
self.clear_plot_apertures()
|
||||
|
||||
|
@ -1955,7 +1810,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|||
self.ui.tools_table.currentItem().text().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_(
|
||||
"[ERROR_NOTCL]Wrong value format entered, use a number."
|
||||
"[ERROR_NOTCL] Wrong value format entered, use a number."
|
||||
))
|
||||
self.ui.tools_table.currentItem().setText(str(self.tool_offset[dia]))
|
||||
return
|
||||
|
@ -2179,7 +2034,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|||
|
||||
if len(tools) == 0:
|
||||
self.app.inform.emit(_(
|
||||
"[ERROR_NOTCL]Please select one or more tools from the list and try again."
|
||||
"[ERROR_NOTCL] Please select one or more tools from the list and try again."
|
||||
))
|
||||
return False, "Error: No tools."
|
||||
|
||||
|
@ -2270,7 +2125,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|||
|
||||
if len(tools) == 0:
|
||||
self.app.inform.emit(_(
|
||||
"[ERROR_NOTCL]Please select one or more tools from the list and try again."
|
||||
"[ERROR_NOTCL] Please select one or more tools from the list and try again."
|
||||
))
|
||||
return False, "Error: No tools."
|
||||
|
||||
|
@ -2385,7 +2240,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|||
tools.append(self.ui.tools_table.item(0, 0).text())
|
||||
else:
|
||||
self.app.inform.emit(_(
|
||||
"[ERROR_NOTCL]Please select one or more tools from the list and try again."
|
||||
"[ERROR_NOTCL] Please select one or more tools from the list and try again."
|
||||
))
|
||||
return
|
||||
|
||||
|
@ -2409,6 +2264,8 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|||
|
||||
### Add properties to the object
|
||||
|
||||
job_obj.origin_kind = 'excellon'
|
||||
|
||||
job_obj.options['Tools_in_use'] = tool_table_items
|
||||
job_obj.options['type'] = 'Excellon'
|
||||
job_obj.options['ppname_e'] = pp_excellon_name
|
||||
|
@ -2443,7 +2300,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|||
except ValueError:
|
||||
self.app.inform.emit(
|
||||
_(
|
||||
'[ERROR_NOTCL]Wrong value format for self.defaults["z_pdepth"] or self.options["z_pdepth"]'
|
||||
'[ERROR_NOTCL] Wrong value format for self.defaults["z_pdepth"] or self.options["z_pdepth"]'
|
||||
))
|
||||
|
||||
try:
|
||||
|
@ -2455,7 +2312,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|||
except ValueError:
|
||||
self.app.inform.emit(
|
||||
_(
|
||||
'[ERROR_NOTCL]Wrong value format for self.defaults["feedrate_probe"] '
|
||||
'[ERROR_NOTCL] Wrong value format for self.defaults["feedrate_probe"] '
|
||||
'or self.options["feedrate_probe"]'
|
||||
)
|
||||
)
|
||||
|
@ -2608,7 +2465,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|||
# except TypeError: # Element is not iterable...
|
||||
# self.add_shape(shape=element, color=color, visible=visible, layer=0)
|
||||
|
||||
def plot(self):
|
||||
def plot(self, kind=None):
|
||||
|
||||
# Does all the required setup and returns False
|
||||
# if the 'ptint' option is set to False.
|
||||
|
@ -3218,7 +3075,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
)
|
||||
except ValueError:
|
||||
self.app.inform.emit(_(
|
||||
"[ERROR_NOTCL]Wrong value format entered, "
|
||||
"[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."
|
||||
)
|
||||
)
|
||||
|
@ -3439,7 +3296,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
else:
|
||||
change_message = False
|
||||
self.app.inform.emit(_(
|
||||
"[ERROR_NOTCL]Default Tool added. Wrong value format entered."
|
||||
"[ERROR_NOTCL] Default Tool added. Wrong value format entered."
|
||||
))
|
||||
self.build_ui()
|
||||
|
||||
|
@ -3469,7 +3326,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
self.tools[int(max_uid)] = deepcopy(self.tools[tooluid_copy])
|
||||
except AttributeError:
|
||||
self.app.inform.emit(_(
|
||||
"[WARNING_NOTCL]Failed. Select a tool to copy."
|
||||
"[WARNING_NOTCL] Failed. Select a tool to copy."
|
||||
))
|
||||
self.build_ui()
|
||||
return
|
||||
|
@ -3479,7 +3336,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
# self.ui.geo_tools_table.clearSelection()
|
||||
else:
|
||||
self.app.inform.emit(_(
|
||||
"[WARNING_NOTCL]Failed. Select a tool to copy."
|
||||
"[WARNING_NOTCL] Failed. Select a tool to copy."
|
||||
))
|
||||
self.build_ui()
|
||||
return
|
||||
|
@ -3524,7 +3381,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
d = float(self.ui.geo_tools_table.item(current_row, 1).text().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_(
|
||||
"[ERROR_NOTCL]Wrong value format entered, "
|
||||
"[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."
|
||||
))
|
||||
return
|
||||
|
@ -3572,7 +3429,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
temp_tools.clear()
|
||||
except AttributeError:
|
||||
self.app.inform.emit(_(
|
||||
"[WARNING_NOTCL]Failed. Select a tool to delete."
|
||||
"[WARNING_NOTCL] Failed. Select a tool to delete."
|
||||
))
|
||||
self.build_ui()
|
||||
return
|
||||
|
@ -3582,7 +3439,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
# self.ui.geo_tools_table.clearSelection()
|
||||
else:
|
||||
self.app.inform.emit(_(
|
||||
"[WARNING_NOTCL]Failed. Select a tool to delete."
|
||||
"[WARNING_NOTCL] Failed. Select a tool to delete."
|
||||
))
|
||||
self.build_ui()
|
||||
return
|
||||
|
@ -3711,7 +3568,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
vdia = float(self.ui.tipdia_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_(
|
||||
"[ERROR_NOTCL]Wrong value format entered, "
|
||||
"[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."
|
||||
))
|
||||
return
|
||||
|
@ -3724,7 +3581,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
half_vangle = float(self.ui.tipangle_entry.get_value().replace(',', '.')) / 2
|
||||
except ValueError:
|
||||
self.app.inform.emit(_(
|
||||
"[ERROR_NOTCL]Wrong value format entered, "
|
||||
"[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."
|
||||
))
|
||||
return
|
||||
|
@ -3841,7 +3698,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
)
|
||||
except ValueError:
|
||||
self.app.inform.emit(_(
|
||||
"[ERROR_NOTCL]Wrong value format entered, "
|
||||
"[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."
|
||||
))
|
||||
return
|
||||
|
@ -4020,7 +3877,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
try:
|
||||
if self.special_group:
|
||||
self.app.inform.emit(_(
|
||||
"[WARNING_NOTCL]This Geometry can't be processed because it is %s geometry."
|
||||
"[WARNING_NOTCL] This Geometry can't be processed because it is %s geometry."
|
||||
) % str(self.special_group))
|
||||
return
|
||||
except AttributeError:
|
||||
|
@ -4037,7 +3894,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
tooldia = float(self.ui.geo_tools_table.item(x.row(), 1).text().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_(
|
||||
"[ERROR_NOTCL]Wrong Tool Dia value format entered, "
|
||||
"[ERROR_NOTCL] Wrong Tool Dia value format entered, "
|
||||
"use a number."
|
||||
))
|
||||
return
|
||||
|
@ -4137,7 +3994,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
except ValueError:
|
||||
self.app.inform.emit(
|
||||
_(
|
||||
'[ERROR_NOTCL]Wrong value format for self.defaults["z_pdepth"] or self.options["z_pdepth"]'
|
||||
'[ERROR_NOTCL] Wrong value format for self.defaults["z_pdepth"] or self.options["z_pdepth"]'
|
||||
))
|
||||
|
||||
try:
|
||||
|
@ -4149,7 +4006,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
except ValueError:
|
||||
self.app.inform.emit(
|
||||
_(
|
||||
'[ERROR_NOTCL]Wrong value format for self.defaults["feedrate_probe"] '
|
||||
'[ERROR_NOTCL] Wrong value format for self.defaults["feedrate_probe"] '
|
||||
'or self.options["feedrate_probe"]'
|
||||
))
|
||||
|
||||
|
@ -4249,7 +4106,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
)
|
||||
except ValueError:
|
||||
self.app.inform.emit(_(
|
||||
"[ERROR_NOTCL]Wrong value format entered, "
|
||||
"[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."
|
||||
))
|
||||
return
|
||||
|
@ -4348,7 +4205,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
except ValueError:
|
||||
self.app.inform.emit(
|
||||
_(
|
||||
'[ERROR_NOTCL]Wrong value format for self.defaults["z_pdepth"] or self.options["z_pdepth"]'
|
||||
'[ERROR_NOTCL] Wrong value format for self.defaults["z_pdepth"] or self.options["z_pdepth"]'
|
||||
))
|
||||
|
||||
try:
|
||||
|
@ -4360,7 +4217,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
except ValueError:
|
||||
self.app.inform.emit(
|
||||
_(
|
||||
'[ERROR_NOTCL]Wrong value format for self.defaults["feedrate_probe"] '
|
||||
'[ERROR_NOTCL] Wrong value format for self.defaults["feedrate_probe"] '
|
||||
'or self.options["feedrate_probe"]'
|
||||
))
|
||||
|
||||
|
@ -4372,7 +4229,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
a += 1
|
||||
if a == len(self.tools):
|
||||
self.app.inform.emit(_(
|
||||
'[ERROR_NOTCL]Cancelled. Empty file, it has no geometry...'
|
||||
'[ERROR_NOTCL] Cancelled. Empty file, it has no geometry...'
|
||||
))
|
||||
return 'fail'
|
||||
|
||||
|
@ -4482,7 +4339,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
)
|
||||
except ValueError:
|
||||
self.app.inform.emit(_(
|
||||
"[ERROR_NOTCL]Wrong value format entered, "
|
||||
"[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."
|
||||
))
|
||||
return
|
||||
|
@ -4552,12 +4409,12 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
if self.solid_geometry:
|
||||
with self.app.proc_container.new(_("Generating CNC Code")):
|
||||
if app_obj.new_object("cncjob", outname, job_init_single_geometry) != 'fail':
|
||||
app_obj.inform.emit("[success]CNCjob created: %s" % outname)
|
||||
app_obj.inform.emit("[success] CNCjob created: %s" % outname)
|
||||
app_obj.progress.emit(100)
|
||||
else:
|
||||
with self.app.proc_container.new(_("Generating CNC Code")):
|
||||
if app_obj.new_object("cncjob", outname, job_init_multi_geometry) != 'fail':
|
||||
app_obj.inform.emit("[success]CNCjob created: %s" % outname)
|
||||
app_obj.inform.emit("[success] CNCjob created: %s" % outname)
|
||||
app_obj.progress.emit(100)
|
||||
|
||||
# Create a promise with the name
|
||||
|
@ -4663,7 +4520,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
except ValueError:
|
||||
self.app.inform.emit(
|
||||
_(
|
||||
'[ERROR_NOTCL]Wrong value format for self.defaults["z_pdepth"] or self.options["z_pdepth"]'
|
||||
'[ERROR_NOTCL] Wrong value format for self.defaults["z_pdepth"] or self.options["z_pdepth"]'
|
||||
))
|
||||
|
||||
try:
|
||||
|
@ -4675,7 +4532,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
except ValueError:
|
||||
self.app.inform.emit(
|
||||
_(
|
||||
'[ERROR_NOTCL]Wrong value format for self.defaults["feedrate_probe"] '
|
||||
'[ERROR_NOTCL] Wrong value format for self.defaults["feedrate_probe"] '
|
||||
'or self.options["feedrate_probe"]'
|
||||
))
|
||||
|
||||
|
@ -4703,7 +4560,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
def job_thread(app_obj):
|
||||
with self.app.proc_container.new(_("Generating CNC Code")):
|
||||
app_obj.new_object("cncjob", outname, job_init)
|
||||
app_obj.inform.emit("[success]CNCjob created: %s" % outname)
|
||||
app_obj.inform.emit("[success] CNCjob created: %s" % outname)
|
||||
app_obj.progress.emit(100)
|
||||
|
||||
# Create a promise with the name
|
||||
|
@ -4764,7 +4621,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
# else:
|
||||
# self.solid_geometry = affinity.scale(self.solid_geometry, xfactor, yfactor,
|
||||
# origin=(px, py))
|
||||
# self.app.inform.emit("[success]Geometry Scale done.")
|
||||
# self.app.inform.emit("[success] Geometry Scale done.")
|
||||
|
||||
def scale_recursion(geom):
|
||||
if type(geom) == list:
|
||||
|
@ -4782,7 +4639,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
self.solid_geometry=scale_recursion(self.solid_geometry)
|
||||
|
||||
self.app.inform.emit(_(
|
||||
"[success]Geometry Scale done."
|
||||
"[success] Geometry Scale done."
|
||||
))
|
||||
|
||||
def offset(self, vect):
|
||||
|
@ -4799,7 +4656,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
dx, dy = vect
|
||||
except TypeError:
|
||||
self.app.inform.emit(_(
|
||||
"[ERROR_NOTCL]An (x,y) pair of values are needed. "
|
||||
"[ERROR_NOTCL] An (x,y) pair of values are needed. "
|
||||
"Probable you entered only one value in the Offset field."
|
||||
))
|
||||
return
|
||||
|
@ -4819,7 +4676,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
else:
|
||||
self.solid_geometry=translate_recursion(self.solid_geometry)
|
||||
self.app.inform.emit(_(
|
||||
"[success]Geometry Offset done."
|
||||
"[success] Geometry Offset done."
|
||||
))
|
||||
|
||||
def convert_units(self, units):
|
||||
|
@ -4888,7 +4745,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
)
|
||||
except ValueError:
|
||||
self.app.inform.emit(_(
|
||||
"[ERROR_NOTCL]Wrong value format entered, "
|
||||
"[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."
|
||||
))
|
||||
return
|
||||
|
@ -4948,11 +4805,14 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
except TypeError: # Element is not iterable...
|
||||
self.add_shape(shape=element, color=color, visible=visible, layer=0)
|
||||
|
||||
def plot(self, visible=None):
|
||||
def plot(self, visible=None, kind=None):
|
||||
"""
|
||||
Adds the object into collection.
|
||||
Plot the object.
|
||||
|
||||
:return: None
|
||||
:param visible: Controls if the added shape is visible of not
|
||||
:param kind: added so there is no error when a project is loaded and it has both geometry and CNCJob, because
|
||||
CNCJob require the 'kind' parameter. Perhaps the FlatCAMObj.plot() has to be rewrited
|
||||
:return:
|
||||
"""
|
||||
|
||||
# Does all the required setup and returns False
|
||||
|
@ -5305,7 +5165,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
|||
|
||||
self.ui.updateplot_button.clicked.connect(self.on_updateplot_button_click)
|
||||
self.ui.export_gcode_button.clicked.connect(self.on_exportgcode_button_click)
|
||||
self.ui.modify_gcode_button.clicked.connect(self.on_modifygcode_button_click)
|
||||
self.ui.modify_gcode_button.clicked.connect(self.on_edit_code_click)
|
||||
|
||||
self.ui.tc_variable_combo.currentIndexChanged[str].connect(self.on_cnc_custom_parameters)
|
||||
|
||||
|
@ -5372,7 +5232,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
|||
|
||||
if filename == '':
|
||||
self.app.inform.emit(_(
|
||||
"[WARNING_NOTCL]Export Machine Code cancelled ..."))
|
||||
"[WARNING_NOTCL] Export Machine Code cancelled ..."))
|
||||
return
|
||||
|
||||
preamble = str(self.ui.prepend_text.get_value())
|
||||
|
@ -5385,7 +5245,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
|||
self.app.file_saved.emit("gcode", filename)
|
||||
self.app.inform.emit(_("[success] Machine Code file saved to: %s") % filename)
|
||||
|
||||
def on_modifygcode_button_click(self, *args):
|
||||
def on_edit_code_click(self, *args):
|
||||
preamble = str(self.ui.prepend_text.get_value())
|
||||
postamble = str(self.ui.append_text.get_value())
|
||||
gc = self.export_gcode(preamble=preamble, postamble=postamble, to_file=True)
|
||||
|
@ -5394,18 +5254,9 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
|||
else:
|
||||
self.app.gcode_edited = gc
|
||||
|
||||
# add the tab if it was closed
|
||||
self.app.ui.plot_tab_area.addTab(self.app.ui.cncjob_tab, _("Code Editor"))
|
||||
|
||||
# delete the absolute and relative position and messages in the infobar
|
||||
self.app.ui.position_label.setText("")
|
||||
self.app.ui.rel_position_label.setText("")
|
||||
|
||||
# Switch plot_area to CNCJob tab
|
||||
self.app.ui.plot_tab_area.setCurrentWidget(self.app.ui.cncjob_tab)
|
||||
|
||||
# first clear previous text in text editor (if any)
|
||||
self.app.ui.code_editor.clear()
|
||||
self.app.init_code_editor(name=_("Code Editor"))
|
||||
self.app.ui.buttonOpen.clicked.connect(self.app.handleOpen)
|
||||
self.app.ui.buttonSave.clicked.connect(self.app.handleSaveGCode)
|
||||
|
||||
# then append the text from GCode to the text editor
|
||||
try:
|
||||
|
@ -5413,8 +5264,8 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
|||
proc_line = str(line).strip('\n')
|
||||
self.app.ui.code_editor.append(proc_line)
|
||||
except Exception as e:
|
||||
log.debug('FlatCAMCNNJob.on_modifygcode_button_click() -->%s' % str(e))
|
||||
self.app.inform.emit(_('[ERROR]FlatCAMCNNJob.on_modifygcode_button_click() -->%s') % str(e))
|
||||
log.debug('FlatCAMCNNJob.on_edit_code_click() -->%s' % str(e))
|
||||
self.app.inform.emit(_('[ERROR]FlatCAMCNNJob.on_edit_code_click() -->%s') % str(e))
|
||||
return
|
||||
|
||||
self.app.ui.code_editor.moveCursor(QtGui.QTextCursor.Start)
|
||||
|
@ -5513,6 +5364,17 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
|||
|
||||
return gcode
|
||||
|
||||
def gcode_footer(self, end_command=None):
|
||||
"""
|
||||
|
||||
:param end_command: 'M02' or 'M30' - String
|
||||
:return:
|
||||
"""
|
||||
if end_command:
|
||||
return end_command
|
||||
else:
|
||||
return 'M02'
|
||||
|
||||
def export_gcode(self, filename=None, preamble='', postamble='', to_file=False):
|
||||
gcode = ''
|
||||
roland = False
|
||||
|
@ -5520,7 +5382,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
|||
|
||||
try:
|
||||
if self.special_group:
|
||||
self.app.inform.emit(_("[WARNING_NOTCL]This CNCJob object can't be processed because "
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] This CNCJob object can't be processed because "
|
||||
"it is a %s CNCJob object.") % str(self.special_group))
|
||||
return 'fail'
|
||||
except AttributeError:
|
||||
|
@ -5577,7 +5439,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
|||
))
|
||||
return
|
||||
|
||||
g = gcode[:g_idx] + preamble + '\n' + gcode[g_idx:] + postamble
|
||||
g = gcode[:g_idx] + preamble + '\n' + gcode[g_idx:] + postamble + self.gcode_footer()
|
||||
|
||||
# if toolchange custom is used, replace M6 code with the code from the Toolchange Custom Text box
|
||||
if self.ui.toolchange_cb.get_value() is True:
|
||||
|
@ -5711,7 +5573,6 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
|||
self.ui.plot_cb.setChecked(True)
|
||||
self.ui_connect()
|
||||
|
||||
|
||||
def plot(self, visible=None, kind='all'):
|
||||
|
||||
# Does all the required setup and returns False
|
||||
|
|
|
@ -86,12 +86,14 @@ def on_language_apply_click(app, restart=False):
|
|||
msgbox.setInformativeText("Are you sure do you want to change the current language to %s?" % name.capitalize())
|
||||
msgbox.setWindowTitle("Apply Language ...")
|
||||
msgbox.setWindowIcon(QtGui.QIcon('share/language32.png'))
|
||||
msgbox.setStandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.Cancel)
|
||||
msgbox.setDefaultButton(QtWidgets.QMessageBox.Yes)
|
||||
bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole)
|
||||
bt_no = msgbox.addButton(_('No'), QtWidgets.QMessageBox.NoRole)
|
||||
|
||||
response = msgbox.exec_()
|
||||
msgbox.setDefaultButton(bt_yes)
|
||||
msgbox.exec_()
|
||||
response = msgbox.clickedButton()
|
||||
|
||||
if response == QtWidgets.QMessageBox.Cancel:
|
||||
if response == bt_no:
|
||||
return
|
||||
else:
|
||||
settings = QSettings("Open Source", "FlatCAM")
|
||||
|
@ -112,6 +114,12 @@ def apply_language(domain, lang=None):
|
|||
name = settings.value('language')
|
||||
else:
|
||||
name = settings.value('English')
|
||||
# in case the 'language' parameter is not in QSettings add it to QSettings and it's value is
|
||||
# the default language, English
|
||||
settings.setValue('language', 'English')
|
||||
|
||||
# This will write the setting to the platform specific storage.
|
||||
del settings
|
||||
else:
|
||||
name = str(lang)
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ class WorkerStack(QtCore.QObject):
|
|||
worker_task = QtCore.pyqtSignal(dict) # 'worker_name', 'func', 'params'
|
||||
thread_exception = QtCore.pyqtSignal(object)
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, workers_number):
|
||||
super(WorkerStack, self).__init__()
|
||||
|
||||
self.workers = []
|
||||
|
@ -16,7 +16,7 @@ class WorkerStack(QtCore.QObject):
|
|||
self.load = {} # {'worker_name': tasks_count}
|
||||
|
||||
# Create workers crew
|
||||
for i in range(0, 2):
|
||||
for i in range(0, workers_number):
|
||||
worker = Worker(self, 'Slogger-' + str(i))
|
||||
thread = QtCore.QThread()
|
||||
|
||||
|
|
|
@ -240,6 +240,9 @@ class ObjectCollection(QtCore.QAbstractItemModel):
|
|||
# tasks know that they have to wait until available.
|
||||
self.promises = set()
|
||||
|
||||
# same as above only for objects that are plotted
|
||||
self.plot_promises = set()
|
||||
|
||||
self.app = app
|
||||
|
||||
### View
|
||||
|
@ -275,6 +278,16 @@ class ObjectCollection(QtCore.QAbstractItemModel):
|
|||
def has_promises(self):
|
||||
return len(self.promises) > 0
|
||||
|
||||
def plot_promise(self, plot_obj_name):
|
||||
self.plot_promises.add(plot_obj_name)
|
||||
|
||||
def plot_remove_promise(self, plot_obj_name):
|
||||
if plot_obj_name in self.plot_promises:
|
||||
self.plot_promises.remove(plot_obj_name)
|
||||
|
||||
def has_plot_promises(self):
|
||||
return len(self.plot_promises) > 0
|
||||
|
||||
def on_mouse_down(self, event):
|
||||
FlatCAMApp.App.log.debug("Mouse button pressed on list")
|
||||
|
||||
|
@ -394,6 +407,7 @@ class ObjectCollection(QtCore.QAbstractItemModel):
|
|||
self.app.myKeywords.remove(old_name)
|
||||
self.app.myKeywords.append(new_name)
|
||||
self.app.shell._edit.set_model_data(self.app.myKeywords)
|
||||
self.app.ui.code_editor.set_model_data(self.app.myKeywords)
|
||||
except:
|
||||
log.debug(
|
||||
"setData() --> Could not remove the old object name from auto-completer model list")
|
||||
|
@ -550,6 +564,7 @@ class ObjectCollection(QtCore.QAbstractItemModel):
|
|||
try:
|
||||
self.app.myKeywords.remove(name)
|
||||
self.app.shell._edit.set_model_data(self.app.myKeywords)
|
||||
self.app.ui.code_editor.set_model_data(self.app.myKeywords)
|
||||
except:
|
||||
log.debug(
|
||||
"delete_active() --> Could not remove the old object name from auto-completer model list")
|
||||
|
|
178
README.md
|
@ -9,6 +9,184 @@ CAD program, and create G-Code for Isolation routing.
|
|||
|
||||
=================================================
|
||||
|
||||
13.04.2019
|
||||
|
||||
- updating the German translation
|
||||
- Gerber Editor: added ability to change on the fly the aperture after one of the tools: Add Pad or Add Pad Array is activated
|
||||
- Gerber Editor: if a tool is cancelled via key shortcut ESCAPE, the selection is now deleted and any other action require a new selection
|
||||
- finished German translation (Google translated with some adjustments)
|
||||
- final fix for issue #277. Previous fix was applied only for one case out of three.
|
||||
- RELEASE 8.913
|
||||
|
||||
12.04.2019
|
||||
|
||||
- Gerber Editor: added support for Oblong type of aperture
|
||||
- fixed an issue with automatically filled in aperture code when the edited Gerber file has no apertures; established an default with value 10 (according to Gerber specifications)
|
||||
- fixed a bug in editing a blank Gerber object
|
||||
- added handlers for the Gerber Editor context menu
|
||||
- updated the translation template POT file and the EN PO/MO files
|
||||
- Gerber Editor: added toggle effect to the Transform Tool
|
||||
- Gerber Editor: added shortcut for Transform Tool and also toggle effect here, too
|
||||
- updated the shortcut list with the Gerber Editor shortcut keys
|
||||
- Gerber Editor: fixed error when adding an aperture with code value lower than the ones that already exists
|
||||
- when adding an aperture with code '0' (zero) it will automatically be set with size zero and type: 'REG' (from region); here we store all the regions from a Gerber file, the ones without a declared aperture
|
||||
- Gerber Editor: added support for Gerber polarity change commands (LPD, LPC)
|
||||
- moved the polarity change processing from FlatCAMGrbEditor() class to camlib.Gerber().parse_lines()
|
||||
- made optional the saving of an edited object. Now the user can cancel the changes to the object.
|
||||
- replaced the standard buttons in the QMessageBox's used in the app with custom ones that can have text translated
|
||||
- updated the POT translation file and the MO/PO files for English and Romanian language
|
||||
|
||||
11.04.2019
|
||||
|
||||
- changed the color of the marked apertures to the global_selection_color
|
||||
- Gerber Editor: added Transformation Tool and Rotation key shortcut
|
||||
- in all Editors, manually deactivating a button in the editor toolbar will automatically select the 'Select' button
|
||||
- fixed Excellon Editor selection: when a tool is selected in Tools Table, all the drills belonging to that tool are selected. When a drill is selected on canvas, the associated tool will be selected without automatically selecting all other drills with same tool
|
||||
- Gerber Editor: added Add Pad Array tool
|
||||
- Gerber Editor: in Add Pad Array tool, if the pad is not circular type, for circular array the pad will be rotated to match the array angle
|
||||
- Gerber Editor: fixed multiple selection with key modifier such that first click selects, second deselects
|
||||
|
||||
10.04.2019
|
||||
|
||||
- Gerber Editor: added Add Track and Add Region functions
|
||||
- Gerber Editor: fixed key shortcuts
|
||||
- fixed setting the Layout combobox in Preferences according to the current layout
|
||||
- created menu links and shortcut keys for adding a new empty Gerber objects; on update of the edited Gerber, if the source object was an empty one (new blank one) this source obj will be deleted
|
||||
- removed the old apertures editing from Gerber Obj selected tab
|
||||
- Gerber Editor: added Add Pad (circular or rectangular type only)
|
||||
- Gerber Editor: autoincrement aperture code when adding new apertures
|
||||
- Gerber Editor: automatically calculate the size of the rectangular aperture
|
||||
|
||||
9.04.2019
|
||||
|
||||
- Gerber Editor: added buffer and scale tools
|
||||
- Gerber Editor: working on aperture selection to show on Aperture Table
|
||||
- Gerber Editor: finished the selection on canvas; should be used as an template for the other Editors
|
||||
- Gerber Editor: finished the Copy, Aperture Add, Buffer, Scale, Move including the Utility geometry
|
||||
- Trying to fix bug in Measurement Tool: the mouse events don't disconnect
|
||||
- fixed above bug in Measurement Tool (but there is a TODO there)
|
||||
|
||||
7.04.2019
|
||||
|
||||
- default values for Jump To function is jumping to origin (0, 0)
|
||||
|
||||
6.04.2019
|
||||
|
||||
- fixed bug in Geometry Editor in buffer_int() function that created an Circular Reference Error when applying buffer interior on a geometry.
|
||||
- fixed issue with not possible to close the app after a project save.
|
||||
- preliminary Gerber Editor.on_aperture_delete()
|
||||
- fixed 'circular reference' error when creating the new Gerber file in Gerber Editor
|
||||
- preliminary Gerber Editor.on_aperture_add()
|
||||
|
||||
5.04.2019
|
||||
|
||||
- Gerber Editor: made geometry transfer (which is slow) to Editor to be multithreaded
|
||||
- Gerber Editor: plotting process is showed in the status bar
|
||||
- increased the number of workers in FlatCAM and made the number of workers customizable from Preferences
|
||||
- WIP in Gerber Editor: geometry is no longer stored in a Rtree storage as it is not needed
|
||||
- changed the way delayed plot is working in Gerber Editor to use a Qtimer instead of python threading module
|
||||
- WIP in Gerber Editor
|
||||
- fixed bug in saving the maximized state
|
||||
- fixed bug in applying default language on first start
|
||||
~~- on activating 'V' key shortcut (zoom fit) the mouse cursor is now jumping to origin (0, 0)~~
|
||||
- fixed bug in saving toolbars state; the file was saved before setting the self.defaults['global_toolbar_view]
|
||||
|
||||
4.04.2019
|
||||
|
||||
- added support for Gerber format specification D (no zero suppression) - PCBWizard Gerber files support
|
||||
- added support for Excellon file with no info about tool diameters - PCB Wizard Excellon file support
|
||||
- modified the bogus diameters series for Excellon objects that do not have tool diameter info
|
||||
- made Excellon Editor aware of the fact that the Excellon object that is edited has fake (bogus) tool diameters and therefore it will not sort the tools based on diameter but based on tool number
|
||||
- fixed bug on Excellon Editor: when diameter is edited in Tools Table and the target diameter is already in the tool table, the drills from current tool are moved to the new tool (with new dia) - before it crashed
|
||||
- fixed offset after editing drill diameters in Excellon Editor.
|
||||
|
||||
3.04.2019
|
||||
|
||||
- fixed plotting in Gerber Editor
|
||||
- working on GUI in Gerber Editor
|
||||
- added a Gcode end_command: default is M02
|
||||
- modified the calling of the editor2object() slot function to fix an issue with updating geometry imported from SVG file, after edit
|
||||
- working on Gerber Editor - added the key shortcuts: wip
|
||||
- made saving of the project file non-blocking and also while saving the project file, if the user tries again to close the app while project file is being saved, the app will close only after saving is complete (the project file size is non zero)
|
||||
- fixed the camlib.Geometry.import_svg() and camlib.Gerber.bounds() to work when importing SVG files as Gerber
|
||||
|
||||
31.03.2019
|
||||
|
||||
- fixed issue #281 by making generation of a convex shape for the freeform cutout in Tool Cutout a choice rather than the default
|
||||
- fixed bug in Tool Cutout, now in manual cutout mode the gap size reflect the value set
|
||||
- changed Measuring Tool to use the mouse click release instead of mouse click press; also fixed a bug when using the ESC key.
|
||||
- fixed errors when the File -> New Project is initiated while an Editor is still active.
|
||||
- the File->Exit action handler is now self.final_save()
|
||||
- wip in Gerber editor
|
||||
|
||||
29.03.2019
|
||||
|
||||
- update the TCL keyword list
|
||||
- fix on the Gerber parser that makes searching for '%%' char optional when doing regex search for mode, units or image polarity. This allow loading Gerber files generated by the ECAD software TCl4.4
|
||||
- fix error in plotting Excellon when toggling units
|
||||
- FlatCAM editors now are separated each in it's own file
|
||||
- fixed TextTool in Geometry Editor so it will open the notebook on activation and close it after finishing text adding
|
||||
- started to work on a Gerber Editor
|
||||
- added a fix in the Excellon parser by allowing a comma in the tool definitions between the diameter and the rest
|
||||
|
||||
28.03.2019
|
||||
|
||||
- About 45% progress in German translation
|
||||
- new feature: added ability to edit MultiGeo geometry (geometry from Paint Tool)
|
||||
- changed all the info messages that are of type warning, error or success so they have a space added after the keyword
|
||||
- changed the Romanian translation by adding more diacritics
|
||||
- modified Gerber parser to copy the follow_geometry in the self.apertures
|
||||
- modified the Properties Tool to show the number of elements in the follow_geometry for each aperture
|
||||
- modified the copy functions to copy the follow_geometry and also the apertures if it's possible (only for Gerber objects)
|
||||
|
||||
27.03.2019
|
||||
|
||||
- added new feature: user can delete apertures in Advanced mode and then create a new FlatCAM Gerber object
|
||||
- progress in German translation. About 27% done.
|
||||
- fixed issue #278. Crash on name change in the Name field in the Selected Tab.
|
||||
|
||||
26.03.2019
|
||||
|
||||
- fixed an issue where the Geometry plot function protested that it does not have an parameter that is used by the CNCJob plot function. But both inherit from FaltCAMObj plot function which does not have that parameter so something may need to be changed. Until then I provided a phony keyboard parameter to make that function 'shut up'
|
||||
- fixed bug: after using Paint Tool shortcut keys are disabled
|
||||
- added CNCJob geometry for the holes created by the drills from Excellon objects
|
||||
|
||||
25.03.2019
|
||||
|
||||
- in the TCL completer if the word is already complete don't add it again but add a space
|
||||
- added all the TCL keywords in the completer keyword list
|
||||
- work in progress in German translation ~7%
|
||||
- after any autocomplete in TCL completer, a space is added
|
||||
- fixed an module import issue in NCC Tool
|
||||
- minor change (optimization) of the CNCJob UI
|
||||
- work in progress in German translation ~20%
|
||||
|
||||
22.03.2019
|
||||
|
||||
- fixed an error that created a situation that when saving a project with some of the CNCJob objects disabled, on project reload the CNCJob objects are no longer loaded
|
||||
- fixed the Gerber.merge() function. When some of the Gerber files have apertures with same id, create a new aperture id for the object that is fused because each aperture id may hold different geometries.
|
||||
- changed the autoname for saving Preferences, Project and PNG file
|
||||
|
||||
20.03.2019
|
||||
|
||||
- added autocomplete finish with ENTER key for the TCL Shell
|
||||
- made sure that the autocomplete function works only for FlatCAM Scripts
|
||||
- ESC key will trigger normal view if in full screen and the ESC key is pressed
|
||||
- added an icon and title text for the Toggle Units QMessageBox
|
||||
|
||||
19.03.2019
|
||||
|
||||
- added autocomplete for Code editor;
|
||||
- autocomplete in Code Editor is finished by hitting either TAB key or ENTER key
|
||||
- fixed the Gerber.merge() to work for the case when one of the merged Gerber objects solid_geometry type is Polygon and not a list
|
||||
|
||||
18.03.2019
|
||||
|
||||
- added ability to create new scripts and open scripts in FlatCAM Script Editor
|
||||
- the Code Editor tab name is changed according to the task; 'save' and 'open' buttons will have filters installed for the QOpenDialog fit to the task
|
||||
- added ability to run a FlatCAM Tcl script by double-clicking on the file
|
||||
- in Code Editor added shortcut combo key CTRL+SHIFT+V to function as a Special Paste that will replace the '\' char with '/' so the Windows paths will be pasted correctly for TCL Shell. Also doing SHIFT + LMB on the Paste in contextual menu is doing the same.
|
||||
|
||||
17.03.2019
|
||||
|
||||
- remade the layout in 2Sided Tool
|
||||
|
|
485
camlib.py
|
@ -621,7 +621,6 @@ class Geometry(object):
|
|||
|
||||
# flatten the self.solid_geometry list for import_svg() to import SVG as Gerber
|
||||
self.solid_geometry = list(self.flatten_list(self.solid_geometry))
|
||||
self.solid_geometry = cascaded_union(self.solid_geometry)
|
||||
|
||||
geos_text = getsvgtext(svg_root, object_type, units=units)
|
||||
if geos_text is not None:
|
||||
|
@ -632,7 +631,8 @@ class Geometry(object):
|
|||
_, minimy, _, maximy = i.bounds
|
||||
h2 = (maximy - minimy) * 0.5
|
||||
geos_text_f.append(translate(scale(i, 1.0, -1.0, origin=(0, 0)), yoff=(h + h2)))
|
||||
self.solid_geometry = [self.solid_geometry, geos_text_f]
|
||||
if geos_text_f:
|
||||
self.solid_geometry = self.solid_geometry + geos_text_f
|
||||
|
||||
def import_dxf(self, filename, object_type=None, units='MM'):
|
||||
"""
|
||||
|
@ -1384,7 +1384,7 @@ class Geometry(object):
|
|||
self.tools[tool]['solid_geometry'] = mirror_geom(self.tools[tool]['solid_geometry'])
|
||||
else:
|
||||
self.solid_geometry = mirror_geom(self.solid_geometry)
|
||||
self.app.inform.emit(_('[success]Object was mirrored ...'))
|
||||
self.app.inform.emit(_('[success] Object was mirrored ...'))
|
||||
except AttributeError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Failed to mirror. No object selected"))
|
||||
|
||||
|
@ -1422,7 +1422,7 @@ class Geometry(object):
|
|||
self.tools[tool]['solid_geometry'] = rotate_geom(self.tools[tool]['solid_geometry'])
|
||||
else:
|
||||
self.solid_geometry = rotate_geom(self.solid_geometry)
|
||||
self.app.inform.emit(_('[success]Object was rotated ...'))
|
||||
self.app.inform.emit(_('[success] Object was rotated ...'))
|
||||
except AttributeError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Failed to rotate. No object selected"))
|
||||
|
||||
|
@ -1458,7 +1458,7 @@ class Geometry(object):
|
|||
self.tools[tool]['solid_geometry'] = skew_geom(self.tools[tool]['solid_geometry'])
|
||||
else:
|
||||
self.solid_geometry = skew_geom(self.solid_geometry)
|
||||
self.app.inform.emit(_('[success]Object was skewed ...'))
|
||||
self.app.inform.emit(_('[success] Object was skewed ...'))
|
||||
except AttributeError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Failed to skew. No object selected"))
|
||||
|
||||
|
@ -1951,14 +1951,14 @@ class Gerber (Geometry):
|
|||
#### Parser patterns ####
|
||||
# FS - Format Specification
|
||||
# The format of X and Y must be the same!
|
||||
# L-omit leading zeros, T-omit trailing zeros
|
||||
# L-omit leading zeros, T-omit trailing zeros, D-no zero supression
|
||||
# A-absolute notation, I-incremental notation
|
||||
self.fmt_re = re.compile(r'%FS([LT])([AI])X(\d)(\d)Y\d\d\*%$')
|
||||
self.fmt_re = re.compile(r'%?FS([LTD])([AI])X(\d)(\d)Y\d\d\*%?$')
|
||||
self.fmt_re_alt = re.compile(r'%FS([LT])([AI])X(\d)(\d)Y\d\d\*MO(IN|MM)\*%$')
|
||||
self.fmt_re_orcad = re.compile(r'(G\d+)*\**%FS([LT])([AI]).*X(\d)(\d)Y\d\d\*%$')
|
||||
|
||||
# Mode (IN/MM)
|
||||
self.mode_re = re.compile(r'^%MO(IN|MM)\*%$')
|
||||
self.mode_re = re.compile(r'^%?MO(IN|MM)\*%?$')
|
||||
|
||||
# Comment G04|G4
|
||||
self.comm_re = re.compile(r'^G0?4(.*)$')
|
||||
|
@ -2013,7 +2013,7 @@ class Gerber (Geometry):
|
|||
self.eof_re = re.compile(r'^M02\*')
|
||||
|
||||
# IP - Image polarity
|
||||
self.pol_re = re.compile(r'^%IP(POS|NEG)\*%$')
|
||||
self.pol_re = re.compile(r'^%?IP(POS|NEG)\*%?$')
|
||||
|
||||
# LP - Level polarity
|
||||
self.lpol_re = re.compile(r'^%LP([DC])\*%$')
|
||||
|
@ -2169,6 +2169,10 @@ class Gerber (Geometry):
|
|||
# applying a union for every new polygon.
|
||||
poly_buffer = []
|
||||
|
||||
# made True when the LPC command is encountered in Gerber parsing
|
||||
# it allows adding data into the clear_geometry key of the self.apertures[aperture] dict
|
||||
self.is_lpc = False
|
||||
|
||||
# store here the follow geometry
|
||||
follow_buffer = []
|
||||
|
||||
|
@ -2242,15 +2246,27 @@ class Gerber (Geometry):
|
|||
geo = LineString(path)
|
||||
if not geo.is_empty:
|
||||
follow_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[last_path_aperture]['follow_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['follow_geometry'] = []
|
||||
self.apertures[last_path_aperture]['follow_geometry'].append(geo)
|
||||
|
||||
geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4))
|
||||
if not geo.is_empty:
|
||||
poly_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[current_aperture]['solid_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['solid_geometry'] = []
|
||||
self.apertures[current_aperture]['solid_geometry'].append(geo)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['clear_geometry'] = []
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
else:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['solid_geometry'] = []
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
|
||||
path = [path[-1]]
|
||||
|
||||
|
@ -2259,10 +2275,12 @@ class Gerber (Geometry):
|
|||
# TODO: Remove when bug fixed
|
||||
if len(poly_buffer) > 0:
|
||||
if current_polarity == 'D':
|
||||
self.is_lpc = True
|
||||
# self.follow_geometry = self.follow_geometry.union(cascaded_union(follow_buffer))
|
||||
self.solid_geometry = self.solid_geometry.union(cascaded_union(poly_buffer))
|
||||
|
||||
else:
|
||||
self.is_lpc = False
|
||||
# self.follow_geometry = self.follow_geometry.difference(cascaded_union(follow_buffer))
|
||||
self.solid_geometry = self.solid_geometry.difference(cascaded_union(poly_buffer))
|
||||
|
||||
|
@ -2284,8 +2302,8 @@ class Gerber (Geometry):
|
|||
log.debug("Gerber format found. (%s) " % str(gline))
|
||||
|
||||
log.debug(
|
||||
"Gerber format found. Gerber zeros = %s (L-omit leading zeros, T-omit trailing zeros)" %
|
||||
self.gerber_zeros)
|
||||
"Gerber format found. Gerber zeros = %s (L-omit leading zeros, T-omit trailing zeros, "
|
||||
"D-no zero supression)" % self.gerber_zeros)
|
||||
log.debug("Gerber format found. Coordinates type = %s (Absolute or Relative)" % absolute)
|
||||
continue
|
||||
|
||||
|
@ -2308,8 +2326,8 @@ class Gerber (Geometry):
|
|||
self.frac_digits = int(match.group(4))
|
||||
log.debug("Gerber format found. (%s) " % str(gline))
|
||||
log.debug(
|
||||
"Gerber format found. Gerber zeros = %s (L-omit leading zeros, T-omit trailing zeros)" %
|
||||
self.gerber_zeros)
|
||||
"Gerber format found. Gerber zeros = %s (L-omit leading zeros, T-omit trailing zeros, "
|
||||
"D-no zero suppression)" % self.gerber_zeros)
|
||||
log.debug("Gerber format found. Coordinates type = %s (Absolute or Relative)" % absolute)
|
||||
|
||||
gerber_units = match.group(1)
|
||||
|
@ -2332,8 +2350,8 @@ class Gerber (Geometry):
|
|||
self.frac_digits = int(match.group(5))
|
||||
log.debug("Gerber format found. (%s) " % str(gline))
|
||||
log.debug(
|
||||
"Gerber format found. Gerber zeros = %s (L-omit leading zeros, T-omit trailing zeros)" %
|
||||
self.gerber_zeros)
|
||||
"Gerber format found. Gerber zeros = %s (L-omit leading zeros, T-omit trailing zeros, "
|
||||
"D-no zerosuppressionn)" % self.gerber_zeros)
|
||||
log.debug("Gerber format found. Coordinates type = %s (Absolute or Relative)" % absolute)
|
||||
|
||||
gerber_units = match.group(1)
|
||||
|
@ -2415,11 +2433,18 @@ class Gerber (Geometry):
|
|||
int(self.steps_per_circle))
|
||||
if not flash.is_empty:
|
||||
poly_buffer.append(flash)
|
||||
try:
|
||||
self.apertures[current_aperture]['solid_geometry'].append(flash)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['solid_geometry'] = []
|
||||
self.apertures[current_aperture]['solid_geometry'].append(flash)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[current_aperture]['clear_geometry'].append(flash)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['clear_geometry'] = []
|
||||
self.apertures[current_aperture]['clear_geometry'].append(flash)
|
||||
else:
|
||||
try:
|
||||
self.apertures[current_aperture]['solid_geometry'].append(flash)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['solid_geometry'] = []
|
||||
self.apertures[current_aperture]['solid_geometry'].append(flash)
|
||||
except IndexError:
|
||||
log.warning("Line %d: %s -> Nothing there to flash!" % (line_num, gline))
|
||||
|
||||
|
@ -2453,15 +2478,27 @@ class Gerber (Geometry):
|
|||
geo = LineString(path)
|
||||
if not geo.is_empty:
|
||||
follow_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['follow_geometry'] = []
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
|
||||
geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4))
|
||||
if not geo.is_empty:
|
||||
poly_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['solid_geometry'] = []
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['clear_geometry'] = []
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
else:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['solid_geometry'] = []
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
|
||||
path = [path[-1]]
|
||||
|
||||
|
@ -2478,15 +2515,27 @@ class Gerber (Geometry):
|
|||
geo = LineString(path)
|
||||
if not geo.is_empty:
|
||||
follow_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['follow_geometry'] = []
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
|
||||
geo = LineString(path).buffer(width/1.999, int(self.steps_per_circle / 4))
|
||||
if not geo.is_empty:
|
||||
poly_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['solid_geometry'] = []
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['clear_geometry'] = []
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
else:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['solid_geometry'] = []
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
|
||||
path = [path[-1]]
|
||||
|
||||
|
@ -2503,12 +2552,25 @@ class Gerber (Geometry):
|
|||
if geo:
|
||||
if not geo.is_empty:
|
||||
follow_buffer.append(geo)
|
||||
poly_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[current_aperture]['solid_geometry'].append(geo)
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['solid_geometry'] = []
|
||||
self.apertures[current_aperture]['solid_geometry'].append(geo)
|
||||
self.apertures[current_aperture]['follow_geometry'] = []
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
|
||||
poly_buffer.append(geo)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[current_aperture]['clear_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['clear_geometry'] = []
|
||||
self.apertures[current_aperture]['clear_geometry'].append(geo)
|
||||
else:
|
||||
try:
|
||||
self.apertures[current_aperture]['solid_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['solid_geometry'] = []
|
||||
self.apertures[current_aperture]['solid_geometry'].append(geo)
|
||||
continue
|
||||
|
||||
# Only one path defines region?
|
||||
|
@ -2531,6 +2593,11 @@ class Gerber (Geometry):
|
|||
region = Polygon()
|
||||
if not region.is_empty:
|
||||
follow_buffer.append(region)
|
||||
try:
|
||||
self.apertures[current_aperture]['follow_geometry'].append(region)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['follow_geometry'] = []
|
||||
self.apertures[current_aperture]['follow_geometry'].append(region)
|
||||
|
||||
region = Polygon(path)
|
||||
if not region.is_valid:
|
||||
|
@ -2552,11 +2619,18 @@ class Gerber (Geometry):
|
|||
self.apertures['0']['solid_geometry'] = []
|
||||
used_aperture = '0'
|
||||
|
||||
try:
|
||||
self.apertures[used_aperture]['solid_geometry'].append(region)
|
||||
except KeyError:
|
||||
self.apertures[used_aperture]['solid_geometry'] = []
|
||||
self.apertures[used_aperture]['solid_geometry'].append(region)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[used_aperture]['clear_geometry'].append(region)
|
||||
except KeyError:
|
||||
self.apertures[used_aperture]['clear_geometry'] = []
|
||||
self.apertures[used_aperture]['clear_geometry'].append(region)
|
||||
else:
|
||||
try:
|
||||
self.apertures[used_aperture]['solid_geometry'].append(region)
|
||||
except KeyError:
|
||||
self.apertures[used_aperture]['solid_geometry'] = []
|
||||
self.apertures[used_aperture]['solid_geometry'].append(region)
|
||||
|
||||
path = [[current_x, current_y]] # Start new path
|
||||
continue
|
||||
|
@ -2627,11 +2701,18 @@ class Gerber (Geometry):
|
|||
|
||||
geo = shply_box(minx, miny, maxx, maxy)
|
||||
poly_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[current_aperture]['solid_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['solid_geometry'] = []
|
||||
self.apertures[current_aperture]['solid_geometry'].append(geo)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[current_aperture]['clear_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['clear_geometry'] = []
|
||||
self.apertures[current_aperture]['clear_geometry'].append(geo)
|
||||
else:
|
||||
try:
|
||||
self.apertures[current_aperture]['solid_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['solid_geometry'] = []
|
||||
self.apertures[current_aperture]['solid_geometry'].append(geo)
|
||||
except:
|
||||
pass
|
||||
last_path_aperture = current_aperture
|
||||
|
@ -2670,10 +2751,20 @@ class Gerber (Geometry):
|
|||
if self.apertures[last_path_aperture]["type"] != 'R':
|
||||
if not geo.is_empty:
|
||||
follow_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['follow_geometry'] = []
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
except Exception as e:
|
||||
log.debug("camlib.Gerber.parse_lines() --> %s" % str(e))
|
||||
if not geo.is_empty:
|
||||
follow_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['follow_geometry'] = []
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
|
||||
# this treats the case when we are storing geometry as solids
|
||||
if making_region:
|
||||
|
@ -2706,19 +2797,33 @@ class Gerber (Geometry):
|
|||
if self.apertures[last_path_aperture]["type"] != 'R':
|
||||
if not geo.is_empty:
|
||||
poly_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['solid_geometry'] = []
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['clear_geometry'] = []
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
else:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['solid_geometry'] = []
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
except Exception as e:
|
||||
log.debug("camlib.Gerber.parse_lines() --> %s" % str(e))
|
||||
poly_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['solid_geometry'] = []
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['clear_geometry'] = []
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
else:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['solid_geometry'] = []
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
|
||||
# if linear_x or linear_y are None, ignore those
|
||||
if linear_x is not None and linear_y is not None:
|
||||
|
@ -2741,8 +2846,18 @@ class Gerber (Geometry):
|
|||
try:
|
||||
if self.apertures[last_path_aperture]["type"] != 'R':
|
||||
follow_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['follow_geometry'] = []
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
except:
|
||||
follow_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['follow_geometry'] = []
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
|
||||
# this treats the case when we are storing geometry as solids
|
||||
width = self.apertures[last_path_aperture]["size"]
|
||||
|
@ -2751,18 +2866,32 @@ class Gerber (Geometry):
|
|||
try:
|
||||
if self.apertures[last_path_aperture]["type"] != 'R':
|
||||
poly_buffer.append(geo)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['clear_geometry'] = []
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
else:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['solid_geometry'] = []
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
except:
|
||||
poly_buffer.append(geo)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['clear_geometry'] = []
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
else:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['solid_geometry'] = []
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
except:
|
||||
poly_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['solid_geometry'] = []
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
|
||||
# Reset path starting point
|
||||
path = [[linear_x, linear_y]]
|
||||
|
@ -2770,7 +2899,13 @@ class Gerber (Geometry):
|
|||
# --- BUFFERED ---
|
||||
# Draw the flash
|
||||
# this treats the case when we are storing geometry as paths
|
||||
follow_buffer.append(Point([linear_x, linear_y]))
|
||||
geo_flash = Point([linear_x, linear_y])
|
||||
follow_buffer.append(geo_flash)
|
||||
try:
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo_flash)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['follow_geometry'] = []
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo_flash)
|
||||
|
||||
# this treats the case when we are storing geometry as solids
|
||||
flash = Gerber.create_flash_geometry(
|
||||
|
@ -2780,11 +2915,18 @@ class Gerber (Geometry):
|
|||
)
|
||||
if not flash.is_empty:
|
||||
poly_buffer.append(flash)
|
||||
try:
|
||||
self.apertures[current_aperture]['solid_geometry'].append(flash)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['solid_geometry'] = []
|
||||
self.apertures[current_aperture]['solid_geometry'].append(flash)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[current_aperture]['clear_geometry'].append(flash)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['clear_geometry'] = []
|
||||
self.apertures[current_aperture]['clear_geometry'].append(flash)
|
||||
else:
|
||||
try:
|
||||
self.apertures[current_aperture]['solid_geometry'].append(flash)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['solid_geometry'] = []
|
||||
self.apertures[current_aperture]['solid_geometry'].append(flash)
|
||||
|
||||
# maybe those lines are not exactly needed but it is easier to read the program as those coordinates
|
||||
# are used in case that circular interpolation is encountered within the Gerber file
|
||||
|
@ -2869,16 +3011,28 @@ class Gerber (Geometry):
|
|||
geo = LineString(path)
|
||||
if not geo.is_empty:
|
||||
follow_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['follow_geometry'] = []
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
|
||||
# this treats the case when we are storing geometry as solids
|
||||
buffered = LineString(path).buffer(width / 1.999, int(self.steps_per_circle))
|
||||
if not buffered.is_empty:
|
||||
poly_buffer.append(buffered)
|
||||
try:
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(buffered)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['solid_geometry'] = []
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(buffered)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(buffered)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['clear_geometry'] = []
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(buffered)
|
||||
else:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(buffered)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['solid_geometry'] = []
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(buffered)
|
||||
|
||||
current_x = circular_x
|
||||
current_y = circular_y
|
||||
|
@ -3000,20 +3154,46 @@ class Gerber (Geometry):
|
|||
geo = LineString(path)
|
||||
if not geo.is_empty:
|
||||
follow_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[current_aperture]['follow_geometry'] = []
|
||||
self.apertures[current_aperture]['follow_geometry'].append(geo)
|
||||
|
||||
# this treats the case when we are storing geometry as solids
|
||||
width = self.apertures[last_path_aperture]["size"]
|
||||
geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4))
|
||||
if not geo.is_empty:
|
||||
poly_buffer.append(geo)
|
||||
try:
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['solid_geometry'] = []
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
if self.is_lpc is True:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['clear_geometry'] = []
|
||||
self.apertures[last_path_aperture]['clear_geometry'].append(geo)
|
||||
else:
|
||||
try:
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
except KeyError:
|
||||
self.apertures[last_path_aperture]['solid_geometry'] = []
|
||||
self.apertures[last_path_aperture]['solid_geometry'].append(geo)
|
||||
|
||||
# first check if we have any clear_geometry (LPC) and if yes then we need to substract it
|
||||
# from the apertures solid_geometry
|
||||
temp_geo = []
|
||||
for apid in self.apertures:
|
||||
if 'clear_geometry' in self.apertures[apid]:
|
||||
for clear_geo in self.apertures[apid]['clear_geometry']:
|
||||
for solid_geo in self.apertures[apid]['solid_geometry']:
|
||||
if solid_geo.intersects(clear_geo):
|
||||
res_geo = clear_geo.symmetric_difference(solid_geo)
|
||||
temp_geo.append(res_geo)
|
||||
else:
|
||||
temp_geo.append(solid_geo)
|
||||
self.apertures[apid]['solid_geometry'] = deepcopy(temp_geo)
|
||||
self.apertures[apid].pop('clear_geometry', None)
|
||||
|
||||
# --- Apply buffer ---
|
||||
|
||||
# this treats the case when we are storing geometry as paths
|
||||
self.follow_geometry = follow_buffer
|
||||
|
||||
|
@ -3124,7 +3304,7 @@ class Gerber (Geometry):
|
|||
:rtype : None
|
||||
:return: None
|
||||
"""
|
||||
|
||||
pass
|
||||
# self.buffer_paths()
|
||||
#
|
||||
# self.fix_regions()
|
||||
|
@ -3184,16 +3364,17 @@ class Gerber (Geometry):
|
|||
maxx = max(maxx, maxx_)
|
||||
maxy = max(maxy, maxy_)
|
||||
else:
|
||||
try:
|
||||
minx_, miny_, maxx_, maxy_ = bounds_rec(k)
|
||||
except Exception as e:
|
||||
log.debug("camlib.Geometry.bounds() --> %s" % str(e))
|
||||
return
|
||||
if not k.is_empty:
|
||||
try:
|
||||
minx_, miny_, maxx_, maxy_ = bounds_rec(k)
|
||||
except Exception as e:
|
||||
log.debug("camlib.Gerber.bounds() --> %s" % str(e))
|
||||
return
|
||||
|
||||
minx = min(minx, minx_)
|
||||
miny = min(miny, miny_)
|
||||
maxx = max(maxx, maxx_)
|
||||
maxy = max(maxy, maxy_)
|
||||
minx = min(minx, minx_)
|
||||
miny = min(miny, miny_)
|
||||
maxx = max(maxx, maxx_)
|
||||
maxy = max(maxy, maxy_)
|
||||
return minx, miny, maxx, maxy
|
||||
else:
|
||||
# it's a Shapely object, return it's bounds
|
||||
|
@ -3264,7 +3445,7 @@ class Gerber (Geometry):
|
|||
except Exception as e:
|
||||
log.debug('FlatCAMGeometry.scale() --> %s' % str(e))
|
||||
|
||||
self.app.inform.emit(_("[success]Gerber Scale done."))
|
||||
self.app.inform.emit(_("[success] Gerber Scale done."))
|
||||
|
||||
|
||||
## solid_geometry ???
|
||||
|
@ -3297,7 +3478,7 @@ class Gerber (Geometry):
|
|||
try:
|
||||
dx, dy = vect
|
||||
except TypeError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]An (x,y) pair of values are needed. "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] An (x,y) pair of values are needed. "
|
||||
"Probable you entered only one value in the Offset field."))
|
||||
return
|
||||
|
||||
|
@ -3321,7 +3502,7 @@ class Gerber (Geometry):
|
|||
except Exception as e:
|
||||
log.debug('FlatCAMGeometry.offset() --> %s' % str(e))
|
||||
|
||||
self.app.inform.emit(_("[success]Gerber Offset done."))
|
||||
self.app.inform.emit(_("[success] Gerber Offset done."))
|
||||
|
||||
def mirror(self, axis, point):
|
||||
"""
|
||||
|
@ -3371,7 +3552,6 @@ class Gerber (Geometry):
|
|||
# self.solid_geometry = affinity.scale(self.solid_geometry,
|
||||
# xscale, yscale, origin=(px, py))
|
||||
|
||||
|
||||
def skew(self, angle_x, angle_y, point):
|
||||
"""
|
||||
Shear/Skew the geometries of an object by angles along x and y dimensions.
|
||||
|
@ -3530,6 +3710,12 @@ class Excellon(Geometry):
|
|||
self.zeros_found = self.zeros
|
||||
self.units_found = self.units
|
||||
|
||||
# this will serve as a default if the Excellon file has no info regarding of tool diameters (this info may be
|
||||
# in another file like for PCB WIzard ECAD software
|
||||
self.toolless_diam = 1.0
|
||||
# signal that the Excellon file has no tool diameter informations and the tools have bogus (random) diameter
|
||||
self.diameterless = False
|
||||
|
||||
# Excellon format
|
||||
self.excellon_format_upper_in = excellon_format_upper_in or self.defaults["excellon_format_upper_in"]
|
||||
self.excellon_format_lower_in = excellon_format_lower_in or self.defaults["excellon_format_lower_in"]
|
||||
|
@ -3575,7 +3761,7 @@ class Excellon(Geometry):
|
|||
# r'(?=.*F(\d*\.?\d*))?(?=.*S(\d*\.?\d*))?' +
|
||||
# r'(?=.*B(\d*\.?\d*))?(?=.*H(\d*\.?\d*))?' +
|
||||
# r'(?=.*Z([-\+]?\d*\.?\d*))?[CFSBHT]')
|
||||
self.toolset_re = re.compile(r'^T(\d+)(?=.*C(\d*\.?\d*))?' +
|
||||
self.toolset_re = re.compile(r'^T(\d+)(?=.*C,?(\d*\.?\d*))?' +
|
||||
r'(?=.*F(\d*\.?\d*))?(?=.*S(\d*\.?\d*))?' +
|
||||
r'(?=.*B(\d*\.?\d*))?(?=.*H(\d*\.?\d*))?' +
|
||||
r'(?=.*Z([-\+]?\d*\.?\d*))?[CFSBHT]')
|
||||
|
@ -3590,7 +3776,8 @@ class Excellon(Geometry):
|
|||
self.toolsel_re = re.compile(r'^T(\d+)')
|
||||
|
||||
# Headerless toolset
|
||||
self.toolset_hl_re = re.compile(r'^T(\d+)(?=.*C(\d*\.?\d*))')
|
||||
# self.toolset_hl_re = re.compile(r'^T(\d+)(?=.*C(\d*\.?\d*))')
|
||||
self.toolset_hl_re = re.compile(r'^T(\d+)(?:.?C(\d+\.?\d*))?')
|
||||
|
||||
# Comment
|
||||
self.comm_re = re.compile(r'^;(.*)$')
|
||||
|
@ -3739,10 +3926,9 @@ class Excellon(Geometry):
|
|||
continue
|
||||
else:
|
||||
log.warning("Line ignored, it's a comment: %s" % eline)
|
||||
|
||||
else:
|
||||
if self.hend_re.search(eline):
|
||||
if in_header is False:
|
||||
if in_header is False or bool(self.tools) is False:
|
||||
log.warning("Found end of the header but there is no header: %s" % eline)
|
||||
log.warning("The only useful data in header are tools, units and format.")
|
||||
log.warning("Therefore we will create units and format based on defaults.")
|
||||
|
@ -3787,12 +3973,27 @@ class Excellon(Geometry):
|
|||
if match:
|
||||
current_tool = str(int(match.group(1)))
|
||||
log.debug("Tool change: %s" % current_tool)
|
||||
if headerless is True:
|
||||
if bool(headerless):
|
||||
match = self.toolset_hl_re.search(eline)
|
||||
if match:
|
||||
name = str(int(match.group(1)))
|
||||
try:
|
||||
diam = float(match.group(2))
|
||||
except:
|
||||
# it's possible that tool definition has only tool number and no diameter info
|
||||
# (those could be in another file like PCB Wizard do)
|
||||
# then match.group(2) = None and float(None) will create the exception
|
||||
# the bellow construction is so each tool will have a slightly different diameter
|
||||
# starting with a default value, to allow Excellon editing after that
|
||||
self.diameterless = True
|
||||
|
||||
if self.excellon_units == 'MM':
|
||||
diam = self.toolless_diam + (int(current_tool) - 1) / 100
|
||||
else:
|
||||
diam = (self.toolless_diam + (int(current_tool) - 1) / 100) / 25.4
|
||||
|
||||
spec = {
|
||||
"C": float(match.group(2)),
|
||||
"C": diam,
|
||||
}
|
||||
spec['solid_geometry'] = []
|
||||
self.tools[name] = spec
|
||||
|
@ -4719,6 +4920,8 @@ class CNCjob(Geometry):
|
|||
Geometry.__init__(self, geo_steps_per_circle=int(steps_per_circle))
|
||||
|
||||
self.kind = kind
|
||||
self.origin_kind = None
|
||||
|
||||
self.units = units
|
||||
|
||||
self.z_cut = z_cut
|
||||
|
@ -4783,6 +4986,10 @@ class CNCjob(Geometry):
|
|||
|
||||
self.tool = 0.0
|
||||
|
||||
# used for creating drill CCode geometry; will be updated in the generate_from_excellon_by_tool()
|
||||
self.exc_drills = None
|
||||
self.exc_tools = None
|
||||
|
||||
# search for toolchange parameters in the Toolchange Custom Code
|
||||
self.re_toolchange_custom = re.compile(r'(%[a-zA-Z0-9\-_]+%)')
|
||||
|
||||
|
@ -4904,6 +5111,11 @@ class CNCjob(Geometry):
|
|||
:return: None
|
||||
:rtype: None
|
||||
"""
|
||||
|
||||
# create a local copy of the exobj.drills so it can be used for creating drill CCode geometry
|
||||
self.exc_drills = deepcopy(exobj.drills)
|
||||
self.exc_tools = deepcopy(exobj.tools)
|
||||
|
||||
if drillz > 0:
|
||||
self.app.inform.emit(_("[WARNING] The Cut Z parameter has positive value. "
|
||||
"It is the depth value to drill into material.\n"
|
||||
|
@ -5109,7 +5321,13 @@ class CNCjob(Geometry):
|
|||
current_tooldia = float('%.2f' % float(exobj.tools[tool]["C"]))
|
||||
else:
|
||||
current_tooldia = float('%.3f' % float(exobj.tools[tool]["C"]))
|
||||
z_offset = float(self.tool_offset[current_tooldia]) * (-1)
|
||||
|
||||
# TODO apply offset only when using the GUI, for TclCommand this will create an error
|
||||
# because the values for Z offset are created in build_ui()
|
||||
try:
|
||||
z_offset = float(self.tool_offset[current_tooldia]) * (-1)
|
||||
except KeyError:
|
||||
z_offset = 0
|
||||
self.z_cut += z_offset
|
||||
|
||||
# Drillling!
|
||||
|
@ -5128,7 +5346,7 @@ class CNCjob(Geometry):
|
|||
else:
|
||||
log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
|
||||
"The loaded Excellon file has no drills ...")
|
||||
self.app.inform.emit(_('[ERROR_NOTCL]The loaded Excellon file has no drills ...'))
|
||||
self.app.inform.emit(_('[ERROR_NOTCL] The loaded Excellon file has no drills ...'))
|
||||
return 'fail'
|
||||
|
||||
log.debug("The total travel distance with OR-TOOLS Metaheuristics is: %s" % str(measured_distance))
|
||||
|
@ -5223,7 +5441,7 @@ class CNCjob(Geometry):
|
|||
else:
|
||||
log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
|
||||
"The loaded Excellon file has no drills ...")
|
||||
self.app.inform.emit(_('[ERROR_NOTCL]The loaded Excellon file has no drills ...'))
|
||||
self.app.inform.emit(_('[ERROR_NOTCL] The loaded Excellon file has no drills ...'))
|
||||
return 'fail'
|
||||
|
||||
log.debug("The total travel distance with OR-TOOLS Basic Algorithm is: %s" % str(measured_distance))
|
||||
|
@ -5255,8 +5473,15 @@ class CNCjob(Geometry):
|
|||
current_tooldia = float('%.2f' % float(exobj.tools[tool]["C"]))
|
||||
else:
|
||||
current_tooldia = float('%.3f' % float(exobj.tools[tool]["C"]))
|
||||
z_offset = float(self.tool_offset[current_tooldia]) * (-1)
|
||||
|
||||
# TODO apply offset only when using the GUI, for TclCommand this will create an error
|
||||
# because the values for Z offset are created in build_ui()
|
||||
try:
|
||||
z_offset = float(self.tool_offset[current_tooldia]) * (-1)
|
||||
except KeyError:
|
||||
z_offset = 0
|
||||
self.z_cut += z_offset
|
||||
|
||||
# Drillling!
|
||||
altPoints = []
|
||||
for point in points[tool]:
|
||||
|
@ -5274,7 +5499,7 @@ class CNCjob(Geometry):
|
|||
else:
|
||||
log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
|
||||
"The loaded Excellon file has no drills ...")
|
||||
self.app.inform.emit(_('[ERROR_NOTCL]The loaded Excellon file has no drills ...'))
|
||||
self.app.inform.emit(_('[ERROR_NOTCL] The loaded Excellon file has no drills ...'))
|
||||
return 'fail'
|
||||
log.debug("The total travel distance with Travelling Salesman Algorithm is: %s" % str(measured_distance))
|
||||
|
||||
|
@ -5537,7 +5762,7 @@ class CNCjob(Geometry):
|
|||
|
||||
# if solid_geometry is empty raise an exception
|
||||
if not geometry.solid_geometry:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Trying to generate a CNC Job "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Trying to generate a CNC Job "
|
||||
"from a Geometry object without solid_geometry."))
|
||||
|
||||
temp_solid_geometry = []
|
||||
|
@ -5576,7 +5801,7 @@ class CNCjob(Geometry):
|
|||
# if the offset is less than half of the total length or less than half of the total width of the
|
||||
# solid geometry it's obvious we can't do the offset
|
||||
if -offset > ((c - a) / 2) or -offset > ((d - b) / 2):
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]The Tool Offset value is too negative to use "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] The Tool Offset value is too negative to use "
|
||||
"for the current_geometry.\n"
|
||||
"Raise the value (in module) and try again."))
|
||||
return 'fail'
|
||||
|
@ -6098,7 +6323,7 @@ class CNCjob(Geometry):
|
|||
|
||||
# Process every instruction
|
||||
for line in StringIO(self.gcode):
|
||||
if '%MO' in line or '%' in line:
|
||||
if '%MO' in line or '%' in line or 'MOIN' in line or 'MOMM' in line:
|
||||
return "fail"
|
||||
|
||||
gobj = self.codes_split(line)
|
||||
|
@ -6130,6 +6355,23 @@ class CNCjob(Geometry):
|
|||
"kind": kind})
|
||||
path = [path[-1]] # Start with the last point of last path.
|
||||
|
||||
# create the geometry for the holes created when drilling Excellon drills
|
||||
if self.origin_kind == 'excellon':
|
||||
if current['Z'] < 0:
|
||||
current_drill_point_coords = (float('%.4f' % current['X']), float('%.4f' % current['Y']))
|
||||
# find the drill diameter knowing the drill coordinates
|
||||
for pt_dict in self.exc_drills:
|
||||
point_in_dict_coords = (float('%.4f' % pt_dict['point'].x),
|
||||
float('%.4f' % pt_dict['point'].y))
|
||||
if point_in_dict_coords == current_drill_point_coords:
|
||||
tool = pt_dict['tool']
|
||||
dia = self.exc_tools[tool]['C']
|
||||
kind = ['C', 'F']
|
||||
geometry.append({"geom": Point(current_drill_point_coords).
|
||||
buffer(dia/2).exterior,
|
||||
"kind": kind})
|
||||
break
|
||||
|
||||
if 'G' in gobj:
|
||||
current['G'] = int(gobj['G'])
|
||||
|
||||
|
@ -6256,7 +6498,17 @@ class CNCjob(Geometry):
|
|||
text.append(str(path_num))
|
||||
pos.append(geo['geom'].coords[0])
|
||||
|
||||
poly = geo['geom'].buffer(tooldia / 2.0).simplify(tool_tolerance)
|
||||
# plot the geometry of Excellon objects
|
||||
if self.origin_kind == 'excellon':
|
||||
try:
|
||||
poly = Polygon(geo['geom'])
|
||||
except ValueError:
|
||||
# if the geos are travel lines it will enter into Exception
|
||||
poly = geo['geom'].buffer(tooldia / 2.0).simplify(tool_tolerance)
|
||||
else:
|
||||
# plot the geometry of any objects other than Excellon
|
||||
poly = geo['geom'].buffer(tooldia / 2.0).simplify(tool_tolerance)
|
||||
|
||||
if kind == 'all':
|
||||
obj.add_shape(shape=poly, color=color[geo['kind'][0]][1], face_color=color[geo['kind'][0]][0],
|
||||
visible=visible, layer=1 if geo['kind'][0] == 'C' else 2)
|
||||
|
@ -7075,17 +7327,22 @@ def parse_gerber_number(strnumber, int_digits, frac_digits, zeros):
|
|||
:param frac_digits: Number of digits used for the fractional
|
||||
part of the number
|
||||
:type frac_digits: int
|
||||
:param zeros: If 'L', leading zeros are removed and trailing zeros are kept. If 'T', is in reverse.
|
||||
:param zeros: If 'L', leading zeros are removed and trailing zeros are kept. Same situation for 'D' when
|
||||
no zero suppression is done. If 'T', is in reverse.
|
||||
:type zeros: str
|
||||
:return: The number in floating point.
|
||||
:rtype: float
|
||||
"""
|
||||
if zeros == 'L':
|
||||
|
||||
ret_val = None
|
||||
|
||||
if zeros == 'L' or zeros == 'D':
|
||||
ret_val = int(strnumber) * (10 ** (-frac_digits))
|
||||
|
||||
if zeros == 'T':
|
||||
int_val = int(strnumber)
|
||||
ret_val = (int_val * (10 ** ((int_digits + frac_digits) - len(strnumber)))) * (10 ** (-frac_digits))
|
||||
|
||||
return ret_val
|
||||
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ from flatcamGUI.GUIElements import *
|
|||
import platform
|
||||
import webbrowser
|
||||
|
||||
from FlatCAMEditor import FCShapeTool
|
||||
from flatcamEditors.FlatCAMGeoEditor import FCShapeTool
|
||||
|
||||
import gettext
|
||||
import FlatCAMTranslation as fcTranslate
|
||||
|
@ -66,6 +66,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.menufilenewgeo.setToolTip(
|
||||
_("Will create a new, empty Geometry Object.")
|
||||
)
|
||||
self.menufilenewgrb = self.menufilenew.addAction(QtGui.QIcon('share/flatcam_icon32.png'), _('Gerber\tB'))
|
||||
self.menufilenewgrb.setToolTip(
|
||||
_("Will create a new, empty Gerber Object.")
|
||||
)
|
||||
self.menufilenewexc = self.menufilenew.addAction(QtGui.QIcon('share/drill16.png'), _('Excellon\tL'))
|
||||
self.menufilenewexc.setToolTip(
|
||||
_("Will create a new, empty Excellon Object.")
|
||||
|
@ -107,7 +111,14 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
# Separator
|
||||
self.menufile.addSeparator()
|
||||
|
||||
# Run Scripts
|
||||
# Scripting
|
||||
self.menufile_scripting = self.menufile.addMenu(QtGui.QIcon('share/script16.png'), _('Scripting'))
|
||||
self.menufile_scripting.setToolTipsVisible(True)
|
||||
|
||||
self.menufilenewscript = QtWidgets.QAction(QtGui.QIcon('share/script_new16.png'), _('New Script ...'),
|
||||
self)
|
||||
self.menufileopenscript = QtWidgets.QAction(QtGui.QIcon('share/script_open16.png'), _('Open Script ...'),
|
||||
self)
|
||||
self.menufilerunscript = QtWidgets.QAction(QtGui.QIcon('share/script16.png'), _('Run Script ...\tSHIFT+S'),
|
||||
self)
|
||||
self.menufilerunscript.setToolTip(
|
||||
|
@ -115,7 +126,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
"enabling the automation of certain\n"
|
||||
"functions of FlatCAM.")
|
||||
)
|
||||
self.menufile.addAction(self.menufilerunscript)
|
||||
self.menufile_scripting.addAction(self.menufilenewscript)
|
||||
self.menufile_scripting.addAction(self.menufileopenscript)
|
||||
self.menufile_scripting.addSeparator()
|
||||
self.menufile_scripting.addAction(self.menufilerunscript)
|
||||
|
||||
# Separator
|
||||
self.menufile.addSeparator()
|
||||
|
@ -209,7 +223,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
# Separator
|
||||
self.menuedit.addSeparator()
|
||||
self.menueditedit = self.menuedit.addAction(QtGui.QIcon('share/edit16.png'), _('Edit Object\tE'))
|
||||
self.menueditok = self.menuedit.addAction(QtGui.QIcon('share/edit_ok16.png'), _('Save && Close Editor\tCTRL+S'))
|
||||
self.menueditok = self.menuedit.addAction(QtGui.QIcon('share/edit_ok16.png'), _('Close Editor\tCTRL+S'))
|
||||
|
||||
# adjust the initial state of the menu entries related to the editor
|
||||
self.menueditedit.setDisabled(False)
|
||||
|
@ -441,13 +455,48 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.exc_move_drill_menuitem = self.exc_editor_menu.addAction(
|
||||
QtGui.QIcon('share/move32.png'),_( 'Move Drill(s)\tM'))
|
||||
|
||||
### APPLICATION GERBER EDITOR MENU ###
|
||||
|
||||
self.grb_editor_menu = QtWidgets.QMenu(_(">Gerber Editor<"))
|
||||
self.menu.addMenu(self.grb_editor_menu)
|
||||
|
||||
self.grb_add_pad_menuitem = self.grb_editor_menu.addAction(
|
||||
QtGui.QIcon('share/aperture16.png'), _('Add Pad\tP'))
|
||||
self.grb_add_pad_array_menuitem = self.grb_editor_menu.addAction(
|
||||
QtGui.QIcon('share/padarray32.png'), _('Add Pad Array\tA'))
|
||||
self.grb_add_track_menuitem = self.grb_editor_menu.addAction(
|
||||
QtGui.QIcon('share/track32.png'), _('Add Track\tT'))
|
||||
self.grb_add_region_menuitem = self.grb_editor_menu.addAction(QtGui.QIcon('share/rectangle32.png'),
|
||||
_('Add Region\tN'))
|
||||
self.grb_editor_menu.addSeparator()
|
||||
|
||||
self.grb_add_buffer_menuitem = self.grb_editor_menu.addAction(QtGui.QIcon('share/buffer16-2.png'),
|
||||
_('Buffer\tB'))
|
||||
self.grb_add_scale_menuitem = self.grb_editor_menu.addAction(QtGui.QIcon('share/scale32.png'),
|
||||
_('Scale\tS'))
|
||||
self.grb_transform_menuitem = self.grb_editor_menu.addAction(
|
||||
QtGui.QIcon('share/transform.png'),_( "Transform\tALT+R")
|
||||
)
|
||||
self.grb_editor_menu.addSeparator()
|
||||
|
||||
self.grb_copy_menuitem = self.grb_editor_menu.addAction(QtGui.QIcon('share/copy32.png'), _('Copy\tC'))
|
||||
self.grb_delete_menuitem = self.grb_editor_menu.addAction(
|
||||
QtGui.QIcon('share/deleteshape32.png'), _('Delete\tDEL')
|
||||
)
|
||||
self.grb_editor_menu.addSeparator()
|
||||
|
||||
self.grb_move_menuitem = self.grb_editor_menu.addAction(
|
||||
QtGui.QIcon('share/move32.png'),_( 'Move\tM'))
|
||||
|
||||
self.grb_editor_menu.menuAction().setVisible(False)
|
||||
self.grb_editor_menu.setDisabled(True)
|
||||
|
||||
self.geo_editor_menu.menuAction().setVisible(False)
|
||||
self.geo_editor_menu.setDisabled(True)
|
||||
|
||||
self.exc_editor_menu.menuAction().setVisible(False)
|
||||
self.exc_editor_menu.setDisabled(True)
|
||||
|
||||
|
||||
################################
|
||||
### Project Tab Context menu ###
|
||||
################################
|
||||
|
@ -522,6 +571,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.geo_edit_toolbar.setObjectName('GeoEditor_TB')
|
||||
self.addToolBar(self.geo_edit_toolbar)
|
||||
|
||||
self.grb_edit_toolbar = QtWidgets.QToolBar(_('Gerber Editor Toolbar'))
|
||||
self.grb_edit_toolbar.setObjectName('GrbEditor_TB')
|
||||
self.addToolBar(self.grb_edit_toolbar)
|
||||
|
||||
self.snap_toolbar = QtWidgets.QToolBar(_('Grid Toolbar'))
|
||||
self.snap_toolbar.setObjectName('Snap_TB')
|
||||
self.addToolBar(self.snap_toolbar)
|
||||
|
@ -529,9 +582,9 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
settings = QSettings("Open Source", "FlatCAM")
|
||||
if settings.contains("layout"):
|
||||
layout = settings.value('layout', type=str)
|
||||
if layout == 'Standard':
|
||||
if layout == 'standard':
|
||||
pass
|
||||
elif layout == 'Compact':
|
||||
elif layout == 'compact':
|
||||
self.removeToolBar(self.snap_toolbar)
|
||||
self.snap_toolbar.setMaximumHeight(30)
|
||||
self.splitter_left.addWidget(self.snap_toolbar)
|
||||
|
@ -546,6 +599,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
|
||||
### Edit Toolbar ###
|
||||
self.newgeo_btn = self.toolbargeo.addAction(QtGui.QIcon('share/new_geo32_bis.png'), _("New Blank Geometry"))
|
||||
self.newgrb_btn = self.toolbargeo.addAction(QtGui.QIcon('share/new_geo32.png'), _("New Blank Gerber"))
|
||||
self.newexc_btn = self.toolbargeo.addAction(QtGui.QIcon('share/new_exc32.png'), _("New Blank Excellon"))
|
||||
self.toolbargeo.addSeparator()
|
||||
self.editgeo_btn = self.toolbargeo.addAction(QtGui.QIcon('share/edit32.png'), _("Editor"))
|
||||
|
@ -587,12 +641,12 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.select_drill_btn = self.exc_edit_toolbar.addAction(QtGui.QIcon('share/pointer32.png'), _("Select"))
|
||||
self.add_drill_btn = self.exc_edit_toolbar.addAction(QtGui.QIcon('share/plus16.png'), _('Add Drill Hole'))
|
||||
self.add_drill_array_btn = self.exc_edit_toolbar.addAction(
|
||||
QtGui.QIcon('share/addarray16.png'), 'Add Drill Hole Array')
|
||||
QtGui.QIcon('share/addarray16.png'), _('Add Drill Hole Array'))
|
||||
self.resize_drill_btn = self.exc_edit_toolbar.addAction(QtGui.QIcon('share/resize16.png'), _('Resize Drill'))
|
||||
self.exc_edit_toolbar.addSeparator()
|
||||
|
||||
self.copy_drill_btn = self.exc_edit_toolbar.addAction(QtGui.QIcon('share/copy32.png'), _('Copy Drill'))
|
||||
self.delete_drill_btn = self.exc_edit_toolbar.addAction(QtGui.QIcon('share/deleteshape32.png'), _("Delete Drill"))
|
||||
self.delete_drill_btn = self.exc_edit_toolbar.addAction(QtGui.QIcon('share/trash32.png'), _("Delete Drill"))
|
||||
|
||||
self.exc_edit_toolbar.addSeparator()
|
||||
self.move_drill_btn = self.exc_edit_toolbar.addAction(QtGui.QIcon('share/move32.png'), _("Move Drill"))
|
||||
|
@ -623,13 +677,32 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.geo_cutpath_btn = self.geo_edit_toolbar.addAction(QtGui.QIcon('share/cutpath32.png'), _('Cut Path'))
|
||||
self.geo_copy_btn = self.geo_edit_toolbar.addAction(QtGui.QIcon('share/copy32.png'), _("Copy Shape(s)"))
|
||||
|
||||
self.geo_delete_btn = self.geo_edit_toolbar.addAction(QtGui.QIcon('share/deleteshape32.png'),
|
||||
self.geo_delete_btn = self.geo_edit_toolbar.addAction(QtGui.QIcon('share/trash32.png'),
|
||||
_("Delete Shape '-'"))
|
||||
self.geo_transform_btn = self.geo_edit_toolbar.addAction(QtGui.QIcon('share/transform.png'),
|
||||
_("Transformations"))
|
||||
self.geo_edit_toolbar.addSeparator()
|
||||
self.geo_move_btn = self.geo_edit_toolbar.addAction(QtGui.QIcon('share/move32.png'), _("Move Objects "))
|
||||
|
||||
### Gerber Editor Toolbar ###
|
||||
self.grb_select_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/pointer32.png'), _("Select"))
|
||||
self.grb_add_pad_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/aperture32.png'), _("Add Pad"))
|
||||
self.add_pad_ar_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/padarray32.png'), _('Add Pad Array'))
|
||||
self.grb_add_track_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/track32.png'), _("Add Track"))
|
||||
self.grb_add_region_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/polygon32.png'), _("Add Region"))
|
||||
self.grb_edit_toolbar.addSeparator()
|
||||
|
||||
self.aperture_buffer_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/buffer16-2.png'), _('Buffer'))
|
||||
self.aperture_scale_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/scale32.png'), _('Scale'))
|
||||
self.grb_edit_toolbar.addSeparator()
|
||||
self.aperture_copy_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/copy32.png'), _("Copy"))
|
||||
self.aperture_delete_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/trash32.png'),
|
||||
_("Delete"))
|
||||
self.grb_transform_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/transform.png'),
|
||||
_("Transformations"))
|
||||
self.grb_edit_toolbar.addSeparator()
|
||||
self.aperture_move_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/move32.png'), _("Move"))
|
||||
|
||||
### Snap Toolbar ###
|
||||
# Snap GRID toolbar is always active to facilitate usage of measurements done on GRID
|
||||
# self.addToolBar(self.snap_toolbar)
|
||||
|
@ -1340,6 +1413,81 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
<td> Save Object and Exit Editor</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<br>
|
||||
<br>
|
||||
<strong><span style="color:#00ff00">GERBER EDITOR</span></strong><br>
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="width:283px">
|
||||
<tbody>
|
||||
<tr height="20">
|
||||
<td height="20" width="89"><strong>A</strong></td>
|
||||
<td width="194"> Add Pad Array</td>
|
||||
</tr>
|
||||
<tr height="20">
|
||||
<td height="20"><strong>B</strong></td>
|
||||
<td> Buffer</td>
|
||||
</tr>
|
||||
<tr height="20">
|
||||
<td height="20"><strong>C</strong></td>
|
||||
<td> Copy</td>
|
||||
</tr>
|
||||
<tr height="20">
|
||||
<td height="20"><strong>J</strong></td>
|
||||
<td> Jump to Location (x, y)</td>
|
||||
</tr>
|
||||
<tr height="20">
|
||||
<td height="20"><strong>M</strong></td>
|
||||
<td> Move</td>
|
||||
</tr>
|
||||
<tr height="20">
|
||||
<td height="20"><strong>N</strong></td>
|
||||
<td> Add Region</td>
|
||||
</tr>
|
||||
<tr height="20">
|
||||
<td height="20"><strong>P</strong></td>
|
||||
<td> Add Pad</td>
|
||||
</tr>
|
||||
<tr height="20">
|
||||
<td height="20"><strong>S</strong></td>
|
||||
<td> Scale</td>
|
||||
</tr>
|
||||
<tr height="20">
|
||||
<td height="20"><strong>T</strong></td>
|
||||
<td> Add Track</td>
|
||||
</tr>
|
||||
<tr height="20">
|
||||
<td height="20"> </td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
<tr height="20">
|
||||
<td height="20"><strong>Del</strong></td>
|
||||
<td> Delete</td>
|
||||
</tr>
|
||||
<tr height="20">
|
||||
<td height="20"><strong>Del</strong></td>
|
||||
<td> Alternate: Delete Apertures</td>
|
||||
</tr>
|
||||
<tr height="20">
|
||||
<td height="20"> </td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
<tr height="20">
|
||||
<td height="20"><strong>ESC</strong></td>
|
||||
<td> Abort and return to Select</td>
|
||||
</tr>
|
||||
<tr height="20">
|
||||
<td height="20"><strong>CTRL+S</strong></td>
|
||||
<td> Save Object and Exit Editor</td>
|
||||
</tr>
|
||||
<tr height="20">
|
||||
<td height="20"> </td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
<tr height="20">
|
||||
<td height="20"><strong>ALT+R</strong></td>
|
||||
<td> Transformation Tool</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
'''
|
||||
)
|
||||
|
@ -1379,6 +1527,16 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.g_editor_cmenu.addSeparator()
|
||||
self.draw_move = self.g_editor_cmenu.addAction(QtGui.QIcon('share/move32.png'), _("Move"))
|
||||
|
||||
self.grb_editor_cmenu = self.popMenu.addMenu(QtGui.QIcon('share/draw32.png'), _("Gerber Editor"))
|
||||
self.grb_draw_pad = self.grb_editor_cmenu.addAction(QtGui.QIcon('share/aperture32.png'), _("Pad"))
|
||||
self.grb_draw_pad_array = self.grb_editor_cmenu.addAction(QtGui.QIcon('share/padarray32.png'), _("Pad Array"))
|
||||
self.grb_draw_track = self.grb_editor_cmenu.addAction(QtGui.QIcon('share/track32.png'), _("Track"))
|
||||
self.grb_draw_region = self.grb_editor_cmenu.addAction(QtGui.QIcon('share/polygon32.png'), _("Region"))
|
||||
self.grb_editor_cmenu.addSeparator()
|
||||
self.grb_copy = self.grb_editor_cmenu.addAction(QtGui.QIcon('share/copy.png'), _("Copy"))
|
||||
self.grb_delete = self.grb_editor_cmenu.addAction(QtGui.QIcon('share/trash32.png'), _("Delete"))
|
||||
self.grb_move = self.grb_editor_cmenu.addAction(QtGui.QIcon('share/move32.png'), _("Move"))
|
||||
|
||||
self.e_editor_cmenu = self.popMenu.addMenu(QtGui.QIcon('share/drill32.png'), _("Exc Editor"))
|
||||
self.drill = self.e_editor_cmenu.addAction(QtGui.QIcon('share/drill32.png'), _("Add Drill"))
|
||||
self.drill_array = self.e_editor_cmenu.addAction(QtGui.QIcon('share/addarray32.png'), _("Add Drill Array"))
|
||||
|
@ -1388,7 +1546,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.popmenu_copy = self.popMenu.addAction(QtGui.QIcon('share/copy32.png'), _("Copy"))
|
||||
self.popmenu_delete = self.popMenu.addAction(QtGui.QIcon('share/delete32.png'), _("Delete"))
|
||||
self.popmenu_edit = self.popMenu.addAction(QtGui.QIcon('share/edit32.png'), _("Edit"))
|
||||
self.popmenu_save = self.popMenu.addAction(QtGui.QIcon('share/floppy32.png'), _("Save && Close Edit"))
|
||||
self.popmenu_save = self.popMenu.addAction(QtGui.QIcon('share/floppy32.png'), _("Close Editor"))
|
||||
self.popmenu_save.setVisible(False)
|
||||
self.popMenu.addSeparator()
|
||||
|
||||
|
@ -1404,7 +1562,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.cncjob_tab_layout.setContentsMargins(2, 2, 2, 2)
|
||||
self.cncjob_tab.setLayout(self.cncjob_tab_layout)
|
||||
|
||||
self.code_editor = QtWidgets.QTextEdit()
|
||||
self.code_editor = FCTextAreaExtended()
|
||||
stylesheet = """
|
||||
QTextEdit { selection-background-color:yellow;
|
||||
selection-color:black;
|
||||
|
@ -1523,6 +1681,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.grid_snap_btn.trigger()
|
||||
|
||||
self.g_editor_cmenu.setEnabled(False)
|
||||
self.grb_editor_cmenu.setEnabled(False)
|
||||
self.e_editor_cmenu.setEnabled(False)
|
||||
|
||||
self.general_defaults_form = GeneralPreferencesUI()
|
||||
|
@ -1548,6 +1707,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.restoreState(saved_gui_state)
|
||||
log.debug("FlatCAMGUI.__init__() --> UI state restored.")
|
||||
|
||||
settings = QSettings("Open Source", "FlatCAM")
|
||||
if settings.contains("layout"):
|
||||
layout = settings.value('layout', type=str)
|
||||
if layout == 'standard':
|
||||
|
@ -1555,24 +1715,36 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.exc_edit_toolbar.setDisabled(True)
|
||||
self.geo_edit_toolbar.setVisible(False)
|
||||
self.geo_edit_toolbar.setDisabled(True)
|
||||
self.grb_edit_toolbar.setVisible(False)
|
||||
self.grb_edit_toolbar.setDisabled(True)
|
||||
|
||||
self.corner_snap_btn.setVisible(False)
|
||||
self.snap_magnet.setVisible(False)
|
||||
elif layout == 'Compact':
|
||||
elif layout == 'compact':
|
||||
self.exc_edit_toolbar.setDisabled(True)
|
||||
self.geo_edit_toolbar.setDisabled(True)
|
||||
self.grb_edit_toolbar.setDisabled(True)
|
||||
|
||||
self.snap_magnet.setVisible(True)
|
||||
self.corner_snap_btn.setVisible(True)
|
||||
self.snap_magnet.setDisabled(True)
|
||||
self.corner_snap_btn.setDisabled(True)
|
||||
log.debug("FlatCAMGUI.__init__() --> UI layout restored from QSettings.")
|
||||
else:
|
||||
self.exc_edit_toolbar.setVisible(False)
|
||||
self.exc_edit_toolbar.setDisabled(True)
|
||||
self.geo_edit_toolbar.setVisible(False)
|
||||
self.geo_edit_toolbar.setDisabled(True)
|
||||
self.grb_edit_toolbar.setVisible(False)
|
||||
self.grb_edit_toolbar.setDisabled(True)
|
||||
|
||||
self.corner_snap_btn.setVisible(False)
|
||||
self.snap_magnet.setVisible(False)
|
||||
settings.setValue('layout', "standard")
|
||||
|
||||
# This will write the setting to the platform specific storage.
|
||||
del settings
|
||||
log.debug("FlatCAMGUI.__init__() --> UI layout restored from defaults. QSettings set to 'standard'")
|
||||
|
||||
def eventFilter(self, obj, event):
|
||||
if self.general_defaults_form.general_app_group.toggle_tooltips_cb.get_value() is False:
|
||||
|
@ -1643,7 +1815,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.exc_edit_toolbar.addSeparator()
|
||||
|
||||
self.copy_drill_btn = self.exc_edit_toolbar.addAction(QtGui.QIcon('share/copy32.png'), _('Copy Drill'))
|
||||
self.delete_drill_btn = self.exc_edit_toolbar.addAction(QtGui.QIcon('share/deleteshape32.png'),
|
||||
self.delete_drill_btn = self.exc_edit_toolbar.addAction(QtGui.QIcon('share/trash32.png'),
|
||||
_("Delete Drill"))
|
||||
|
||||
self.exc_edit_toolbar.addSeparator()
|
||||
|
@ -1676,7 +1848,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.geo_edit_toolbar.addSeparator()
|
||||
self.geo_cutpath_btn = self.geo_edit_toolbar.addAction(QtGui.QIcon('share/cutpath32.png'), _('Cut Path'))
|
||||
self.geo_copy_btn = self.geo_edit_toolbar.addAction(QtGui.QIcon('share/copy32.png'), _("Copy Objects"))
|
||||
self.geo_delete_btn = self.geo_edit_toolbar.addAction(QtGui.QIcon('share/deleteshape32.png'),
|
||||
self.geo_delete_btn = self.geo_edit_toolbar.addAction(QtGui.QIcon('share/trash32.png'),
|
||||
_("Delete Shape"))
|
||||
self.geo_transform_btn = self.geo_edit_toolbar.addAction(QtGui.QIcon('share/transform.png'),
|
||||
_("Transformations"))
|
||||
|
@ -1684,6 +1856,25 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.geo_edit_toolbar.addSeparator()
|
||||
self.geo_move_btn = self.geo_edit_toolbar.addAction(QtGui.QIcon('share/move32.png'), _("Move Objects"))
|
||||
|
||||
### Gerber Editor Toolbar ###
|
||||
self.grb_select_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/pointer32.png'), _("Select"))
|
||||
self.grb_add_pad_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/aperture32.png'), _("Add Pad"))
|
||||
self.add_pad_ar_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/padarray32.png'), _('Add Pad Array'))
|
||||
self.grb_add_track_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/track32.png'), _("Add Track"))
|
||||
self.grb_add_region_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/polygon32.png'), _("Add Region"))
|
||||
self.grb_edit_toolbar.addSeparator()
|
||||
|
||||
self.aperture_buffer_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/buffer16-2.png'), _('Buffer'))
|
||||
self.aperture_scale_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/scale32.png'), _('Scale'))
|
||||
self.grb_edit_toolbar.addSeparator()
|
||||
self.aperture_copy_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/copy32.png'), _("Copy"))
|
||||
self.aperture_delete_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/trash32.png'),
|
||||
_("Delete"))
|
||||
self.grb_transform_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/transform.png'),
|
||||
_("Transformations"))
|
||||
self.grb_edit_toolbar.addSeparator()
|
||||
self.aperture_move_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/move32.png'), _("Move"))
|
||||
|
||||
### Snap Toolbar ###
|
||||
# Snap GRID toolbar is always active to facilitate usage of measurements done on GRID
|
||||
# self.addToolBar(self.snap_toolbar)
|
||||
|
@ -1729,14 +1920,18 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.exc_edit_toolbar.setDisabled(True)
|
||||
self.geo_edit_toolbar.setVisible(False)
|
||||
self.geo_edit_toolbar.setDisabled(True)
|
||||
self.grb_edit_toolbar.setVisible(False)
|
||||
self.grb_edit_toolbar.setDisabled(True)
|
||||
|
||||
self.corner_snap_btn.setVisible(False)
|
||||
self.snap_magnet.setVisible(False)
|
||||
elif layout == 'Compact':
|
||||
elif layout == 'compact':
|
||||
self.exc_edit_toolbar.setVisible(True)
|
||||
self.exc_edit_toolbar.setDisabled(True)
|
||||
self.geo_edit_toolbar.setVisible(True)
|
||||
self.geo_edit_toolbar.setDisabled(True)
|
||||
self.grb_edit_toolbar.setVisible(False)
|
||||
self.grb_edit_toolbar.setDisabled(True)
|
||||
|
||||
self.corner_snap_btn.setVisible(True)
|
||||
self.snap_magnet.setVisible(True)
|
||||
|
@ -1948,6 +2143,13 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
# Escape = Deselect All
|
||||
if key == QtCore.Qt.Key_Escape or key == 'Escape':
|
||||
self.app.on_deselect_all()
|
||||
|
||||
# if in full screen, exit to normal view
|
||||
self.showNormal()
|
||||
self.app.restore_toolbar_view()
|
||||
self.splitter_left.setVisible(True)
|
||||
self.app.toggle_fscreen = False
|
||||
|
||||
# try to disconnect the slot from Set Origin
|
||||
try:
|
||||
self.app.plotcanvas.vis_disconnect('mouse_press', self.app.on_set_zero_click)
|
||||
|
@ -1961,6 +2163,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
select.ui.plot_cb.toggle()
|
||||
self.app.delete_selection_shape()
|
||||
|
||||
# New Geometry
|
||||
if key == QtCore.Qt.Key_B:
|
||||
self.app.new_gerber_object()
|
||||
|
||||
# Copy Object Name
|
||||
if key == QtCore.Qt.Key_E:
|
||||
self.app.object2editor()
|
||||
|
@ -2111,7 +2317,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
|
||||
if self.app.geo_editor.active_tool.complete:
|
||||
self.app.geo_editor.on_shape_complete()
|
||||
self.app.inform.emit(_("[success]Done."))
|
||||
self.app.inform.emit(_("[success] Done."))
|
||||
# automatically make the selection tool active after completing current action
|
||||
self.app.geo_editor.select_tool('select')
|
||||
return
|
||||
|
@ -2123,7 +2329,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
|
||||
if self.app.geo_editor.active_tool.complete:
|
||||
self.app.geo_editor.on_shape_complete()
|
||||
self.app.inform.emit(_("[success]Done."))
|
||||
self.app.inform.emit(_("[success] Done."))
|
||||
# automatically make the selection tool active after completing current action
|
||||
self.app.geo_editor.select_tool('select')
|
||||
|
||||
|
@ -2131,7 +2337,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
if key == QtCore.Qt.Key_Escape or key == 'Escape':
|
||||
# TODO: ...?
|
||||
# self.on_tool_select("select")
|
||||
self.app.inform.emit(_("[WARNING_NOTCL]Cancelled."))
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] Cancelled."))
|
||||
|
||||
self.app.geo_editor.delete_utility_geometry()
|
||||
|
||||
|
@ -2150,7 +2356,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.app.geo_editor.delete_selected()
|
||||
self.app.geo_editor.replot()
|
||||
|
||||
# Move
|
||||
# Rotate
|
||||
if key == QtCore.Qt.Key_Space or key == 'Space':
|
||||
self.app.geo_editor.transform_tool.on_rotate_key()
|
||||
|
||||
|
@ -2304,6 +2510,208 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
# Show Shortcut list
|
||||
if key == 'F3':
|
||||
self.app.on_shortcut_list()
|
||||
elif self.app.call_source == 'grb_editor':
|
||||
if modifiers == QtCore.Qt.ControlModifier:
|
||||
# save (update) the current geometry and return to the App
|
||||
if key == QtCore.Qt.Key_S or key == 'S':
|
||||
self.app.editor2object()
|
||||
return
|
||||
|
||||
# toggle the measurement tool
|
||||
if key == QtCore.Qt.Key_M or key == 'M':
|
||||
self.app.measurement_tool.run()
|
||||
return
|
||||
|
||||
elif modifiers == QtCore.Qt.ShiftModifier:
|
||||
pass
|
||||
elif modifiers == QtCore.Qt.AltModifier:
|
||||
# Transformation Tool
|
||||
if key == QtCore.Qt.Key_R or key == 'R':
|
||||
self.app.grb_editor.on_transform()
|
||||
return
|
||||
|
||||
elif modifiers == QtCore.Qt.NoModifier:
|
||||
# Abort the current action
|
||||
if key == QtCore.Qt.Key_Escape or key == 'Escape':
|
||||
# self.on_tool_select("select")
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] Cancelled."))
|
||||
|
||||
self.app.grb_editor.delete_utility_geometry()
|
||||
|
||||
# self.app.grb_editor.plot_all()
|
||||
self.app.grb_editor.active_tool.clean_up()
|
||||
self.app.grb_editor.select_tool('select')
|
||||
return
|
||||
|
||||
# Delete selected object if delete key event comes out of canvas
|
||||
if key == 'Delete':
|
||||
self.app.grb_editor.launched_from_shortcuts = True
|
||||
if self.app.grb_editor.selected:
|
||||
self.app.grb_editor.delete_selected()
|
||||
self.app.grb_editor.plot_all()
|
||||
else:
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] Cancelled. Nothing selected to delete."))
|
||||
return
|
||||
|
||||
# Delete aperture in apertures table if delete key event comes from the Selected Tab
|
||||
if key == QtCore.Qt.Key_Delete:
|
||||
self.app.grb_editor.launched_from_shortcuts = True
|
||||
self.app.grb_editor.on_aperture_delete()
|
||||
return
|
||||
|
||||
if key == QtCore.Qt.Key_Minus or key == '-':
|
||||
self.app.grb_editor.launched_from_shortcuts = True
|
||||
self.app.plotcanvas.zoom(1 / self.app.defaults['zoom_ratio'],
|
||||
[self.app.grb_editor.snap_x, self.app.grb_editor.snap_y])
|
||||
return
|
||||
|
||||
if key == QtCore.Qt.Key_Equal or key == '=':
|
||||
self.app.grb_editor.launched_from_shortcuts = True
|
||||
self.app.plotcanvas.zoom(self.app.defaults['zoom_ratio'],
|
||||
[self.app.grb_editor.snap_x, self.app.grb_editor.snap_y])
|
||||
return
|
||||
|
||||
# toggle display of Notebook area
|
||||
if key == QtCore.Qt.Key_QuoteLeft or key == '`':
|
||||
self.app.grb_editor.launched_from_shortcuts = True
|
||||
self.app.on_toggle_notebook()
|
||||
return
|
||||
|
||||
# Rotate
|
||||
if key == QtCore.Qt.Key_Space or key == 'Space':
|
||||
self.app.grb_editor.transform_tool.on_rotate_key()
|
||||
|
||||
# Switch to Project Tab
|
||||
if key == QtCore.Qt.Key_1 or key == '1':
|
||||
self.app.grb_editor.launched_from_shortcuts = True
|
||||
self.app.on_select_tab('project')
|
||||
return
|
||||
|
||||
# Switch to Selected Tab
|
||||
if key == QtCore.Qt.Key_2 or key == '2':
|
||||
self.app.grb_editor.launched_from_shortcuts = True
|
||||
self.app.on_select_tab('selected')
|
||||
return
|
||||
|
||||
# Switch to Tool Tab
|
||||
if key == QtCore.Qt.Key_3 or key == '3':
|
||||
self.app.grb_editor.launched_from_shortcuts = True
|
||||
self.app.on_select_tab('tool')
|
||||
return
|
||||
|
||||
# Add Array of pads
|
||||
if key == QtCore.Qt.Key_A or key == 'A':
|
||||
self.app.grb_editor.launched_from_shortcuts = True
|
||||
self.app.inform.emit("Click on target point.")
|
||||
self.app.ui.add_pad_ar_btn.setChecked(True)
|
||||
|
||||
self.app.grb_editor.x = self.app.mouse[0]
|
||||
self.app.grb_editor.y = self.app.mouse[1]
|
||||
|
||||
self.app.grb_editor.select_tool('array')
|
||||
return
|
||||
|
||||
# Scale Tool
|
||||
if key == QtCore.Qt.Key_B or key == 'B':
|
||||
self.app.grb_editor.launched_from_shortcuts = True
|
||||
self.app.grb_editor.select_tool('buffer')
|
||||
return
|
||||
|
||||
# Copy
|
||||
if key == QtCore.Qt.Key_C or key == 'C':
|
||||
self.app.grb_editor.launched_from_shortcuts = True
|
||||
if self.app.grb_editor.selected:
|
||||
self.app.inform.emit(_("Click on target point."))
|
||||
self.app.ui.aperture_copy_btn.setChecked(True)
|
||||
self.app.grb_editor.on_tool_select('copy')
|
||||
self.app.grb_editor.active_tool.set_origin(
|
||||
(self.app.grb_editor.snap_x, self.app.grb_editor.snap_y))
|
||||
else:
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] Cancelled. Nothing selected to copy."))
|
||||
return
|
||||
|
||||
# Grid Snap
|
||||
if key == QtCore.Qt.Key_G or key == 'G':
|
||||
self.app.grb_editor.launched_from_shortcuts = True
|
||||
# make sure that the cursor shape is enabled/disabled, too
|
||||
if self.app.grb_editor.options['grid_snap'] is True:
|
||||
self.app.app_cursor.enabled = False
|
||||
else:
|
||||
self.app.app_cursor.enabled = True
|
||||
self.app.ui.grid_snap_btn.trigger()
|
||||
return
|
||||
|
||||
# Jump to coords
|
||||
if key == QtCore.Qt.Key_J or key == 'J':
|
||||
self.app.on_jump_to()
|
||||
|
||||
# Corner Snap
|
||||
if key == QtCore.Qt.Key_K or key == 'K':
|
||||
self.app.grb_editor.launched_from_shortcuts = True
|
||||
self.app.ui.corner_snap_btn.trigger()
|
||||
return
|
||||
|
||||
# Move
|
||||
if key == QtCore.Qt.Key_M or key == 'M':
|
||||
self.app.grb_editor.launched_from_shortcuts = True
|
||||
if self.app.grb_editor.selected:
|
||||
self.app.inform.emit(_("Click on target point."))
|
||||
self.app.ui.aperture_move_btn.setChecked(True)
|
||||
self.app.grb_editor.on_tool_select('move')
|
||||
self.app.grb_editor.active_tool.set_origin(
|
||||
(self.app.grb_editor.snap_x, self.app.grb_editor.snap_y))
|
||||
else:
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] Cancelled. Nothing selected to move."))
|
||||
return
|
||||
|
||||
# Add Region Tool
|
||||
if key == QtCore.Qt.Key_N or key == 'N':
|
||||
self.app.grb_editor.launched_from_shortcuts = True
|
||||
self.app.grb_editor.select_tool('region')
|
||||
return
|
||||
|
||||
# Add Pad Tool
|
||||
if key == QtCore.Qt.Key_P or key == 'P':
|
||||
self.app.grb_editor.launched_from_shortcuts = True
|
||||
self.app.inform.emit(_("Click on target point."))
|
||||
self.app.ui.add_pad_ar_btn.setChecked(True)
|
||||
|
||||
self.app.grb_editor.x = self.app.mouse[0]
|
||||
self.app.grb_editor.y = self.app.mouse[1]
|
||||
|
||||
self.app.grb_editor.select_tool('pad')
|
||||
return
|
||||
|
||||
# Scale Tool
|
||||
if key == QtCore.Qt.Key_S or key == 'S':
|
||||
self.app.grb_editor.launched_from_shortcuts = True
|
||||
self.app.grb_editor.select_tool('scale')
|
||||
return
|
||||
|
||||
# Add Track
|
||||
if key == QtCore.Qt.Key_T or key == 'T':
|
||||
self.app.grb_editor.launched_from_shortcuts = True
|
||||
## Current application units in Upper Case
|
||||
self.app.grb_editor.select_tool('track')
|
||||
return
|
||||
|
||||
# Zoom Fit
|
||||
if key == QtCore.Qt.Key_V or key == 'V':
|
||||
self.app.grb_editor.launched_from_shortcuts = True
|
||||
self.app.on_zoom_fit(None)
|
||||
return
|
||||
|
||||
# Propagate to tool
|
||||
response = None
|
||||
if self.app.grb_editor.active_tool is not None:
|
||||
response = self.app.grb_editor.active_tool.on_key(key=key)
|
||||
if response is not None:
|
||||
self.app.inform.emit(response)
|
||||
|
||||
# Show Shortcut list
|
||||
if key == QtCore.Qt.Key_F3 or key == 'F3':
|
||||
self.app.on_shortcut_list()
|
||||
return
|
||||
elif self.app.call_source == 'exc_editor':
|
||||
if modifiers == QtCore.Qt.ControlModifier:
|
||||
# save (update) the current geometry and return to the App
|
||||
|
@ -2325,7 +2733,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
if key == QtCore.Qt.Key_Escape or key == 'Escape':
|
||||
# TODO: ...?
|
||||
# self.on_tool_select("select")
|
||||
self.app.inform.emit(_("[WARNING_NOTCL]Cancelled."))
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] Cancelled."))
|
||||
|
||||
self.app.exc_editor.delete_utility_geometry()
|
||||
|
||||
|
@ -2342,7 +2750,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.app.exc_editor.delete_selected()
|
||||
self.app.exc_editor.replot()
|
||||
else:
|
||||
self.app.inform.emit(_("[WARNING_NOTCL]Cancelled. Nothing selected to delete."))
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] Cancelled. Nothing selected to delete."))
|
||||
return
|
||||
|
||||
# Delete tools in tools table if delete key event comes from the Selected Tab
|
||||
|
@ -2409,7 +2817,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.app.exc_editor.active_tool.set_origin(
|
||||
(self.app.exc_editor.snap_x, self.app.exc_editor.snap_y))
|
||||
else:
|
||||
self.app.inform.emit(_("[WARNING_NOTCL]Cancelled. Nothing selected to copy."))
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] Cancelled. Nothing selected to copy."))
|
||||
return
|
||||
|
||||
# Add Drill Hole Tool
|
||||
|
@ -2455,7 +2863,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
self.app.exc_editor.active_tool.set_origin(
|
||||
(self.app.exc_editor.snap_x, self.app.exc_editor.snap_y))
|
||||
else:
|
||||
self.app.inform.emit(_("[WARNING_NOTCL]Cancelled. Nothing selected to move."))
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] Cancelled. Nothing selected to move."))
|
||||
return
|
||||
|
||||
# Resize Tool
|
||||
|
@ -2478,7 +2886,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
if ok:
|
||||
self.app.exc_editor.on_tool_add(tooldia=val)
|
||||
self.app.inform.emit(
|
||||
_("[success]Added new tool with dia: {dia} {units}").format(dia='%.4f' % float(val), units=str(self.units)))
|
||||
_("[success] Added new tool with dia: {dia} {units}").format(dia='%.4f' % float(val), units=str(self.units)))
|
||||
else:
|
||||
self.app.inform.emit(
|
||||
_("[WARNING_NOTCL] Adding Tool cancelled ..."))
|
||||
|
@ -2524,35 +2932,37 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
if self.filename == "":
|
||||
self.app.inform.emit("Open cancelled.")
|
||||
else:
|
||||
if self.filename.lower().rpartition('.')[-1] in self.app.grb_list:
|
||||
extension = self.filename.lower().rpartition('.')[-1]
|
||||
|
||||
if extension in self.app.grb_list:
|
||||
self.app.worker_task.emit({'fcn': self.app.open_gerber,
|
||||
'params': [self.filename]})
|
||||
else:
|
||||
event.ignore()
|
||||
|
||||
if self.filename.lower().rpartition('.')[-1] in self.app.exc_list:
|
||||
if extension in self.app.exc_list:
|
||||
self.app.worker_task.emit({'fcn': self.app.open_excellon,
|
||||
'params': [self.filename]})
|
||||
else:
|
||||
event.ignore()
|
||||
|
||||
if self.filename.lower().rpartition('.')[-1] in self.app.gcode_list:
|
||||
if extension in self.app.gcode_list:
|
||||
self.app.worker_task.emit({'fcn': self.app.open_gcode,
|
||||
'params': [self.filename]})
|
||||
else:
|
||||
event.ignore()
|
||||
|
||||
if self.filename.lower().rpartition('.')[-1] in self.app.svg_list:
|
||||
if extension in self.app.svg_list:
|
||||
object_type = 'geometry'
|
||||
self.app.worker_task.emit({'fcn': self.app.import_svg,
|
||||
'params': [self.filename, object_type, None]})
|
||||
|
||||
if self.filename.lower().rpartition('.')[-1] in self.app.dxf_list:
|
||||
if extension in self.app.dxf_list:
|
||||
object_type = 'geometry'
|
||||
self.app.worker_task.emit({'fcn': self.app.import_dxf,
|
||||
'params': [self.filename, object_type, None]})
|
||||
|
||||
if self.filename.lower().rpartition('.')[-1] in self.app.prj_list:
|
||||
if extension in self.app.prj_list:
|
||||
# self.app.open_project() is not Thread Safe
|
||||
self.app.open_project(self.filename)
|
||||
else:
|
||||
|
@ -2561,16 +2971,17 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
|
|||
event.ignore()
|
||||
|
||||
def closeEvent(self, event):
|
||||
grect = self.geometry()
|
||||
if self.app.save_in_progress:
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] Application is saving the project. Please wait ..."))
|
||||
else:
|
||||
grect = self.geometry()
|
||||
|
||||
# self.splitter.sizes()[0] is actually the size of the "notebook"
|
||||
if not self.isMaximized():
|
||||
self.geom_update.emit(grect.x(), grect.y(), grect.width(), grect.height(), self.splitter.sizes()[0])
|
||||
# self.splitter.sizes()[0] is actually the size of the "notebook"
|
||||
if not self.isMaximized():
|
||||
self.geom_update.emit(grect.x(), grect.y(), grect.width(), grect.height(), self.splitter.sizes()[0])
|
||||
|
||||
self.final_save.emit()
|
||||
|
||||
if self.app.should_we_quit is False:
|
||||
event.ignore()
|
||||
self.final_save.emit()
|
||||
event.ignore()
|
||||
|
||||
|
||||
class GeneralPreferencesUI(QtWidgets.QWidget):
|
||||
|
@ -3034,8 +3445,8 @@ class GeneralGUISetGroupUI(OptionsGroupUI):
|
|||
)
|
||||
self.layout_combo = FCComboBox()
|
||||
# don't translate the QCombo items as they are used in QSettings and identified by name
|
||||
self.layout_combo.addItem("Standard")
|
||||
self.layout_combo.addItem("Compact")
|
||||
self.layout_combo.addItem("standard")
|
||||
self.layout_combo.addItem("compact")
|
||||
|
||||
# Set the current index for layout_combo
|
||||
settings = QSettings("Open Source", "FlatCAM")
|
||||
|
@ -3124,24 +3535,24 @@ class GeneralGUISetGroupUI(OptionsGroupUI):
|
|||
|
||||
def handle_clear(self):
|
||||
msgbox = QtWidgets.QMessageBox()
|
||||
# msgbox.setText("<B>Save changes ...</B>")
|
||||
msgbox.setText(_("Are you sure you want to delete the GUI Settings? "
|
||||
"\n")
|
||||
)
|
||||
msgbox.setWindowTitle(_("Clear GUI Settings"))
|
||||
msgbox.setWindowIcon(QtGui.QIcon('share/trash32.png'))
|
||||
msgbox.setStandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
|
||||
msgbox.setDefaultButton(QtWidgets.QMessageBox.No)
|
||||
bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole)
|
||||
bt_no = msgbox.addButton(_('No'), QtWidgets.QMessageBox.NoRole)
|
||||
|
||||
response = msgbox.exec_()
|
||||
msgbox.setDefaultButton(bt_no)
|
||||
msgbox.exec_()
|
||||
response = msgbox.clickedButton()
|
||||
|
||||
if response == QtWidgets.QMessageBox.Yes:
|
||||
if response == bt_yes:
|
||||
settings = QSettings("Open Source", "FlatCAM")
|
||||
for key in settings.allKeys():
|
||||
settings.remove(key)
|
||||
# This will write the setting to the platform specific storage.
|
||||
del settings
|
||||
self.app.inform.emit(_("[success] GUI settings deleted ..."))
|
||||
|
||||
|
||||
class GeneralAppPrefGroupUI(OptionsGroupUI):
|
||||
|
@ -3267,6 +3678,25 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
|
|||
_( "Check this box if you want to have toolTips displayed\n"
|
||||
"when hovering with mouse over items throughout the App.")
|
||||
)
|
||||
self.worker_number_label = QtWidgets.QLabel(_('Workers number:'))
|
||||
self.worker_number_label.setToolTip(
|
||||
_("The number of Qthreads made available to the App.\n"
|
||||
"A bigger number may finish the jobs more quickly but\n"
|
||||
"depending on your computer speed, may make the App\n"
|
||||
"unresponsive. Can have a value between 2 and 16.\n"
|
||||
"Default value is 2.\n"
|
||||
"After change, it will be applied at next App start.")
|
||||
)
|
||||
self.worker_number_sb = FCSpinner()
|
||||
self.worker_number_sb.setToolTip(
|
||||
_("The number of Qthreads made available to the App.\n"
|
||||
"A bigger number may finish the jobs more quickly but\n"
|
||||
"depending on your computer speed, may make the App\n"
|
||||
"unresponsive. Can have a value between 2 and 16.\n"
|
||||
"Default value is 2.\n"
|
||||
"After change, it will be applied at next App start.")
|
||||
)
|
||||
self.worker_number_sb.set_range(2, 16)
|
||||
|
||||
# Just to add empty rows
|
||||
self.spacelabel = QtWidgets.QLabel('')
|
||||
|
@ -3287,6 +3717,8 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
|
|||
self.form_box.addRow(self.project_startup_label, self.project_startup_cb)
|
||||
self.form_box.addRow(self.project_autohide_label, self.project_autohide_cb)
|
||||
self.form_box.addRow(self.toggle_tooltips_label, self.toggle_tooltips_cb)
|
||||
self.form_box.addRow(self.worker_number_label, self.worker_number_sb)
|
||||
|
||||
self.form_box.addRow(self.spacelabel, self.spacelabel)
|
||||
|
||||
# Add the QFormLayout that holds the Application general defaults
|
||||
|
@ -4787,7 +5219,7 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
|
|||
self.ncc_tool_dia_entry = FCEntry()
|
||||
grid0.addWidget(self.ncc_tool_dia_entry, 0, 1)
|
||||
|
||||
nccoverlabel = QtWidgets.QLabel(_('Overlap:'))
|
||||
nccoverlabel = QtWidgets.QLabel(_('Overlap Rate:'))
|
||||
nccoverlabel.setToolTip(
|
||||
_( "How much (fraction) of the tool width to overlap each tool pass.\n"
|
||||
"Example:\n"
|
||||
|
@ -4929,6 +5361,15 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI):
|
|||
self.gaps_combo.addItem(it)
|
||||
self.gaps_combo.setStyleSheet('background-color: rgb(255,255,255)')
|
||||
|
||||
# Surrounding convex box shape
|
||||
self.convex_box = FCCheckBox()
|
||||
self.convex_box_label = QtWidgets.QLabel(_("Convex Sh.:"))
|
||||
self.convex_box_label.setToolTip(
|
||||
_("Create a convex shape surrounding the entire PCB.")
|
||||
)
|
||||
grid0.addWidget(self.convex_box_label, 4, 0)
|
||||
grid0.addWidget(self.convex_box, 4, 1)
|
||||
|
||||
self.layout.addStretch()
|
||||
|
||||
|
||||
|
@ -5023,7 +5464,7 @@ class ToolsPaintPrefGroupUI(OptionsGroupUI):
|
|||
grid0.addWidget(self.painttooldia_entry, 0, 1)
|
||||
|
||||
# Overlap
|
||||
ovlabel = QtWidgets.QLabel(_('Overlap:'))
|
||||
ovlabel = QtWidgets.QLabel(_('Overlap Rate:'))
|
||||
ovlabel.setToolTip(
|
||||
_("How much (fraction) of the tool\n"
|
||||
"width to overlap each tool pass.")
|
||||
|
|
|
@ -490,6 +490,106 @@ class FCTextAreaRich(QtWidgets.QTextEdit):
|
|||
return QtCore.QSize(EDIT_SIZE_HINT, default_hint_size.height())
|
||||
|
||||
|
||||
class FCTextAreaExtended(QtWidgets.QTextEdit):
|
||||
def __init__(self, parent=None):
|
||||
super(FCTextAreaExtended, self).__init__(parent)
|
||||
|
||||
self.completer = MyCompleter()
|
||||
|
||||
self.model = QtCore.QStringListModel()
|
||||
self.completer.setModel(self.model)
|
||||
self.set_model_data(keyword_list=[])
|
||||
self.completer.insertText.connect(self.insertCompletion)
|
||||
|
||||
self.completer_enable = False
|
||||
|
||||
def set_model_data(self, keyword_list):
|
||||
self.model.setStringList(keyword_list)
|
||||
|
||||
def insertCompletion(self, completion):
|
||||
tc = self.textCursor()
|
||||
extra = (len(completion) - len(self.completer.completionPrefix()))
|
||||
|
||||
# don't insert if the word is finished but add a space instead
|
||||
if extra == 0:
|
||||
tc.insertText(' ')
|
||||
self.completer.popup().hide()
|
||||
return
|
||||
|
||||
tc.movePosition(QTextCursor.Left)
|
||||
tc.movePosition(QTextCursor.EndOfWord)
|
||||
tc.insertText(completion[-extra:])
|
||||
# add a space after inserting the word
|
||||
tc.insertText(' ')
|
||||
self.setTextCursor(tc)
|
||||
self.completer.popup().hide()
|
||||
|
||||
def focusInEvent(self, event):
|
||||
if self.completer:
|
||||
self.completer.setWidget(self)
|
||||
QTextEdit.focusInEvent(self, event)
|
||||
|
||||
def set_value(self, val):
|
||||
self.setText(val)
|
||||
|
||||
def get_value(self):
|
||||
self.toPlainText()
|
||||
|
||||
def insertFromMimeData(self, data):
|
||||
"""
|
||||
Reimplemented such that when SHIFT is pressed and doing click Paste in the contextual menu, the '\' symbol
|
||||
is replaced with the '/' symbol. That's because of the difference in path separators in Windows and TCL
|
||||
:param data:
|
||||
:return:
|
||||
"""
|
||||
modifier = QtWidgets.QApplication.keyboardModifiers()
|
||||
if modifier == Qt.ShiftModifier:
|
||||
text = data.text()
|
||||
text = text.replace('\\', '/')
|
||||
self.insertPlainText(text)
|
||||
else:
|
||||
self.insertPlainText(data.text())
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
"""
|
||||
Reimplemented so the CTRL + SHIFT + V shortcut key combo will paste the text but replacing '\' with '/'
|
||||
:param event:
|
||||
:return:
|
||||
"""
|
||||
key = event.key()
|
||||
modifier = QtWidgets.QApplication.keyboardModifiers()
|
||||
|
||||
if modifier & Qt.ControlModifier and modifier & Qt.ShiftModifier:
|
||||
if key == QtCore.Qt.Key_V:
|
||||
clipboard = QtWidgets.QApplication.clipboard()
|
||||
clip_text = clipboard.text()
|
||||
clip_text = clip_text.replace('\\', '/')
|
||||
self.insertPlainText(clip_text)
|
||||
|
||||
tc = self.textCursor()
|
||||
if (key == Qt.Key_Tab or key == Qt.Key_Enter or key == Qt.Key_Return) and self.completer.popup().isVisible():
|
||||
self.completer.insertText.emit(self.completer.getSelected())
|
||||
self.completer.setCompletionMode(QCompleter.PopupCompletion)
|
||||
return
|
||||
else:
|
||||
super(FCTextAreaExtended, self).keyPressEvent(event)
|
||||
|
||||
if self.completer_enable:
|
||||
tc.select(QTextCursor.WordUnderCursor)
|
||||
cr = self.cursorRect()
|
||||
|
||||
if len(tc.selectedText()) > 0:
|
||||
self.completer.setCompletionPrefix(tc.selectedText())
|
||||
popup = self.completer.popup()
|
||||
popup.setCurrentIndex(self.completer.completionModel().index(0, 0))
|
||||
|
||||
cr.setWidth(self.completer.popup().sizeHintForColumn(0)
|
||||
+ self.completer.popup().verticalScrollBar().sizeHint().width())
|
||||
self.completer.complete(cr)
|
||||
else:
|
||||
self.completer.popup().hide()
|
||||
|
||||
|
||||
class FCComboBox(QtWidgets.QComboBox):
|
||||
|
||||
def __init__(self, parent=None, callback=None):
|
||||
|
@ -595,9 +695,12 @@ class FCTab(QtWidgets.QTabWidget):
|
|||
|
||||
|
||||
class FCDetachableTab(QtWidgets.QTabWidget):
|
||||
# From here: https://stackoverflow.com/questions/47267195/in-pyqt4-is-it-possible-to-detach-tabs-from-a-qtabwidget
|
||||
def __init__(self, protect=None, protect_by_name=None, parent=None):
|
||||
"""
|
||||
From here:
|
||||
https://stackoverflow.com/questions/47267195/in-pyqt4-is-it-possible-to-detach-tabs-from-a-qtabwidget
|
||||
"""
|
||||
|
||||
def __init__(self, protect=None, protect_by_name=None, parent=None):
|
||||
super().__init__()
|
||||
|
||||
self.tabBar = self.FCTabBar(self)
|
||||
|
@ -639,25 +742,38 @@ class FCDetachableTab(QtWidgets.QTabWidget):
|
|||
self.removeTab(currentIndex)
|
||||
|
||||
def closeTab(self, currentIndex):
|
||||
"""
|
||||
Slot connected to the tabCloseRequested signal
|
||||
|
||||
:param currentIndex:
|
||||
:return:
|
||||
"""
|
||||
self.removeTab(currentIndex)
|
||||
|
||||
def protectTab(self, currentIndex):
|
||||
# self.FCTabBar().setTabButton(currentIndex, QtWidgets.QTabBar.RightSide, None)
|
||||
self.tabBar.setTabButton(currentIndex, QtWidgets.QTabBar.RightSide, None)
|
||||
|
||||
##
|
||||
# The default movable functionality of QTabWidget must remain disabled
|
||||
# so as not to conflict with the added features
|
||||
def setMovable(self, movable):
|
||||
"""
|
||||
The default movable functionality of QTabWidget must remain disabled
|
||||
so as not to conflict with the added features
|
||||
|
||||
:param movable:
|
||||
:return:
|
||||
"""
|
||||
pass
|
||||
|
||||
##
|
||||
# Move a tab from one position (index) to another
|
||||
#
|
||||
# @param fromIndex the original index location of the tab
|
||||
# @param toIndex the new index location of the tab
|
||||
@pyqtSlot(int, int)
|
||||
def moveTab(self, fromIndex, toIndex):
|
||||
"""
|
||||
Move a tab from one position (index) to another
|
||||
|
||||
:param fromIndex: the original index location of the tab
|
||||
:param toIndex: the new index location of the tab
|
||||
:return:
|
||||
"""
|
||||
|
||||
widget = self.widget(fromIndex)
|
||||
icon = self.tabIcon(fromIndex)
|
||||
text = self.tabText(fromIndex)
|
||||
|
@ -666,15 +782,16 @@ class FCDetachableTab(QtWidgets.QTabWidget):
|
|||
self.insertTab(toIndex, widget, icon, text)
|
||||
self.setCurrentIndex(toIndex)
|
||||
|
||||
##
|
||||
# Detach the tab by removing it's contents and placing them in
|
||||
# a DetachedTab window
|
||||
#
|
||||
# @param index the index location of the tab to be detached
|
||||
# @param point the screen position for creating the new DetachedTab window
|
||||
@pyqtSlot(int, QtCore.QPoint)
|
||||
def detachTab(self, index, point):
|
||||
"""
|
||||
Detach the tab by removing it's contents and placing them in
|
||||
a DetachedTab window
|
||||
|
||||
:param index: the index location of the tab to be detached
|
||||
:param point: the screen position for creating the new DetachedTab window
|
||||
:return:
|
||||
"""
|
||||
self.old_index = index
|
||||
|
||||
# Get the tab content and add name FlatCAM to the tab so we know on which app is this tab linked
|
||||
|
@ -699,20 +816,20 @@ class FCDetachableTab(QtWidgets.QTabWidget):
|
|||
detachedTab.move(point)
|
||||
detachedTab.show()
|
||||
|
||||
|
||||
# Create a reference to maintain access to the detached tab
|
||||
self.detachedTabs[name] = detachedTab
|
||||
|
||||
|
||||
##
|
||||
# Re-attach the tab by removing the content from the DetachedTab window,
|
||||
# closing it, and placing the content back into the DetachableTabWidget
|
||||
#
|
||||
# @param contentWidget the content widget from the DetachedTab window
|
||||
# @param name the name of the detached tab
|
||||
# @param icon the window icon for the detached tab
|
||||
# @param insertAt insert the re-attached tab at the given index
|
||||
def attachTab(self, contentWidget, name, icon, insertAt=None):
|
||||
"""
|
||||
Re-attach the tab by removing the content from the DetachedTab window,
|
||||
closing it, and placing the content back into the DetachableTabWidget
|
||||
|
||||
:param contentWidget: the content widget from the DetachedTab window
|
||||
:param name: the name of the detached tab
|
||||
:param icon: the window icon for the detached tab
|
||||
:param insertAt: insert the re-attached tab at the given index
|
||||
:return:
|
||||
"""
|
||||
|
||||
# Make the content widget a child of this widget
|
||||
contentWidget.setParent(self)
|
||||
|
@ -773,11 +890,13 @@ class FCDetachableTab(QtWidgets.QTabWidget):
|
|||
if index > -1:
|
||||
self.setCurrentIndex(insert_index) if self.use_old_index else self.setCurrentIndex(index)
|
||||
|
||||
##
|
||||
# Remove the tab with the given name, even if it is detached
|
||||
#
|
||||
# @param name the name of the tab to be removed
|
||||
def removeTabByName(self, name):
|
||||
"""
|
||||
Remove the tab with the given name, even if it is detached
|
||||
|
||||
:param name: the name of the tab to be removed
|
||||
:return:
|
||||
"""
|
||||
|
||||
# Remove the tab if it is attached
|
||||
attached = False
|
||||
|
@ -798,17 +917,18 @@ class FCDetachableTab(QtWidgets.QTabWidget):
|
|||
del self.detachedTabs[key]
|
||||
break
|
||||
|
||||
|
||||
##
|
||||
# Handle dropping of a detached tab inside the DetachableTabWidget
|
||||
#
|
||||
# @param name the name of the detached tab
|
||||
# @param index the index of an existing tab (if the tab bar
|
||||
# determined that the drop occurred on an
|
||||
# existing tab)
|
||||
# @param dropPos the mouse cursor position when the drop occurred
|
||||
@QtCore.pyqtSlot(str, int, QtCore.QPoint)
|
||||
def detachedTabDrop(self, name, index, dropPos):
|
||||
"""
|
||||
Handle dropping of a detached tab inside the DetachableTabWidget
|
||||
|
||||
:param name: the name of the detached tab
|
||||
:param index: the index of an existing tab (if the tab bar
|
||||
# determined that the drop occurred on an
|
||||
# existing tab)
|
||||
:param dropPos: the mouse cursor position when the drop occurred
|
||||
:return:
|
||||
"""
|
||||
|
||||
# If the drop occurred on an existing tab, insert the detached
|
||||
# tab at the existing tab's location
|
||||
|
@ -848,10 +968,12 @@ class FCDetachableTab(QtWidgets.QTabWidget):
|
|||
# automatically
|
||||
self.detachedTabs[name].close()
|
||||
|
||||
|
||||
##
|
||||
# Close all tabs that are currently detached.
|
||||
def closeDetachedTabs(self):
|
||||
"""
|
||||
Close all tabs that are currently detached.
|
||||
|
||||
:return:
|
||||
"""
|
||||
listOfDetachedTabs = []
|
||||
|
||||
for key in self.detachedTabs:
|
||||
|
@ -860,11 +982,12 @@ class FCDetachableTab(QtWidgets.QTabWidget):
|
|||
for detachedTab in listOfDetachedTabs:
|
||||
detachedTab.close()
|
||||
|
||||
|
||||
##
|
||||
# When a tab is detached, the contents are placed into this QMainWindow. The tab
|
||||
# can be re-attached by closing the dialog or by dragging the window into the tab bar
|
||||
class FCDetachedTab(QtWidgets.QMainWindow):
|
||||
"""
|
||||
When a tab is detached, the contents are placed into this QMainWindow. The tab
|
||||
can be re-attached by closing the dialog or by dragging the window into the tab bar
|
||||
"""
|
||||
|
||||
onCloseSignal = pyqtSignal(QtWidgets.QWidget, str, QtGui.QIcon)
|
||||
onDropSignal = pyqtSignal(str, QtCore.QPoint)
|
||||
|
||||
|
@ -882,42 +1005,46 @@ class FCDetachableTab(QtWidgets.QTabWidget):
|
|||
self.installEventFilter(self.windowDropFilter)
|
||||
self.windowDropFilter.onDropSignal.connect(self.windowDropSlot)
|
||||
|
||||
|
||||
##
|
||||
# Handle a window drop event
|
||||
#
|
||||
# @param dropPos the mouse cursor position of the drop
|
||||
@QtCore.pyqtSlot(QtCore.QPoint)
|
||||
def windowDropSlot(self, dropPos):
|
||||
"""
|
||||
Handle a window drop event
|
||||
|
||||
:param dropPos: the mouse cursor position of the drop
|
||||
:return:
|
||||
"""
|
||||
self.onDropSignal.emit(self.objectName(), dropPos)
|
||||
|
||||
|
||||
##
|
||||
# If the window is closed, emit the onCloseSignal and give the
|
||||
# content widget back to the DetachableTabWidget
|
||||
#
|
||||
# @param event a close event
|
||||
def closeEvent(self, event):
|
||||
"""
|
||||
If the window is closed, emit the onCloseSignal and give the
|
||||
content widget back to the DetachableTabWidget
|
||||
|
||||
:param event: a close event
|
||||
:return:
|
||||
"""
|
||||
self.onCloseSignal.emit(self.contentWidget, self.objectName(), self.windowIcon())
|
||||
|
||||
|
||||
##
|
||||
# An event filter class to detect a QMainWindow drop event
|
||||
class WindowDropFilter(QtCore.QObject):
|
||||
"""
|
||||
An event filter class to detect a QMainWindow drop event
|
||||
"""
|
||||
|
||||
onDropSignal = pyqtSignal(QtCore.QPoint)
|
||||
|
||||
def __init__(self):
|
||||
QtCore.QObject.__init__(self)
|
||||
self.lastEvent = None
|
||||
|
||||
|
||||
##
|
||||
# Detect a QMainWindow drop event by looking for a NonClientAreaMouseMove (173)
|
||||
# event that immediately follows a Move event
|
||||
#
|
||||
# @param obj the object that generated the event
|
||||
# @param event the current event
|
||||
def eventFilter(self, obj, event):
|
||||
"""
|
||||
Detect a QMainWindow drop event by looking for a NonClientAreaMouseMove (173)
|
||||
event that immediately follows a Move event
|
||||
|
||||
:param obj: the object that generated the event
|
||||
:param event: the current event
|
||||
:return:
|
||||
"""
|
||||
|
||||
# If a NonClientAreaMouseMove (173) event immediately follows a Move event...
|
||||
if self.lastEvent == QtCore.QEvent.Move and event.type() == 173:
|
||||
|
@ -951,19 +1078,24 @@ class FCDetachableTab(QtWidgets.QTabWidget):
|
|||
self.mouseCursor = QtGui.QCursor()
|
||||
self.dragInitiated = False
|
||||
|
||||
|
||||
# Send the onDetachTabSignal when a tab is double clicked
|
||||
#
|
||||
# @param event a mouse double click event
|
||||
def mouseDoubleClickEvent(self, event):
|
||||
"""
|
||||
Send the onDetachTabSignal when a tab is double clicked
|
||||
|
||||
:param event: a mouse double click event
|
||||
:return:
|
||||
"""
|
||||
|
||||
event.accept()
|
||||
self.onDetachTabSignal.emit(self.tabAt(event.pos()), self.mouseCursor.pos())
|
||||
|
||||
|
||||
# Set the starting position for a drag event when the mouse button is pressed
|
||||
#
|
||||
# @param event a mouse press event
|
||||
def mousePressEvent(self, event):
|
||||
"""
|
||||
Set the starting position for a drag event when the mouse button is pressed
|
||||
|
||||
:param event: a mouse press event
|
||||
:return:
|
||||
"""
|
||||
if event.button() == QtCore.Qt.LeftButton:
|
||||
self.dragStartPos = event.pos()
|
||||
|
||||
|
@ -974,14 +1106,15 @@ class FCDetachableTab(QtWidgets.QTabWidget):
|
|||
|
||||
QtWidgets.QTabBar.mousePressEvent(self, event)
|
||||
|
||||
|
||||
# Determine if the current movement is a drag. If it is, convert it into a QDrag. If the
|
||||
# drag ends inside the tab bar, emit an onMoveTabSignal. If the drag ends outside the tab
|
||||
# bar, emit an onDetachTabSignal.
|
||||
#
|
||||
# @param event a mouse move event
|
||||
def mouseMoveEvent(self, event):
|
||||
"""
|
||||
Determine if the current movement is a drag. If it is, convert it into a QDrag. If the
|
||||
drag ends inside the tab bar, emit an onMoveTabSignal. If the drag ends outside the tab
|
||||
bar, emit an onDetachTabSignal.
|
||||
|
||||
:param event: a mouse move event
|
||||
:return:
|
||||
"""
|
||||
# Determine if the current movement is detected as a drag
|
||||
if not self.dragStartPos.isNull() and ((event.pos() - self.dragStartPos).manhattanLength() < QtWidgets.QApplication.startDragDistance()):
|
||||
self.dragInitiated = True
|
||||
|
@ -1038,29 +1171,40 @@ class FCDetachableTab(QtWidgets.QTabWidget):
|
|||
else:
|
||||
QtWidgets.QTabBar.mouseMoveEvent(self, event)
|
||||
|
||||
# Determine if the drag has entered a tab position from another tab position
|
||||
#
|
||||
# @param event a drag enter event
|
||||
def dragEnterEvent(self, event):
|
||||
"""
|
||||
Determine if the drag has entered a tab position from another tab position
|
||||
|
||||
:param event: a drag enter event
|
||||
:return:
|
||||
"""
|
||||
mimeData = event.mimeData()
|
||||
# formats = mcd imeData.formats()
|
||||
|
||||
# if formats.contains('action') and mimeData.data('action') == 'application/tab-detach':
|
||||
# event.acceptProposedAction()
|
||||
# if formats.contains('action') and mimeData.data('action') == 'application/tab-detach':
|
||||
# event.acceptProposedAction()
|
||||
|
||||
QtWidgets.QTabBar.dragMoveEvent(self, event)
|
||||
|
||||
# Get the position of the end of the drag
|
||||
#
|
||||
# @param event a drop event
|
||||
def dropEvent(self, event):
|
||||
"""
|
||||
Get the position of the end of the drag
|
||||
|
||||
:param event: a drop event
|
||||
:return:
|
||||
"""
|
||||
self.dragDropedPos = event.pos()
|
||||
QtWidgets.QTabBar.dropEvent(self, event)
|
||||
|
||||
# Determine if the detached tab drop event occurred on an existing tab,
|
||||
# then send the event to the DetachableTabWidget
|
||||
def detachedTabDrop(self, name, dropPos):
|
||||
"""
|
||||
Determine if the detached tab drop event occurred on an existing tab,
|
||||
then send the event to the DetachableTabWidget
|
||||
|
||||
:param name:
|
||||
:param dropPos:
|
||||
:return:
|
||||
"""
|
||||
tabDropPos = self.mapFromGlobal(dropPos)
|
||||
|
||||
index = self.tabAt(tabDropPos)
|
||||
|
@ -1192,9 +1336,64 @@ class FCTable(QtWidgets.QTableWidget):
|
|||
self.addAction(action)
|
||||
action.triggered.connect(call_function)
|
||||
|
||||
|
||||
class SpinBoxDelegate(QtWidgets.QItemDelegate):
|
||||
|
||||
def __init__(self, units):
|
||||
super(SpinBoxDelegate, self).__init__()
|
||||
self.units = units
|
||||
self.current_value = None
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
editor = QtWidgets.QDoubleSpinBox(parent)
|
||||
editor.setMinimum(-999.9999)
|
||||
editor.setMaximum(999.9999)
|
||||
|
||||
if self.units == 'MM':
|
||||
editor.setDecimals(2)
|
||||
else:
|
||||
editor.setDecimals(3)
|
||||
|
||||
return editor
|
||||
|
||||
def setEditorData(self, spinBox, index):
|
||||
try:
|
||||
value = float(index.model().data(index, Qt.EditRole))
|
||||
except ValueError:
|
||||
value = self.current_value
|
||||
# return
|
||||
|
||||
spinBox.setValue(value)
|
||||
|
||||
def setModelData(self, spinBox, model, index):
|
||||
spinBox.interpretText()
|
||||
value = spinBox.value()
|
||||
self.current_value = value
|
||||
|
||||
model.setData(index, value, Qt.EditRole)
|
||||
|
||||
def updateEditorGeometry(self, editor, option, index):
|
||||
editor.setGeometry(option.rect)
|
||||
|
||||
def setDecimals(self, spinbox, digits):
|
||||
spinbox.setDecimals(digits)
|
||||
|
||||
|
||||
class FCSpinner(QtWidgets.QSpinBox):
|
||||
def __init__(self, parent=None):
|
||||
super(FCSpinner, self).__init__(parent)
|
||||
self.readyToEdit = True
|
||||
|
||||
def mousePressEvent(self, e, parent=None):
|
||||
super(FCSpinner, self).mousePressEvent(e) # required to deselect on 2e click
|
||||
if self.readyToEdit:
|
||||
self.lineEdit().selectAll()
|
||||
self.readyToEdit = False
|
||||
|
||||
def focusOutEvent(self, e):
|
||||
super(FCSpinner, self).focusOutEvent(e) # required to remove cursor on focusOut
|
||||
self.lineEdit().deselect()
|
||||
self.readyToEdit = True
|
||||
|
||||
def get_value(self):
|
||||
return str(self.value())
|
||||
|
@ -1207,6 +1406,9 @@ class FCSpinner(QtWidgets.QSpinBox):
|
|||
return
|
||||
self.setValue(k)
|
||||
|
||||
def set_range(self, min_val, max_val):
|
||||
self.setRange(min_val, max_val)
|
||||
|
||||
# def sizeHint(self):
|
||||
# default_hint_size = super(FCSpinner, self).sizeHint()
|
||||
# return QtCore.QSize(EDIT_SIZE_HINT, default_hint_size.height())
|
||||
|
@ -1243,7 +1445,7 @@ class FCDoubleSpinner(QtWidgets.QDoubleSpinBox):
|
|||
self.setDecimals(val)
|
||||
|
||||
def set_range(self, min_val, max_val):
|
||||
self.setRange(self, min_val, max_val)
|
||||
self.setRange(min_val, max_val)
|
||||
|
||||
|
||||
class Dialog_box(QtWidgets.QWidget):
|
||||
|
@ -1261,7 +1463,20 @@ class Dialog_box(QtWidgets.QWidget):
|
|||
dialog_box.setFixedWidth(290)
|
||||
self.setWindowIcon(icon)
|
||||
|
||||
self.location, self.ok = dialog_box.getText(self, title, label)
|
||||
self.location, self.ok = dialog_box.getText(self, title, label, text="0, 0")
|
||||
self.readyToEdit = True
|
||||
|
||||
def mousePressEvent(self, e, parent=None):
|
||||
super(Dialog_box, self).mousePressEvent(e) # required to deselect on 2e click
|
||||
if self.readyToEdit:
|
||||
self.lineEdit().selectAll()
|
||||
self.readyToEdit = False
|
||||
|
||||
def focusOutEvent(self, e):
|
||||
super(Dialog_box, self).focusOutEvent(e) # required to remove cursor on focusOut
|
||||
self.lineEdit().deselect()
|
||||
self.readyToEdit = True
|
||||
|
||||
|
||||
|
||||
class _BrowserTextEdit(QTextEdit):
|
||||
|
@ -1318,9 +1533,18 @@ class _ExpandableTextEdit(QTextEdit):
|
|||
def insertCompletion(self, completion):
|
||||
tc = self.textCursor()
|
||||
extra = (len(completion) - len(self.completer.completionPrefix()))
|
||||
|
||||
# don't insert if the word is finished but add a space instead
|
||||
if extra == 0:
|
||||
tc.insertText(' ')
|
||||
self.completer.popup().hide()
|
||||
return
|
||||
|
||||
tc.movePosition(QTextCursor.Left)
|
||||
tc.movePosition(QTextCursor.EndOfWord)
|
||||
tc.insertText(completion[-extra:])
|
||||
# add a space after inserting the word
|
||||
tc.insertText(' ')
|
||||
self.setTextCursor(tc)
|
||||
self.completer.popup().hide()
|
||||
|
||||
|
@ -1333,6 +1557,13 @@ class _ExpandableTextEdit(QTextEdit):
|
|||
"""
|
||||
Catch keyboard events. Process Enter, Up, Down
|
||||
"""
|
||||
|
||||
key = event.key()
|
||||
if (key == Qt.Key_Tab or key == Qt.Key_Return or key == Qt.Key_Enter) and self.completer.popup().isVisible():
|
||||
self.completer.insertText.emit(self.completer.getSelected())
|
||||
self.completer.setCompletionMode(QCompleter.PopupCompletion)
|
||||
return
|
||||
|
||||
if event.matches(QKeySequence.InsertParagraphSeparator):
|
||||
text = self.toPlainText()
|
||||
if self._termWidget.is_command_complete(text):
|
||||
|
@ -1363,10 +1594,6 @@ class _ExpandableTextEdit(QTextEdit):
|
|||
return self._termWidget.browser().keyPressEvent(event)
|
||||
|
||||
tc = self.textCursor()
|
||||
if event.key() == Qt.Key_Tab and self.completer.popup().isVisible():
|
||||
self.completer.insertText.emit(self.completer.getSelected())
|
||||
self.completer.setCompletionMode(QCompleter.PopupCompletion)
|
||||
return
|
||||
|
||||
QTextEdit.keyPressEvent(self, event)
|
||||
tc.select(QTextCursor.WordUnderCursor)
|
||||
|
|
|
@ -101,7 +101,7 @@ class ObjectUI(QtWidgets.QWidget):
|
|||
self.scale_button.setToolTip(
|
||||
_("Perform scaling operation.")
|
||||
)
|
||||
self.scale_button.setFixedWidth(50)
|
||||
self.scale_button.setFixedWidth(70)
|
||||
self.scale_grid.addWidget(self.scale_button, 0, 2)
|
||||
|
||||
#### Offset ####
|
||||
|
@ -128,7 +128,7 @@ class ObjectUI(QtWidgets.QWidget):
|
|||
self.offset_button.setToolTip(
|
||||
_("Perform the offset operation.")
|
||||
)
|
||||
self.offset_button.setFixedWidth(50)
|
||||
self.offset_button.setFixedWidth(70)
|
||||
self.offset_grid.addWidget(self.offset_button, 0, 2)
|
||||
|
||||
layout.addStretch()
|
||||
|
@ -244,79 +244,8 @@ class GerberObjectUI(ObjectUI):
|
|||
_("Mark the aperture instances on canvas."))
|
||||
# self.apertures_table.setColumnHidden(5, True)
|
||||
|
||||
#### Aperture Scale ####
|
||||
self.transform_aperture_grid = QtWidgets.QGridLayout()
|
||||
self.custom_box.addLayout(self.transform_aperture_grid)
|
||||
|
||||
# Scale Aperture Factor
|
||||
self.scale_aperture_label = QtWidgets.QLabel(_('Scale Factor:'))
|
||||
self.scale_aperture_label.setToolTip(
|
||||
_("Change the size of the selected apertures.\n"
|
||||
"Factor by which to multiply\n"
|
||||
"geometric features of this object.")
|
||||
)
|
||||
self.scale_aperture_label.setFixedWidth(90)
|
||||
self.transform_aperture_grid.addWidget(self.scale_aperture_label, 0, 0)
|
||||
|
||||
self.scale_aperture_entry = FloatEntry2()
|
||||
self.transform_aperture_grid.addWidget(self.scale_aperture_entry, 0, 1)
|
||||
|
||||
# Scale Button
|
||||
self.scale_aperture_button = QtWidgets.QPushButton(_('Scale'))
|
||||
self.scale_aperture_button.setToolTip(
|
||||
_("Perform scaling operation on the selected apertures.")
|
||||
)
|
||||
self.scale_aperture_button.setFixedWidth(50)
|
||||
self.transform_aperture_grid.addWidget(self.scale_aperture_button, 0, 2)
|
||||
|
||||
# Buffer Aperture Factor
|
||||
self.buffer_aperture_label = QtWidgets.QLabel(_('Buffer Factor:'))
|
||||
self.buffer_aperture_label.setToolTip(
|
||||
_("Change the size of the selected apertures.\n"
|
||||
"Factor by which to expand/shrink\n"
|
||||
"geometric features of this object.")
|
||||
)
|
||||
self.buffer_aperture_label.setFixedWidth(90)
|
||||
self.transform_aperture_grid.addWidget(self.buffer_aperture_label, 1, 0)
|
||||
|
||||
self.buffer_aperture_entry = FloatEntry2()
|
||||
self.transform_aperture_grid.addWidget(self.buffer_aperture_entry, 1, 1)
|
||||
|
||||
# Buffer Button
|
||||
self.buffer_aperture_button = QtWidgets.QPushButton(_('Buffer'))
|
||||
self.buffer_aperture_button.setToolTip(
|
||||
_("Perform buffer operation on the selected apertures.")
|
||||
)
|
||||
self.buffer_aperture_button.setFixedWidth(50)
|
||||
self.transform_aperture_grid.addWidget(self.buffer_aperture_button, 1, 2)
|
||||
|
||||
new_hlay = QtWidgets.QHBoxLayout()
|
||||
self.custom_box.addLayout(new_hlay)
|
||||
|
||||
self.new_grb_label = QtWidgets.QLabel(_("<b>Generate new Gerber Object:</b>"))
|
||||
self.new_grb_label.setToolTip(
|
||||
_("Will generate a new Gerber object from the changed apertures.")
|
||||
)
|
||||
new_hlay.addWidget(self.new_grb_label)
|
||||
|
||||
new_hlay.addStretch()
|
||||
|
||||
self.new_grb_button = FCButton(_('Go'))
|
||||
self.new_grb_button.setToolTip(
|
||||
_("Will generate a new Gerber object from the changed apertures.\n"
|
||||
"This new object can then be isolated etc."))
|
||||
self.new_grb_button.setFixedWidth(50)
|
||||
new_hlay.addWidget(self.new_grb_button)
|
||||
|
||||
# start with apertures table hidden
|
||||
self.apertures_table.setVisible(False)
|
||||
self.scale_aperture_label.setVisible(False)
|
||||
self.scale_aperture_entry.setVisible(False)
|
||||
self.scale_aperture_button.setVisible(False)
|
||||
|
||||
self.buffer_aperture_label.setVisible(False)
|
||||
self.buffer_aperture_entry.setVisible(False)
|
||||
self.buffer_aperture_button.setVisible(False)
|
||||
|
||||
# Isolation Routing
|
||||
self.isolation_routing_label = QtWidgets.QLabel(_("<b>Isolation Routing:</b>"))
|
||||
|
@ -1601,13 +1530,6 @@ class CNCObjectUI(ObjectUI):
|
|||
"a Custom Toolchange GCode (macro)."
|
||||
)
|
||||
)
|
||||
cnclay.addWidget(self.toolchange_cb)
|
||||
|
||||
self.toolch_ois = OptionalInputSection(self.toolchange_cb, [self.toolchangelabel, self.toolchange_text])
|
||||
cnclay.addStretch()
|
||||
|
||||
cnclay1 = QtWidgets.QHBoxLayout()
|
||||
self.cnc_box.addLayout(cnclay1)
|
||||
|
||||
# Variable list
|
||||
self.tc_variable_combo = FCComboBox()
|
||||
|
@ -1618,7 +1540,6 @@ class CNCObjectUI(ObjectUI):
|
|||
"They have to be surrounded by the '%' symbol"
|
||||
)
|
||||
)
|
||||
cnclay1.addWidget(self.tc_variable_combo)
|
||||
|
||||
# Populate the Combo Box
|
||||
variables = [_('Parameters'), 'tool', 'tooldia', 't_drills', 'x_toolchange', 'y_toolchange', 'z_toolchange',
|
||||
|
@ -1638,15 +1559,12 @@ class CNCObjectUI(ObjectUI):
|
|||
self.tc_variable_combo.setItemData(11, _("dwelltime = time to dwell to allow the spindle to reach it's set RPM"),
|
||||
Qt.ToolTipRole)
|
||||
|
||||
cnclay1.addStretch()
|
||||
cnclay.addWidget(self.toolchange_cb)
|
||||
cnclay.addStretch()
|
||||
cnclay.addWidget(self.tc_variable_combo)
|
||||
|
||||
# Insert Variable into the Toolchange G-Code Text Box
|
||||
# self.tc_insert_buton = FCButton("Insert")
|
||||
# self.tc_insert_buton.setToolTip(
|
||||
# "Insert the variable in the GCode Box\n"
|
||||
# "surrounded by the '%' symbol."
|
||||
# )
|
||||
# cnclay1.addWidget(self.tc_insert_buton)
|
||||
self.toolch_ois = OptionalInputSection(self.toolchange_cb,
|
||||
[self.toolchangelabel, self.toolchange_text, self.tc_variable_combo])
|
||||
|
||||
h_lay = QtWidgets.QHBoxLayout()
|
||||
h_lay.setAlignment(QtCore.Qt.AlignVCenter)
|
||||
|
|
|
@ -148,8 +148,11 @@ class PlotCanvas(QtCore.QObject):
|
|||
def vis_connect(self, event_name, callback):
|
||||
return getattr(self.vispy_canvas.events, event_name).connect(callback)
|
||||
|
||||
def vis_disconnect(self, event_name, callback):
|
||||
getattr(self.vispy_canvas.events, event_name).disconnect(callback)
|
||||
def vis_disconnect(self, event_name, callback=None):
|
||||
if callback is None:
|
||||
getattr(self.vispy_canvas.events, event_name).disconnect()
|
||||
else:
|
||||
getattr(self.vispy_canvas.events, event_name).disconnect(callback)
|
||||
|
||||
def zoom(self, factor, center=None):
|
||||
"""
|
||||
|
|
|
@ -437,8 +437,11 @@ class TextGroup(object):
|
|||
:param value: bool
|
||||
"""
|
||||
self._visible = value
|
||||
self._collection.data[self._index]['visible'] = value
|
||||
|
||||
try:
|
||||
self._collection.data[self._index]['visible'] = value
|
||||
except KeyError:
|
||||
print("VisPyVisuals.TextGroup.visible --> KeyError")
|
||||
pass
|
||||
self._collection.redraw()
|
||||
|
||||
|
||||
|
|
|
@ -56,9 +56,9 @@ def svgparselength(lengthstr):
|
|||
def path2shapely(path, object_type, res=1.0):
|
||||
"""
|
||||
Converts an svg.path.Path into a Shapely
|
||||
LinearRing or LinearString.
|
||||
Polygon or LinearString.
|
||||
|
||||
:rtype : LinearRing
|
||||
:rtype : Polygon
|
||||
:rtype : LineString
|
||||
:param path: svg.path.Path instance
|
||||
:param res: Resolution (minimum step along path)
|
||||
|
@ -68,6 +68,7 @@ def path2shapely(path, object_type, res=1.0):
|
|||
points = []
|
||||
geometry = []
|
||||
geo_element = None
|
||||
rings = []
|
||||
|
||||
for component in path:
|
||||
|
||||
|
@ -109,35 +110,30 @@ def path2shapely(path, object_type, res=1.0):
|
|||
|
||||
# Move
|
||||
if isinstance(component, Move):
|
||||
if object_type == 'geometry':
|
||||
geo_element = LineString(points)
|
||||
elif object_type == 'gerber':
|
||||
# Didn't worked out using Polygon because if there is a large outline it will envelope everything
|
||||
# and create issues with intersections. I will let the parameter obj_type present though
|
||||
# geo_element = Polygon(points)
|
||||
geo_element = LineString(points)
|
||||
else:
|
||||
log.error("[ERROR]: Not a valid target object.")
|
||||
if not points:
|
||||
continue
|
||||
else:
|
||||
geometry.append(geo_element)
|
||||
rings.append(points)
|
||||
points = []
|
||||
continue
|
||||
log.warning("I don't know what this is: %s" % str(component))
|
||||
continue
|
||||
|
||||
# if there are still points in points then add them as a LineString
|
||||
if points:
|
||||
geo_element = LineString(points)
|
||||
geometry.append(geo_element)
|
||||
points = []
|
||||
# if there are still points in points then add them to the last ring
|
||||
|
||||
if points:
|
||||
rings.append(points)
|
||||
if len(rings) > 0:
|
||||
if len(rings) == 1:
|
||||
# Polygons are closed and require more than 2 points
|
||||
if Point(rings[0][0]).almost_equals(Point(rings[0][-1])) and len(rings[0]) > 2:
|
||||
geo_element = Polygon(rings[0])
|
||||
else:
|
||||
geo_element = LineString(rings[0])
|
||||
else:
|
||||
geo_element = Polygon(rings[0], rings[1:])
|
||||
geometry.append(geo_element)
|
||||
|
||||
# if path.closed:
|
||||
# return Polygon(points).buffer(0)
|
||||
# # return LinearRing(points)
|
||||
# else:
|
||||
# return LineString(points)
|
||||
return geometry
|
||||
|
||||
def svgrect2shapely(rect, n_points=32):
|
||||
|
@ -362,7 +358,7 @@ def getsvggeo(node, object_type):
|
|||
if tr[0] == 'translate':
|
||||
geo = [translate(geoi, tr[1], tr[2]) for geoi in geo]
|
||||
elif tr[0] == 'scale':
|
||||
geo = [scale(geoi, tr[0], tr[1], origin=(0, 0))
|
||||
geo = [scale(geoi, tr[1], tr[2], origin=(0, 0))
|
||||
for geoi in geo]
|
||||
elif tr[0] == 'rotate':
|
||||
geo = [rotate(geoi, tr[1], origin=(tr[2], tr[3]))
|
||||
|
@ -459,7 +455,7 @@ def getsvgtext(node, object_type, units='MM'):
|
|||
if tr[0] == 'translate':
|
||||
geo = [translate(geoi, tr[1], tr[2]) for geoi in geo]
|
||||
elif tr[0] == 'scale':
|
||||
geo = [scale(geoi, tr[0], tr[1], origin=(0, 0))
|
||||
geo = [scale(geoi, tr[1], tr[2], origin=(0, 0))
|
||||
for geoi in geo]
|
||||
elif tr[0] == 'rotate':
|
||||
geo = [rotate(geoi, tr[1], origin=(tr[2], tr[3]))
|
||||
|
@ -592,7 +588,7 @@ def parse_svg_transform(trstr):
|
|||
trlist.append([
|
||||
'translate',
|
||||
float(match.group(1)),
|
||||
float(match.group(2)) if match.group else 0.0
|
||||
float(match.group(2)) if (match.group(2) is not None) else 0.0
|
||||
])
|
||||
trstr = trstr[len(match.group(0)):].strip(' ')
|
||||
continue
|
||||
|
@ -600,9 +596,9 @@ def parse_svg_transform(trstr):
|
|||
match = re.search(r'^' + scale_re_str, trstr)
|
||||
if match:
|
||||
trlist.append([
|
||||
'translate',
|
||||
'scale',
|
||||
float(match.group(1)),
|
||||
float(match.group(2)) if not None else float(match.group(1))
|
||||
float(match.group(2)) if (match.group(2) is not None) else float(match.group(1))
|
||||
])
|
||||
trstr = trstr[len(match.group(0)):].strip(' ')
|
||||
continue
|
||||
|
|
|
@ -304,7 +304,7 @@ class ToolCalculator(FlatCAMTool):
|
|||
try:
|
||||
tip_diameter = float(self.tipDia_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
|
||||
|
@ -315,7 +315,7 @@ class ToolCalculator(FlatCAMTool):
|
|||
try:
|
||||
half_tip_angle = float(self.tipAngle_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
half_tip_angle /= 2
|
||||
|
@ -327,7 +327,7 @@ class ToolCalculator(FlatCAMTool):
|
|||
try:
|
||||
cut_depth = float(self.cutDepth_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
|
||||
|
@ -342,7 +342,7 @@ class ToolCalculator(FlatCAMTool):
|
|||
try:
|
||||
mm_val = float(self.mm_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
self.inch_entry.set_value('%.6f' % (mm_val / 25.4))
|
||||
|
@ -355,7 +355,7 @@ class ToolCalculator(FlatCAMTool):
|
|||
try:
|
||||
inch_val = float(self.inch_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
self.mm_entry.set_value('%.6f' % (inch_val * 25.4))
|
||||
|
@ -369,7 +369,7 @@ class ToolCalculator(FlatCAMTool):
|
|||
try:
|
||||
length = float(self.pcblength_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
|
||||
|
@ -380,7 +380,7 @@ class ToolCalculator(FlatCAMTool):
|
|||
try:
|
||||
width = float(self.pcbwidth_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
|
||||
|
@ -391,7 +391,7 @@ class ToolCalculator(FlatCAMTool):
|
|||
try:
|
||||
density = float(self.cdensity_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
|
||||
|
@ -402,7 +402,7 @@ class ToolCalculator(FlatCAMTool):
|
|||
try:
|
||||
copper = float(self.growth_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
|
||||
|
|
|
@ -110,6 +110,14 @@ class CutOut(FlatCAMTool):
|
|||
# 2tb - 2*top + 2*bottom
|
||||
# 8 - 2*left + 2*right +2*top + 2*bottom
|
||||
|
||||
# Surrounding convex box shape
|
||||
self.convex_box = FCCheckBox()
|
||||
self.convex_box_label = QtWidgets.QLabel(_("Convex Sh.:"))
|
||||
self.convex_box_label.setToolTip(
|
||||
_("Create a convex shape surrounding the entire PCB.")
|
||||
)
|
||||
form_layout.addRow(self.convex_box_label, self.convex_box)
|
||||
|
||||
## Title2
|
||||
title_param_label = QtWidgets.QLabel("<font size=4><b>%s</b></font>" % _('A. Automatic Bridge Gaps'))
|
||||
title_param_label.setToolTip(
|
||||
|
@ -310,7 +318,8 @@ class CutOut(FlatCAMTool):
|
|||
self.dia.set_value(float(self.app.defaults["tools_cutouttooldia"]))
|
||||
self.margin.set_value(float(self.app.defaults["tools_cutoutmargin"]))
|
||||
self.gapsize.set_value(float(self.app.defaults["tools_cutoutgapsize"]))
|
||||
self.gaps.set_value(4)
|
||||
self.gaps.set_value(self.app.defaults["tools_gaps_ff"])
|
||||
self.convex_box.set_value(self.app.defaults['tools_cutout_convexshape'])
|
||||
|
||||
self.gapFinished.connect(self.on_gap_finished)
|
||||
|
||||
|
@ -326,11 +335,11 @@ class CutOut(FlatCAMTool):
|
|||
try:
|
||||
cutout_obj = self.app.collection.get_by_name(str(name))
|
||||
except:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Could not retrieve object: %s") % name)
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % name)
|
||||
return "Could not retrieve object: %s" % name
|
||||
|
||||
if cutout_obj is None:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]There is no object selected for Cutout.\nSelect one and try again."))
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] There is no object selected for Cutout.\nSelect one and try again."))
|
||||
return
|
||||
|
||||
try:
|
||||
|
@ -346,8 +355,8 @@ class CutOut(FlatCAMTool):
|
|||
|
||||
|
||||
if 0 in {dia}:
|
||||
self.app.inform.emit(_("[WARNING_NOTCL]Tool Diameter is zero value. Change it to a positive integer."))
|
||||
return "Tool Diameter is zero value. Change it to a positive integer."
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] Tool Diameter is zero value. Change it to a positive real number."))
|
||||
return "Tool Diameter is zero value. Change it to a positive real number."
|
||||
|
||||
try:
|
||||
margin = float(self.margin.get_value())
|
||||
|
@ -388,6 +397,8 @@ class CutOut(FlatCAMTool):
|
|||
"and after that perform Cutout."))
|
||||
return
|
||||
|
||||
convex_box = self.convex_box.get_value()
|
||||
|
||||
# Get min and max data for each object as we just cut rectangles across X or Y
|
||||
xmin, ymin, xmax, ymax = cutout_obj.bounds()
|
||||
px = 0.5 * (xmin + xmax) + margin
|
||||
|
@ -402,8 +413,12 @@ class CutOut(FlatCAMTool):
|
|||
cutout_obj.options["name"] += "_cutout"
|
||||
else:
|
||||
def geo_init(geo_obj, app_obj):
|
||||
geo = cutout_obj.solid_geometry.convex_hull
|
||||
geo_obj.solid_geometry = geo.buffer(margin + abs(dia / 2))
|
||||
if convex_box:
|
||||
geo = cutout_obj.solid_geometry.convex_hull
|
||||
geo_obj.solid_geometry = geo.buffer(margin + abs(dia / 2))
|
||||
else:
|
||||
geo = cutout_obj.solid_geometry
|
||||
geo_obj.solid_geometry = geo.buffer(margin + abs(dia / 2)).exterior
|
||||
|
||||
outname = cutout_obj.options["name"] + "_cutout"
|
||||
self.app.new_object('geometry', outname, geo_init)
|
||||
|
@ -465,11 +480,11 @@ class CutOut(FlatCAMTool):
|
|||
try:
|
||||
cutout_obj = self.app.collection.get_by_name(str(name))
|
||||
except:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Could not retrieve object: %s") % name)
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % name)
|
||||
return "Could not retrieve object: %s" % name
|
||||
|
||||
if cutout_obj is None:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Object not found: %s") % cutout_obj)
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Object not found: %s") % cutout_obj)
|
||||
|
||||
try:
|
||||
dia = float(self.dia.get_value())
|
||||
|
@ -483,8 +498,8 @@ class CutOut(FlatCAMTool):
|
|||
return
|
||||
|
||||
if 0 in {dia}:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Tool Diameter is zero value. Change it to a positive integer."))
|
||||
return "Tool Diameter is zero value. Change it to a positive integer."
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Tool Diameter is zero value. Change it to a positive real number."))
|
||||
return "Tool Diameter is zero value. Change it to a positive real number."
|
||||
|
||||
try:
|
||||
margin = float(self.margin.get_value())
|
||||
|
@ -603,8 +618,8 @@ class CutOut(FlatCAMTool):
|
|||
return
|
||||
|
||||
if 0 in {self.cutting_dia}:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Tool Diameter is zero value. Change it to a positive integer."))
|
||||
return "Tool Diameter is zero value. Change it to a positive integer."
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Tool Diameter is zero value. Change it to a positive real number."))
|
||||
return "Tool Diameter is zero value. Change it to a positive real number."
|
||||
|
||||
try:
|
||||
self.cutting_gapsize = float(self.gapsize.get_value())
|
||||
|
@ -652,11 +667,11 @@ class CutOut(FlatCAMTool):
|
|||
try:
|
||||
cutout_obj = self.app.collection.get_by_name(str(name))
|
||||
except:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Could not retrieve Geoemtry object: %s") % name)
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve Geometry object: %s") % name)
|
||||
return "Could not retrieve object: %s" % name
|
||||
|
||||
if cutout_obj is None:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Geometry object for manual cutout not found: %s") % cutout_obj)
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Geometry object for manual cutout not found: %s") % cutout_obj)
|
||||
return
|
||||
|
||||
# use the snapped position as reference
|
||||
|
@ -683,16 +698,16 @@ class CutOut(FlatCAMTool):
|
|||
try:
|
||||
cutout_obj = self.app.collection.get_by_name(str(name))
|
||||
except:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Could not retrieve Gerber object: %s") % name)
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve Gerber object: %s") % name)
|
||||
return "Could not retrieve object: %s" % name
|
||||
|
||||
if cutout_obj is None:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]There is no Gerber object selected for Cutout.\n"
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] There is no Gerber object selected for Cutout.\n"
|
||||
"Select one and try again."))
|
||||
return
|
||||
|
||||
if not isinstance(cutout_obj, FlatCAMGerber):
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]The selected object has to be of Gerber type.\n"
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] The selected object has to be of Gerber type.\n"
|
||||
"Select a Gerber file and try again."))
|
||||
return
|
||||
|
||||
|
@ -708,8 +723,8 @@ class CutOut(FlatCAMTool):
|
|||
return
|
||||
|
||||
if 0 in {dia}:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Tool Diameter is zero value. Change it to a positive integer."))
|
||||
return "Tool Diameter is zero value. Change it to a positive integer."
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Tool Diameter is zero value. Change it to a positive real number."))
|
||||
return "Tool Diameter is zero value. Change it to a positive real number."
|
||||
|
||||
try:
|
||||
margin = float(self.margin.get_value())
|
||||
|
@ -722,16 +737,21 @@ class CutOut(FlatCAMTool):
|
|||
"Add it and retry."))
|
||||
return
|
||||
|
||||
convex_box = self.convex_box.get_value()
|
||||
|
||||
def geo_init(geo_obj, app_obj):
|
||||
geo = cutout_obj.solid_geometry.convex_hull
|
||||
geo_obj.solid_geometry = geo.buffer(margin + abs(dia / 2))
|
||||
if convex_box:
|
||||
geo = cutout_obj.solid_geometry.convex_hull
|
||||
geo_obj.solid_geometry = geo.buffer(margin + abs(dia / 2))
|
||||
else:
|
||||
geo = cutout_obj.solid_geometry
|
||||
geo_obj.solid_geometry = geo.buffer(margin + abs(dia / 2)).exterior
|
||||
|
||||
outname = cutout_obj.options["name"] + "_cutout"
|
||||
self.app.new_object('geometry', outname, geo_init)
|
||||
|
||||
def cutting_geo(self, pos):
|
||||
self.cutting_gapsize = self.cutting_gapsize / 2 + (self.cutting_dia / 2)
|
||||
offset = self.cutting_gapsize / 2
|
||||
offset = self.cutting_dia / 2 + self.cutting_gapsize / 2
|
||||
|
||||
# cutting area definition
|
||||
orig_x = pos[0]
|
||||
|
|
|
@ -190,7 +190,7 @@ class DblSidedTool(FlatCAMTool):
|
|||
|
||||
|
||||
## Alignment holes
|
||||
self.ah_label = QtWidgets.QLabel("<b%s</b>" % _('Alignment Drill Coordinates:'))
|
||||
self.ah_label = QtWidgets.QLabel("<b>%s</b>" % _('Alignment Drill Coordinates:'))
|
||||
self.ah_label.setToolTip(
|
||||
_( "Alignment holes (x1, y1), (x2, y2), ... "
|
||||
"on one side of the mirror axis. For each set of (x, y) coordinates\n"
|
||||
|
@ -365,7 +365,7 @@ class DblSidedTool(FlatCAMTool):
|
|||
return
|
||||
|
||||
if dia is '':
|
||||
self.app.inform.emit(_("[WARNING_NOTCL]No value or wrong format in Drill Dia entry. Add it and retry."))
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] No value or wrong format in Drill Dia entry. Add it and retry."))
|
||||
return
|
||||
tools = {"1": {"C": dia}}
|
||||
|
||||
|
|
|
@ -238,14 +238,14 @@ class Film(FlatCAMTool):
|
|||
try:
|
||||
border = float(self.boundary_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
|
||||
try:
|
||||
scale_stroke_width = int(self.film_scale_entry.get_value())
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
|
||||
|
@ -266,7 +266,7 @@ class Film(FlatCAMTool):
|
|||
filename = str(filename)
|
||||
|
||||
if str(filename) == "":
|
||||
self.app.inform.emit(_("[WARNING_NOTCL]Export SVG positive cancelled."))
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] Export SVG positive cancelled."))
|
||||
return
|
||||
else:
|
||||
self.app.export_svg_black(name, boxname, filename, scale_factor=scale_stroke_width)
|
||||
|
@ -282,7 +282,7 @@ class Film(FlatCAMTool):
|
|||
filename = str(filename)
|
||||
|
||||
if str(filename) == "":
|
||||
self.app.inform.emit(_("[WARNING_NOTCL]Export SVG negative cancelled."))
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] Export SVG negative cancelled."))
|
||||
return
|
||||
else:
|
||||
self.app.export_svg_negative(name, boxname, filename, border, scale_factor=scale_stroke_width)
|
||||
|
|
|
@ -28,6 +28,8 @@ class Measurement(FlatCAMTool):
|
|||
def __init__(self, app):
|
||||
FlatCAMTool.__init__(self, app)
|
||||
|
||||
self.app = app
|
||||
self.canvas = self.app.plotcanvas
|
||||
self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower()
|
||||
|
||||
## Title
|
||||
|
@ -38,11 +40,11 @@ class Measurement(FlatCAMTool):
|
|||
form_layout = QtWidgets.QFormLayout()
|
||||
self.layout.addLayout(form_layout)
|
||||
|
||||
form_layout_child_1 = QtWidgets.QFormLayout()
|
||||
form_layout_child_1_1 = QtWidgets.QFormLayout()
|
||||
form_layout_child_1_2 = QtWidgets.QFormLayout()
|
||||
form_layout_child_2 = QtWidgets.QFormLayout()
|
||||
form_layout_child_3 = QtWidgets.QFormLayout()
|
||||
|
||||
self.units_label = QtWidgets.QLabel(_("Units:"))
|
||||
self.units_label.setToolTip(_("Those are the units in which the distance is measured."))
|
||||
self.units_value = QtWidgets.QLabel("%s" % str({'mm': "METRIC (mm)", 'in': "INCH (in)"}[self.units]))
|
||||
self.units_value.setDisabled(True)
|
||||
|
||||
self.start_label = QtWidgets.QLabel("<b>%s</b> %s:" % (_('Start'), _('Coords')))
|
||||
self.start_label.setToolTip(_("This is measuring Start point coordinates."))
|
||||
|
@ -59,84 +61,38 @@ class Measurement(FlatCAMTool):
|
|||
self.total_distance_label = QtWidgets.QLabel("<b>%s:</b>" % _('DISTANCE'))
|
||||
self.total_distance_label.setToolTip(_("This is the point to point Euclidian distance."))
|
||||
|
||||
self.units_entry_1 = FCEntry()
|
||||
self.units_entry_1.setToolTip(_("Those are the units in which the distance is measured."))
|
||||
self.units_entry_1.setDisabled(True)
|
||||
self.units_entry_1.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||
self.units_entry_1.setFrame(False)
|
||||
self.units_entry_1.setFixedWidth(30)
|
||||
|
||||
self.units_entry_2 = FCEntry()
|
||||
self.units_entry_2.setToolTip(_("Those are the units in which the distance is measured."))
|
||||
self.units_entry_2.setDisabled(True)
|
||||
self.units_entry_2.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||
self.units_entry_2.setFrame(False)
|
||||
self.units_entry_2.setFixedWidth(30)
|
||||
|
||||
self.units_entry_3 = FCEntry()
|
||||
self.units_entry_3.setToolTip(_("Those are the units in which the distance is measured."))
|
||||
self.units_entry_3.setDisabled(True)
|
||||
self.units_entry_3.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||
self.units_entry_3.setFrame(False)
|
||||
self.units_entry_3.setFixedWidth(30)
|
||||
|
||||
self.units_entry_4 = FCEntry()
|
||||
self.units_entry_4.setToolTip(_("Those are the units in which the distance is measured."))
|
||||
self.units_entry_4.setDisabled(True)
|
||||
self.units_entry_4.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||
self.units_entry_4.setFrame(False)
|
||||
self.units_entry_4.setFixedWidth(30)
|
||||
|
||||
self.units_entry_5 = FCEntry()
|
||||
self.units_entry_5.setToolTip(_("Those are the units in which the distance is measured."))
|
||||
self.units_entry_5.setDisabled(True)
|
||||
self.units_entry_5.setFocusPolicy(QtCore.Qt.NoFocus)
|
||||
self.units_entry_5.setFrame(False)
|
||||
self.units_entry_5.setFixedWidth(30)
|
||||
|
||||
self.start_entry = FCEntry()
|
||||
self.start_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
|
||||
self.start_entry.setToolTip(_("This is measuring Start point coordinates."))
|
||||
self.start_entry.setFixedWidth(100)
|
||||
|
||||
self.stop_entry = FCEntry()
|
||||
self.stop_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
|
||||
self.stop_entry.setToolTip(_("This is the measuring Stop point coordinates."))
|
||||
self.stop_entry.setFixedWidth(100)
|
||||
|
||||
self.distance_x_entry = FCEntry()
|
||||
self.distance_x_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
|
||||
self.distance_x_entry.setToolTip(_("This is the distance measured over the X axis."))
|
||||
self.distance_x_entry.setFixedWidth(100)
|
||||
|
||||
|
||||
self.distance_y_entry = FCEntry()
|
||||
self.distance_y_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
|
||||
self.distance_y_entry.setToolTip(_("This is the distance measured over the Y axis."))
|
||||
self.distance_y_entry.setFixedWidth(100)
|
||||
|
||||
|
||||
self.total_distance_entry = FCEntry()
|
||||
self.total_distance_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
|
||||
self.total_distance_entry.setToolTip(_("This is the point to point Euclidian distance."))
|
||||
self.total_distance_entry.setFixedWidth(100)
|
||||
|
||||
self.measure_btn = QtWidgets.QPushButton(_("Measure"))
|
||||
self.measure_btn.setFixedWidth(70)
|
||||
# self.measure_btn.setFixedWidth(70)
|
||||
self.layout.addWidget(self.measure_btn)
|
||||
|
||||
|
||||
form_layout_child_1.addRow(self.start_entry, self.units_entry_1)
|
||||
form_layout_child_1_1.addRow(self.stop_entry, self.units_entry_2)
|
||||
form_layout_child_1_2.addRow(self.distance_x_entry, self.units_entry_3)
|
||||
form_layout_child_2.addRow(self.distance_y_entry, self.units_entry_4)
|
||||
form_layout_child_3.addRow(self.total_distance_entry, self.units_entry_5)
|
||||
|
||||
form_layout.addRow(self.start_label, form_layout_child_1)
|
||||
form_layout.addRow(self.stop_label, form_layout_child_1_1)
|
||||
form_layout.addRow(self.distance_x_label, form_layout_child_1_2)
|
||||
form_layout.addRow(self.distance_y_label, form_layout_child_2)
|
||||
form_layout.addRow(self.total_distance_label, form_layout_child_3)
|
||||
form_layout.addRow(self.units_label, self.units_value)
|
||||
form_layout.addRow(self.start_label, self.start_entry)
|
||||
form_layout.addRow(self.stop_label, self.stop_entry)
|
||||
form_layout.addRow(self.distance_x_label, self.distance_x_entry)
|
||||
form_layout.addRow(self.distance_y_label, self.distance_y_entry)
|
||||
form_layout.addRow(self.total_distance_label, self.total_distance_entry)
|
||||
|
||||
# initial view of the layout
|
||||
self.start_entry.set_value('(0, 0)')
|
||||
|
@ -144,12 +100,6 @@ class Measurement(FlatCAMTool):
|
|||
self.distance_x_entry.set_value('0')
|
||||
self.distance_y_entry.set_value('0')
|
||||
self.total_distance_entry.set_value('0')
|
||||
self.units_entry_1.set_value(str(self.units))
|
||||
self.units_entry_2.set_value(str(self.units))
|
||||
self.units_entry_3.set_value(str(self.units))
|
||||
self.units_entry_4.set_value(str(self.units))
|
||||
self.units_entry_5.set_value(str(self.units))
|
||||
|
||||
|
||||
self.layout.addStretch()
|
||||
|
||||
|
@ -160,12 +110,12 @@ class Measurement(FlatCAMTool):
|
|||
|
||||
# the default state is disabled for the Move command
|
||||
# self.setVisible(False)
|
||||
self.active = False
|
||||
self.active = 0
|
||||
|
||||
# VisPy visuals
|
||||
self.sel_shapes = ShapeCollection(parent=self.app.plotcanvas.vispy_canvas.view.scene, layers=1)
|
||||
|
||||
self.measure_btn.clicked.connect(self.toggle_f)
|
||||
self.measure_btn.clicked.connect(lambda: self.on_measure(activate=True))
|
||||
|
||||
def run(self, toggle=False):
|
||||
self.app.report_usage("ToolMeasurement()")
|
||||
|
@ -173,14 +123,13 @@ class Measurement(FlatCAMTool):
|
|||
if self.app.tool_tab_locked is True:
|
||||
return
|
||||
|
||||
self.app.ui.notebook.setTabText(2, _("Meas. Tool"))
|
||||
|
||||
# if the splitter is hidden, display it, else hide it but only if the current widget is the same
|
||||
if self.app.ui.splitter.sizes()[0] == 0:
|
||||
self.app.ui.splitter.setSizes([1, 1])
|
||||
|
||||
self.toggle_f()
|
||||
|
||||
self.set_tool_ui()
|
||||
self.app.ui.notebook.setTabText(2, _("Meas. Tool"))
|
||||
self.on_measure(activate=True)
|
||||
|
||||
def install(self, icon=None, separator=None, **kwargs):
|
||||
FlatCAMTool.install(self, icon, separator, shortcut='CTRL+M', **kwargs)
|
||||
|
@ -195,90 +144,91 @@ class Measurement(FlatCAMTool):
|
|||
# Switch notebook to tool page
|
||||
self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
|
||||
self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower()
|
||||
self.show()
|
||||
|
||||
def toggle_f(self):
|
||||
# the self.active var is doing the 'toggle'
|
||||
if self.active is True:
|
||||
self.app.command_active = "Measurement"
|
||||
|
||||
# initial view of the layout
|
||||
self.start_entry.set_value('(0, 0)')
|
||||
self.stop_entry.set_value('(0, 0)')
|
||||
|
||||
self.distance_x_entry.set_value('0')
|
||||
self.distance_y_entry.set_value('0')
|
||||
self.total_distance_entry.set_value('0')
|
||||
|
||||
def activate(self):
|
||||
# we disconnect the mouse/key handlers from wherever the measurement tool was called
|
||||
self.canvas.vis_disconnect('key_press')
|
||||
self.canvas.vis_disconnect('mouse_move')
|
||||
self.canvas.vis_disconnect('mouse_press')
|
||||
self.canvas.vis_disconnect('mouse_release')
|
||||
self.canvas.vis_disconnect('key_release')
|
||||
|
||||
# we can safely connect the app mouse events to the measurement tool
|
||||
self.canvas.vis_connect('mouse_move', self.on_mouse_move_meas)
|
||||
self.canvas.vis_connect('mouse_release', self.on_mouse_click)
|
||||
self.canvas.vis_connect('key_release', self.on_key_release_meas)
|
||||
|
||||
self.set_tool_ui()
|
||||
|
||||
def deactivate(self):
|
||||
# disconnect the mouse/key events from functions of measurement tool
|
||||
self.canvas.vis_disconnect('mouse_move', self.on_mouse_move_meas)
|
||||
self.canvas.vis_disconnect('mouse_release', self.on_mouse_click)
|
||||
self.canvas.vis_disconnect('key_release', self.on_key_release_meas)
|
||||
|
||||
# reconnect the mouse/key events to the functions from where the tool was called
|
||||
self.canvas.vis_connect('key_press', self.app.ui.keyPressEvent)
|
||||
|
||||
if self.app.call_source == 'app':
|
||||
self.canvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot)
|
||||
self.canvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
|
||||
self.canvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
|
||||
elif self.app.call_source == 'geo_editor':
|
||||
self.canvas.vis_connect('mouse_move', self.app.geo_editor.on_canvas_move)
|
||||
self.canvas.vis_connect('mouse_press', self.app.geo_editor.on_canvas_click)
|
||||
# self.canvas.vis_connect('key_press', self.app.geo_editor.on_canvas_key)
|
||||
self.canvas.vis_connect('mouse_release', self.app.geo_editor.on_canvas_click_release)
|
||||
elif self.app.call_source == 'exc_editor':
|
||||
self.canvas.vis_connect('mouse_move', self.app.exc_editor.on_canvas_move)
|
||||
self.canvas.vis_connect('mouse_press', self.app.exc_editor.on_canvas_click)
|
||||
# self.canvas.vis_connect('key_press', self.app.exc_editor.on_canvas_key)
|
||||
self.canvas.vis_connect('mouse_release', self.app.exc_editor.on_canvas_click_release)
|
||||
elif self.app.call_source == 'grb_editor':
|
||||
self.canvas.vis_connect('mouse_move', self.app.grb_editor.on_canvas_move)
|
||||
self.canvas.vis_connect('mouse_press', self.app.grb_editor.on_canvas_click)
|
||||
# self.canvas.vis_connect('key_press', self.app.grb_editor.on_canvas_key)
|
||||
self.canvas.vis_connect('mouse_release', self.app.grb_editor.on_canvas_click_release)
|
||||
|
||||
self.app.ui.notebook.setTabText(2, _("Tools"))
|
||||
self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
|
||||
|
||||
def on_measure(self, signal=None, activate=None):
|
||||
log.debug("Measurement.on_measure()")
|
||||
if activate is False or activate is None:
|
||||
# DISABLE the Measuring TOOL
|
||||
self.active = False
|
||||
self.deactivate()
|
||||
|
||||
# disconnect the mouse/key events from functions of measurement tool
|
||||
self.app.plotcanvas.vis_disconnect('mouse_move', self.on_mouse_move_meas)
|
||||
self.app.plotcanvas.vis_disconnect('mouse_press', self.on_click_meas)
|
||||
|
||||
# reconnect the mouse/key events to the functions from where the tool was called
|
||||
if self.app.call_source == 'app':
|
||||
self.app.plotcanvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot)
|
||||
self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
|
||||
self.app.plotcanvas.vis_connect('key_press', self.app.ui.keyPressEvent)
|
||||
self.app.plotcanvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
|
||||
elif self.app.call_source == 'geo_editor':
|
||||
self.app.geo_editor.canvas.vis_connect('mouse_move', self.app.geo_editor.on_canvas_move)
|
||||
self.app.geo_editor.canvas.vis_connect('mouse_press', self.app.geo_editor.on_canvas_click)
|
||||
self.app.geo_editor.canvas.vis_connect('key_press', self.app.geo_editor.on_canvas_key)
|
||||
self.app.geo_editor.canvas.vis_connect('mouse_release', self.app.geo_editor.on_canvas_click_release)
|
||||
elif self.app.call_source == 'exc_editor':
|
||||
self.app.exc_editor.canvas.vis_connect('mouse_move', self.app.exc_editor.on_canvas_move)
|
||||
self.app.exc_editor.canvas.vis_connect('mouse_press', self.app.exc_editor.on_canvas_click)
|
||||
self.app.exc_editor.canvas.vis_connect('key_press', self.app.exc_editor.on_canvas_key)
|
||||
self.app.exc_editor.canvas.vis_connect('mouse_release', self.app.exc_editor.on_canvas_click_release)
|
||||
|
||||
self.app.call_source = 'measurement'
|
||||
self.clicked_meas = 0
|
||||
self.app.command_active = None
|
||||
|
||||
# delete the measuring line
|
||||
self.delete_shape()
|
||||
return
|
||||
else:
|
||||
|
||||
log.debug("Measurement Tool --> exit tool")
|
||||
elif activate is True:
|
||||
# ENABLE the Measuring TOOL
|
||||
self.active = True
|
||||
self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower()
|
||||
|
||||
# we disconnect the mouse/key handlers from wherever the measurement tool was called
|
||||
if self.app.call_source == 'app':
|
||||
self.app.plotcanvas.vis_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
|
||||
self.app.plotcanvas.vis_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
|
||||
self.app.plotcanvas.vis_disconnect('key_press', self.app.ui.keyPressEvent)
|
||||
self.app.plotcanvas.vis_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
|
||||
elif self.app.call_source == 'geo_editor':
|
||||
self.app.geo_editor.canvas.vis_disconnect('mouse_move', self.app.geo_editor.on_canvas_move)
|
||||
self.app.geo_editor.canvas.vis_disconnect('mouse_press', self.app.geo_editor.on_canvas_click)
|
||||
self.app.geo_editor.canvas.vis_disconnect('key_press', self.app.geo_editor.on_canvas_key)
|
||||
self.app.geo_editor.canvas.vis_disconnect('mouse_release', self.app.geo_editor.on_canvas_click_release)
|
||||
elif self.app.call_source == 'exc_editor':
|
||||
self.app.exc_editor.canvas.vis_disconnect('mouse_move', self.app.exc_editor.on_canvas_move)
|
||||
self.app.exc_editor.canvas.vis_disconnect('mouse_press', self.app.exc_editor.on_canvas_click)
|
||||
self.app.exc_editor.canvas.vis_disconnect('key_press', self.app.exc_editor.on_canvas_key)
|
||||
self.app.exc_editor.canvas.vis_disconnect('mouse_release', self.app.exc_editor.on_canvas_click_release)
|
||||
|
||||
# we can safely connect the app mouse events to the measurement tool
|
||||
self.app.plotcanvas.vis_connect('mouse_move', self.on_mouse_move_meas)
|
||||
self.app.plotcanvas.vis_connect('mouse_press', self.on_click_meas)
|
||||
self.app.plotcanvas.vis_connect('key_release', self.on_key_release_meas)
|
||||
|
||||
self.app.command_active = "Measurement"
|
||||
|
||||
# initial view of the layout
|
||||
self.start_entry.set_value('(0, 0)')
|
||||
self.stop_entry.set_value('(0, 0)')
|
||||
|
||||
self.distance_x_entry.set_value('0')
|
||||
self.distance_y_entry.set_value('0')
|
||||
self.total_distance_entry.set_value('0')
|
||||
|
||||
self.units_entry_1.set_value(str(self.units))
|
||||
self.units_entry_2.set_value(str(self.units))
|
||||
self.units_entry_3.set_value(str(self.units))
|
||||
self.units_entry_4.set_value(str(self.units))
|
||||
self.units_entry_5.set_value(str(self.units))
|
||||
self.clicked_meas = 0
|
||||
|
||||
self.app.inform.emit(_("MEASURING: Click on the Start point ..."))
|
||||
self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower()
|
||||
|
||||
self.activate()
|
||||
log.debug("Measurement Tool --> tool initialized")
|
||||
|
||||
def on_key_release_meas(self, event):
|
||||
if event.key == 'escape':
|
||||
# abort the measurement action
|
||||
self.toggle()
|
||||
self.on_measure(activate=False)
|
||||
self.app.inform.emit(_("Measurement Tool exit..."))
|
||||
return
|
||||
|
||||
if event.key == 'G':
|
||||
|
@ -286,63 +236,57 @@ class Measurement(FlatCAMTool):
|
|||
self.app.ui.grid_snap_btn.trigger()
|
||||
return
|
||||
|
||||
def on_click_meas(self, event):
|
||||
# mouse click will be accepted only if the left button is clicked
|
||||
# this is necessary because right mouse click and middle mouse click
|
||||
def on_mouse_click(self, event):
|
||||
# mouse click releases will be accepted only if the left button is clicked
|
||||
# this is necessary because right mouse click or middle mouse click
|
||||
# are used for panning on the canvas
|
||||
|
||||
if event.button == 1:
|
||||
pos_canvas = self.canvas.vispy_canvas.translate_coords(event.pos)
|
||||
|
||||
if self.clicked_meas == 0:
|
||||
pos_canvas = self.app.plotcanvas.vispy_canvas.translate_coords(event.pos)
|
||||
self.clicked_meas = 1
|
||||
|
||||
# if GRID is active we need to get the snapped positions
|
||||
if self.app.grid_status() == True:
|
||||
pos = self.app.geo_editor.snap(pos_canvas[0], pos_canvas[1])
|
||||
else:
|
||||
pos = pos_canvas[0], pos_canvas[1]
|
||||
|
||||
self.point1 = pos
|
||||
self.start_entry.set_value("(%.4f, %.4f)" % pos)
|
||||
self.app.inform.emit(_("MEASURING: Click on the Destination point ..."))
|
||||
|
||||
if self.clicked_meas == 1:
|
||||
try:
|
||||
pos_canvas = self.app.plotcanvas.vispy_canvas.translate_coords(event.pos)
|
||||
else:
|
||||
# delete the selection bounding box
|
||||
self.delete_shape()
|
||||
|
||||
# delete the selection bounding box
|
||||
self.delete_shape()
|
||||
# if GRID is active we need to get the snapped positions
|
||||
if self.app.grid_status() is True:
|
||||
pos = self.app.geo_editor.snap(pos_canvas[0], pos_canvas[1])
|
||||
else:
|
||||
pos = pos_canvas[0], pos_canvas[1]
|
||||
|
||||
# if GRID is active we need to get the snapped positions
|
||||
if self.app.grid_status() == True:
|
||||
pos = self.app.geo_editor.snap(pos_canvas[0], pos_canvas[1])
|
||||
else:
|
||||
pos = pos_canvas[0], pos_canvas[1]
|
||||
dx = pos[0] - self.point1[0]
|
||||
dy = pos[1] - self.point1[1]
|
||||
d = sqrt(dx ** 2 + dy ** 2)
|
||||
|
||||
dx = pos[0] - self.point1[0]
|
||||
dy = pos[1] - self.point1[1]
|
||||
d = sqrt(dx**2 + dy**2)
|
||||
self.stop_entry.set_value("(%.4f, %.4f)" % pos)
|
||||
|
||||
self.stop_entry.set_value("(%.4f, %.4f)" % pos)
|
||||
self.app.inform.emit(_("MEASURING: Result D(x) = {d_x} | D(y) = {d_y} | Distance = {d_z}").format(
|
||||
d_x='%4f' % abs(dx), d_y='%4f' % abs(dy), d_z='%4f' % abs(d)))
|
||||
|
||||
self.app.inform.emit(_("MEASURING: Result D(x) = {d_x} | D(y) = {d_y} | Distance = {d_z}").format(
|
||||
d_x='%4f' % abs(dx), d_y='%4f' % abs(dy), d_z='%4f' % abs(d)))
|
||||
self.distance_x_entry.set_value('%.4f' % abs(dx))
|
||||
self.distance_y_entry.set_value('%.4f' % abs(dy))
|
||||
self.total_distance_entry.set_value('%.4f' % abs(d))
|
||||
|
||||
self.distance_x_entry.set_value('%.4f' % abs(dx))
|
||||
self.distance_y_entry.set_value('%.4f' % abs(dy))
|
||||
self.total_distance_entry.set_value('%.4f' % abs(d))
|
||||
|
||||
self.clicked_meas = 0
|
||||
self.toggle_f()
|
||||
|
||||
# delete the measuring line
|
||||
self.delete_shape()
|
||||
return
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
self.clicked_meas = 1
|
||||
# TODO: I don't understand why I have to do it twice ... but without it the mouse handlers are
|
||||
# TODO: are left disconnected ...
|
||||
self.on_measure(activate=False)
|
||||
self.deactivate()
|
||||
|
||||
def on_mouse_move_meas(self, event):
|
||||
pos_canvas = self.app.plotcanvas.vispy_canvas.translate_coords(event.pos)
|
||||
pos_canvas = self.canvas.vispy_canvas.translate_coords(event.pos)
|
||||
|
||||
# if GRID is active we need to get the snapped positions
|
||||
if self.app.grid_status() == True:
|
||||
|
@ -356,21 +300,18 @@ class Measurement(FlatCAMTool):
|
|||
|
||||
self.point2 = (pos[0], pos[1])
|
||||
|
||||
# update utility geometry
|
||||
if self.clicked_meas == 1:
|
||||
self.update_meas_shape([self.point2, self.point1])
|
||||
|
||||
def update_meas_shape(self, pos):
|
||||
self.delete_shape()
|
||||
self.draw_shape(pos)
|
||||
# first delete old shape
|
||||
self.delete_shape()
|
||||
# second draw the new shape of the utility geometry
|
||||
self.meas_line = LineString([self.point2, self.point1])
|
||||
self.sel_shapes.add(self.meas_line, color='black', update=True, layer=0, tolerance=None)
|
||||
|
||||
def delete_shape(self):
|
||||
self.sel_shapes.clear()
|
||||
self.sel_shapes.redraw()
|
||||
|
||||
def draw_shape(self, coords):
|
||||
self.meas_line = LineString(coords)
|
||||
self.sel_shapes.add(self.meas_line, color='black', update=True, layer=0, tolerance=None)
|
||||
|
||||
def set_meas_units(self, units):
|
||||
self.meas.units_label.setText("[" + self.app.options["units"].lower() + "]")
|
||||
|
||||
|
|
|
@ -161,7 +161,7 @@ class ToolMove(FlatCAMTool):
|
|||
proc.done()
|
||||
# delete the selection bounding box
|
||||
self.delete_shape()
|
||||
self.app.inform.emit(_('[success]%s object was moved ...') %
|
||||
self.app.inform.emit(_('[success] %s object was moved ...') %
|
||||
str(sel_obj.kind).capitalize())
|
||||
|
||||
self.app.worker_task.emit({'fcn': job_move, 'params': [self]})
|
||||
|
@ -199,7 +199,7 @@ class ToolMove(FlatCAMTool):
|
|||
def on_key_press(self, event):
|
||||
if event.key == 'escape':
|
||||
# abort the move action
|
||||
self.app.inform.emit(_("[WARNING_NOTCL]Move action cancelled."))
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] Move action cancelled."))
|
||||
self.toggle()
|
||||
return
|
||||
|
||||
|
@ -211,7 +211,7 @@ class ToolMove(FlatCAMTool):
|
|||
|
||||
obj_list = self.app.collection.get_selected()
|
||||
if not obj_list:
|
||||
self.app.inform.emit(_("[WARNING_NOTCL]Object(s) not selected"))
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] Object(s) not selected"))
|
||||
self.toggle()
|
||||
else:
|
||||
# if we have an object selected then we can safely activate the mouse events
|
||||
|
|
|
@ -13,6 +13,7 @@ import time
|
|||
|
||||
import gettext
|
||||
import FlatCAMTranslation as fcTranslate
|
||||
from shapely.geometry import base
|
||||
|
||||
fcTranslate.apply_language('strings')
|
||||
import builtins
|
||||
|
@ -161,7 +162,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
|||
e_lab_1 = QtWidgets.QLabel('')
|
||||
grid3.addWidget(e_lab_1, 0, 0)
|
||||
|
||||
nccoverlabel = QtWidgets.QLabel(_('Overlap:'))
|
||||
nccoverlabel = QtWidgets.QLabel(_('Overlap Rate:'))
|
||||
nccoverlabel.setToolTip(
|
||||
_("How much (fraction) of the tool width to overlap each tool pass.\n"
|
||||
"Example:\n"
|
||||
|
@ -475,7 +476,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
|||
try:
|
||||
tool_dia = float(self.addtool_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
if tool_dia is None:
|
||||
|
@ -508,7 +509,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
|||
|
||||
if float('%.4f' % tool_dia) in tool_dias:
|
||||
if muted is None:
|
||||
self.app.inform.emit(_("[WARNING_NOTCL]Adding tool cancelled. Tool already in Tool Table."))
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] Adding tool cancelled. Tool already in Tool Table."))
|
||||
self.tools_table.itemChanged.connect(self.on_tool_edit)
|
||||
return
|
||||
else:
|
||||
|
@ -605,7 +606,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
|||
self.ncc_tools.pop(t, None)
|
||||
|
||||
except AttributeError:
|
||||
self.app.inform.emit(_("[WARNING_NOTCL]Delete failed. Select a tool to delete."))
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] Delete failed. Select a tool to delete."))
|
||||
return
|
||||
except Exception as e:
|
||||
log.debug(str(e))
|
||||
|
@ -622,11 +623,16 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
|||
try:
|
||||
over = float(self.ncc_overlap_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
over = over if over else self.app.defaults["tools_nccoverlap"]
|
||||
|
||||
if over >= 1 or over < 0:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Overlap value must be between "
|
||||
"0 (inclusive) and 1 (exclusive), "))
|
||||
return
|
||||
|
||||
try:
|
||||
margin = float(self.ncc_margin_entry.get_value())
|
||||
except ValueError:
|
||||
|
@ -634,7 +640,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
|||
try:
|
||||
margin = float(self.ncc_margin_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
margin = margin if margin else self.app.defaults["tools_nccmargin"]
|
||||
|
@ -656,14 +662,14 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
|||
try:
|
||||
self.ncc_obj = self.app.collection.get_by_name(self.obj_name)
|
||||
except:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Could not retrieve object: %s") % self.obj_name)
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % self.obj_name)
|
||||
return "Could not retrieve object: %s" % self.obj_name
|
||||
|
||||
# Prepare non-copper polygons
|
||||
try:
|
||||
bounding_box = self.ncc_obj.solid_geometry.envelope.buffer(distance=margin, join_style=JOIN_STYLE.mitre)
|
||||
bounding_box = self.ncc_obj.solid_geometry.envelope.buffer(distance=margin, join_style=base.JOIN_STYLE.mitre)
|
||||
except AttributeError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]No Gerber file available."))
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] No Gerber file available."))
|
||||
return
|
||||
|
||||
# calculate the empty area by subtracting the solid_geometry from the object bounding box geometry
|
||||
|
|
|
@ -157,7 +157,7 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
self.tools_box.addLayout(grid3)
|
||||
|
||||
# Overlap
|
||||
ovlabel = QtWidgets.QLabel(_('Overlap:'))
|
||||
ovlabel = QtWidgets.QLabel(_('Overlap Rate:'))
|
||||
ovlabel.setToolTip(
|
||||
_("How much (fraction) of the tool width to overlap each tool pass.\n"
|
||||
"Example:\n"
|
||||
|
@ -534,7 +534,7 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
try:
|
||||
tool_dia = float(self.addtool_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
|
||||
|
@ -564,7 +564,7 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
|
||||
if float('%.4f' % tool_dia) in tool_dias:
|
||||
if muted is None:
|
||||
self.app.inform.emit(_("[WARNING_NOTCL]Adding tool cancelled. Tool already in Tool Table."))
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] Adding tool cancelled. Tool already in Tool Table."))
|
||||
self.tools_table.itemChanged.connect(self.on_tool_edit)
|
||||
return
|
||||
else:
|
||||
|
@ -604,7 +604,7 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
try:
|
||||
new_tool_dia = float(self.tools_table.item(row, 1).text().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
tooluid = int(self.tools_table.item(row, 3).text())
|
||||
|
@ -656,7 +656,7 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
# print("COPIED", self.paint_tools[td])
|
||||
# self.build_ui()
|
||||
# except AttributeError:
|
||||
# self.app.inform.emit("[WARNING_NOTCL]Failed. Select a tool to copy.")
|
||||
# self.app.inform.emit("[WARNING_NOTCL] Failed. Select a tool to copy.")
|
||||
# self.build_ui()
|
||||
# return
|
||||
# except Exception as e:
|
||||
|
@ -664,7 +664,7 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
# # deselect the table
|
||||
# # self.ui.geo_tools_table.clearSelection()
|
||||
# else:
|
||||
# self.app.inform.emit("[WARNING_NOTCL]Failed. Select a tool to copy.")
|
||||
# self.app.inform.emit("[WARNING_NOTCL] Failed. Select a tool to copy.")
|
||||
# self.build_ui()
|
||||
# return
|
||||
# else:
|
||||
|
@ -720,7 +720,7 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
self.paint_tools.pop(t, None)
|
||||
|
||||
except AttributeError:
|
||||
self.app.inform.emit(_("[WARNING_NOTCL]Delete failed. Select a tool to delete."))
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] Delete failed. Select a tool to delete."))
|
||||
return
|
||||
except Exception as e:
|
||||
log.debug(str(e))
|
||||
|
@ -730,9 +730,8 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
|
||||
def on_paint_button_click(self):
|
||||
self.app.report_usage(_("geometry_on_paint_button"))
|
||||
self.app.call_source = 'paint'
|
||||
# self.app.call_source = 'paint'
|
||||
|
||||
self.app.inform.emit(_("[WARNING_NOTCL]Click inside the desired polygon."))
|
||||
try:
|
||||
overlap = float(self.paintoverlap_entry.get_value())
|
||||
except ValueError:
|
||||
|
@ -740,10 +739,17 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
try:
|
||||
overlap = float(self.paintoverlap_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
|
||||
if overlap >= 1 or overlap < 0:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Overlap value must be between "
|
||||
"0 (inclusive) and 1 (exclusive), "))
|
||||
return
|
||||
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] Click inside the desired polygon."))
|
||||
|
||||
connect = self.pathconnect_cb.get_value()
|
||||
contour = self.paintcontour_cb.get_value()
|
||||
select_method = self.selectmethod_combo.get_value()
|
||||
|
@ -754,11 +760,11 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
try:
|
||||
self.paint_obj = self.app.collection.get_by_name(str(self.obj_name))
|
||||
except:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Could not retrieve object: %s") % self.obj_name)
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % self.obj_name)
|
||||
return
|
||||
|
||||
if self.paint_obj is None:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Object not found: %s") % self.paint_obj)
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Object not found: %s") % self.paint_obj)
|
||||
return
|
||||
|
||||
# test if the Geometry Object is multigeo and return Fail if True because
|
||||
|
@ -777,7 +783,7 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
contour=contour)
|
||||
|
||||
if select_method == "single":
|
||||
self.app.inform.emit(_("[WARNING_NOTCL]Click inside the desired polygon."))
|
||||
self.app.inform.emit(_("[WARNING_NOTCL] Click inside the desired polygon."))
|
||||
|
||||
# use the first tool in the tool table; get the diameter
|
||||
tooldia = float('%.4f' % float(self.tools_table.item(0, 1).text()))
|
||||
|
@ -830,7 +836,7 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
try:
|
||||
paint_margin = float(self.paintmargin_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
|
||||
|
@ -985,7 +991,7 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
try:
|
||||
paint_margin = float(self.paintmargin_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
|
||||
|
@ -1068,8 +1074,9 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
|
||||
for geo in recurse(obj.solid_geometry):
|
||||
try:
|
||||
#Polygons are the only really paintable geometries, lines in theory have no area to be painted
|
||||
if not isinstance(geo, Polygon):
|
||||
geo = Polygon(geo)
|
||||
continue
|
||||
poly_buf = geo.buffer(-paint_margin)
|
||||
|
||||
if paint_method == "seed":
|
||||
|
|
|
@ -290,13 +290,13 @@ class Panelize(FlatCAMTool):
|
|||
try:
|
||||
obj = self.app.collection.get_by_name(str(name))
|
||||
except:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Could not retrieve object: %s") % name)
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % name)
|
||||
return "Could not retrieve object: %s" % name
|
||||
|
||||
panel_obj = obj
|
||||
|
||||
if panel_obj is None:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Object not found: %s") % panel_obj)
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Object not found: %s") % panel_obj)
|
||||
return "Object not found: %s" % panel_obj
|
||||
|
||||
boxname = self.box_combo.currentText()
|
||||
|
@ -304,7 +304,7 @@ class Panelize(FlatCAMTool):
|
|||
try:
|
||||
box = self.app.collection.get_by_name(boxname)
|
||||
except:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Could not retrieve object: %s") % boxname)
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % boxname)
|
||||
return "Could not retrieve object: %s" % boxname
|
||||
|
||||
if box is None:
|
||||
|
@ -320,7 +320,7 @@ class Panelize(FlatCAMTool):
|
|||
try:
|
||||
spacing_columns = float(self.spacing_columns.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
spacing_columns = spacing_columns if spacing_columns is not None else 0
|
||||
|
@ -332,7 +332,7 @@ class Panelize(FlatCAMTool):
|
|||
try:
|
||||
spacing_rows = float(self.spacing_rows.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
spacing_rows = spacing_rows if spacing_rows is not None else 0
|
||||
|
@ -345,7 +345,7 @@ class Panelize(FlatCAMTool):
|
|||
rows = float(self.rows.get_value().replace(',', '.'))
|
||||
rows = int(rows)
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
rows = rows if rows is not None else 1
|
||||
|
@ -358,7 +358,7 @@ class Panelize(FlatCAMTool):
|
|||
columns = float(self.columns.get_value().replace(',', '.'))
|
||||
columns = int(columns)
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
columns = columns if columns is not None else 1
|
||||
|
@ -370,7 +370,7 @@ class Panelize(FlatCAMTool):
|
|||
try:
|
||||
constrain_dx = float(self.x_width_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
|
||||
|
@ -381,7 +381,7 @@ class Panelize(FlatCAMTool):
|
|||
try:
|
||||
constrain_dy = float(self.y_height_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
|
||||
|
@ -389,7 +389,7 @@ class Panelize(FlatCAMTool):
|
|||
|
||||
|
||||
if 0 in {columns, rows}:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Columns or Rows are zero value. Change them to a positive integer."))
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Columns or Rows are zero value. Change them to a positive integer."))
|
||||
return "Columns or Rows are zero value. Change them to a positive integer."
|
||||
|
||||
xmin, ymin, xmax, ymax = box.bounds()
|
||||
|
@ -517,7 +517,7 @@ class Panelize(FlatCAMTool):
|
|||
plot=True, autoselected=True)
|
||||
|
||||
if self.constrain_flag is False:
|
||||
self.app.inform.emit(_("[success]Panel done..."))
|
||||
self.app.inform.emit(_("[success] Panel done..."))
|
||||
else:
|
||||
self.constrain_flag = False
|
||||
self.app.inform.emit(_("[WARNING] Too big for the constrain area. Final panel has {col} columns and {row} rows").format(
|
||||
|
@ -528,7 +528,7 @@ class Panelize(FlatCAMTool):
|
|||
def job_thread(app_obj):
|
||||
try:
|
||||
panelize_2()
|
||||
self.app.inform.emit(_("[success]Panel created successfully."))
|
||||
self.app.inform.emit(_("[success] Panel created successfully."))
|
||||
except Exception as e:
|
||||
proc.done()
|
||||
log.debug(str(e))
|
||||
|
|
|
@ -178,6 +178,12 @@ class Properties(FlatCAMTool):
|
|||
if obj.apertures[ap]['solid_geometry']:
|
||||
elems = len(obj.apertures[ap]['solid_geometry'])
|
||||
temp_ap['solid_geometry'] = '%s Polygons' % str(elems)
|
||||
try:
|
||||
if obj.apertures[ap]['follow_geometry']:
|
||||
elems = len(obj.apertures[ap]['follow_geometry'])
|
||||
temp_ap['follow_geometry'] = '%s Polygons' % str(elems)
|
||||
except KeyError:
|
||||
pass
|
||||
self.addChild(apertures, [str(ap), str(temp_ap)], True)
|
||||
elif obj.kind.lower() == 'excellon':
|
||||
for tool, value in obj.tools.items():
|
||||
|
|
|
@ -752,7 +752,7 @@ class SolderPaste(FlatCAMTool):
|
|||
try:
|
||||
tool_dia = float(self.addtool_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
if tool_dia is None:
|
||||
|
@ -823,7 +823,7 @@ class SolderPaste(FlatCAMTool):
|
|||
try:
|
||||
new_tool_dia = float(self.tools_table.item(row, 1).text().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
|
||||
"use a number."))
|
||||
return
|
||||
|
||||
|
|
|
@ -465,7 +465,7 @@ class ToolTransform(FlatCAMTool):
|
|||
try:
|
||||
value = float(self.rotate_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered for Rotate, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Rotate, "
|
||||
"use a number."))
|
||||
return
|
||||
self.app.worker_task.emit({'fcn': self.on_rotate_action,
|
||||
|
@ -499,7 +499,7 @@ class ToolTransform(FlatCAMTool):
|
|||
try:
|
||||
value = float(self.skewx_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered for Skew X, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Skew X, "
|
||||
"use a number."))
|
||||
return
|
||||
|
||||
|
@ -517,7 +517,7 @@ class ToolTransform(FlatCAMTool):
|
|||
try:
|
||||
value = float(self.skewy_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered for Skew Y, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Skew Y, "
|
||||
"use a number."))
|
||||
return
|
||||
|
||||
|
@ -535,7 +535,7 @@ class ToolTransform(FlatCAMTool):
|
|||
try:
|
||||
xvalue = float(self.scalex_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered for Scale X, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Scale X, "
|
||||
"use a number."))
|
||||
return
|
||||
|
||||
|
@ -569,7 +569,7 @@ class ToolTransform(FlatCAMTool):
|
|||
try:
|
||||
yvalue = float(self.scaley_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered for Scale Y, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Scale Y, "
|
||||
"use a number."))
|
||||
return
|
||||
|
||||
|
@ -598,7 +598,7 @@ class ToolTransform(FlatCAMTool):
|
|||
try:
|
||||
value = float(self.offx_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered for Offset X, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Offset X, "
|
||||
"use a number."))
|
||||
return
|
||||
|
||||
|
@ -616,7 +616,7 @@ class ToolTransform(FlatCAMTool):
|
|||
try:
|
||||
value = float(self.offy_entry.get_value().replace(',', '.'))
|
||||
except ValueError:
|
||||
self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered for Offset Y, "
|
||||
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Offset Y, "
|
||||
"use a number."))
|
||||
return
|
||||
|
||||
|
@ -671,7 +671,7 @@ class ToolTransform(FlatCAMTool):
|
|||
# add information to the object that it was changed and how much
|
||||
sel_obj.options['rotate'] = num
|
||||
|
||||
self.app.inform.emit(_('[success]Rotate done ...'))
|
||||
self.app.inform.emit(_('[success] Rotate done ...'))
|
||||
self.app.progress.emit(100)
|
||||
|
||||
except Exception as e:
|
||||
|
@ -732,7 +732,7 @@ class ToolTransform(FlatCAMTool):
|
|||
else:
|
||||
obj.options['mirror_y'] = True
|
||||
obj.plot()
|
||||
self.app.inform.emit(_('[success]Flip on the Y axis done ...'))
|
||||
self.app.inform.emit(_('[success] Flip on the Y axis done ...'))
|
||||
elif axis is 'Y':
|
||||
obj.mirror('Y', (px, py))
|
||||
# add information to the object that it was changed and how much
|
||||
|
@ -742,7 +742,7 @@ class ToolTransform(FlatCAMTool):
|
|||
else:
|
||||
obj.options['mirror_x'] = True
|
||||
obj.plot()
|
||||
self.app.inform.emit(_('[success]Flip on the X axis done ...'))
|
||||
self.app.inform.emit(_('[success] Flip on the X axis done ...'))
|
||||
self.app.object_changed.emit(obj)
|
||||
self.app.progress.emit(100)
|
||||
|
||||
|
@ -790,7 +790,7 @@ class ToolTransform(FlatCAMTool):
|
|||
obj.options['skew_y'] = num
|
||||
obj.plot()
|
||||
self.app.object_changed.emit(obj)
|
||||
self.app.inform.emit(_('[success]Skew on the %s axis done ...') % str(axis))
|
||||
self.app.inform.emit(_('[success] Skew on the %s axis done ...') % str(axis))
|
||||
self.app.progress.emit(100)
|
||||
|
||||
except Exception as e:
|
||||
|
@ -891,7 +891,7 @@ class ToolTransform(FlatCAMTool):
|
|||
obj.options['offset_y'] = num
|
||||
obj.plot()
|
||||
self.app.object_changed.emit(obj)
|
||||
self.app.inform.emit(_('[success]Offset on the %s axis done ...') % str(axis))
|
||||
self.app.inform.emit(_('[success] Offset on the %s axis done ...') % str(axis))
|
||||
self.app.progress.emit(100)
|
||||
|
||||
except Exception as e:
|
||||
|
|
After Width: | Height: | Size: 377 B |
After Width: | Height: | Size: 796 B |
After Width: | Height: | Size: 289 B |
After Width: | Height: | Size: 246 B |
After Width: | Height: | Size: 300 B |
After Width: | Height: | Size: 341 B |
After Width: | Height: | Size: 342 B |
|
@ -314,7 +314,7 @@ class TclCommandSignaled(TclCommand):
|
|||
|
||||
|
||||
This class is child of TclCommand and is used for commands which create new objects
|
||||
it handles all neccessary stuff about blocking and passing exeptions
|
||||
it handles all necessary stuff about blocking and passing exceptions
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
|
|
|
@ -121,6 +121,6 @@ class TclCommandCutout(TclCommand):
|
|||
|
||||
try:
|
||||
obj.app.new_object("geometry", name + "_cutout", geo_init_me)
|
||||
self.app.inform.emit("[success]Rectangular-form Cutout operation finished.")
|
||||
self.app.inform.emit("[success] Rectangular-form Cutout operation finished.")
|
||||
except Exception as e:
|
||||
return "Operation failed: %s" % str(e)
|
||||
|
|
|
@ -155,8 +155,8 @@ class TclCommandGeoCutout(TclCommandSignaled):
|
|||
return "Could not retrieve object: %s" % name
|
||||
|
||||
if 0 in {dia}:
|
||||
self.app.inform.emit("[WARNING]Tool Diameter is zero value. Change it to a positive integer.")
|
||||
return "Tool Diameter is zero value. Change it to a positive integer."
|
||||
self.app.inform.emit("[WARNING]Tool Diameter is zero value. Change it to a positive real number.")
|
||||
return "Tool Diameter is zero value. Change it to a positive real number."
|
||||
|
||||
if gaps not in ['lr', 'tb', '2lr', '2tb', 4, 8]:
|
||||
self.app.inform.emit("[WARNING]Gaps value can be only one of: 'lr', 'tb', '2lr', '2tb', 4 or 8. "
|
||||
|
@ -220,7 +220,7 @@ class TclCommandGeoCutout(TclCommandSignaled):
|
|||
ymax + gapsize)
|
||||
|
||||
cutout_obj.plot()
|
||||
self.app.inform.emit("[success]Any-form Cutout operation finished.")
|
||||
self.app.inform.emit("[success] Any-form Cutout operation finished.")
|
||||
elif isinstance(cutout_obj, FlatCAMGerber):
|
||||
|
||||
def geo_init(geo_obj, app_obj):
|
||||
|
|
|
@ -89,4 +89,4 @@ class TclCommandOpenGerber(TclCommandSignaled):
|
|||
self.app.progress.emit(100)
|
||||
|
||||
# GUI feedback
|
||||
self.app.inform.emit("[success]Opened: " + filename)
|
||||
self.app.inform.emit("[success] Opened: " + filename)
|
||||
|
|
|
@ -276,7 +276,7 @@ class TclCommandPanelize(TclCommand):
|
|||
def job_thread(app_obj):
|
||||
try:
|
||||
panelize_2()
|
||||
self.app.inform.emit("[success]Panel created successfully.")
|
||||
self.app.inform.emit("[success] Panel created successfully.")
|
||||
except Exception as e:
|
||||
proc.done()
|
||||
log.debug(str(e))
|
||||
|
@ -287,4 +287,4 @@ class TclCommandPanelize(TclCommand):
|
|||
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
|
||||
else:
|
||||
panelize_2()
|
||||
self.app.inform.emit("[success]Panel created successfully.")
|
||||
self.app.inform.emit("[success] Panel created successfully.")
|
||||
|
|