Merged in marius_stanciu/flatcam_beta/Beta (pull request #179)

Beta
This commit is contained in:
Marius Stanciu 2019-08-10 16:49:16 +00:00
commit f7818d50d6
29 changed files with 5353 additions and 4196 deletions

View File

@ -97,7 +97,7 @@ class App(QtCore.QObject):
# Version and VERSION DATE ###########
# ####################################
version = 8.93
version_date = "2019/08/31"
version_date = "2019/08/10"
beta = True
# current date now
@ -513,6 +513,7 @@ class App(QtCore.QObject):
"tools_nccconnect": self.ui.tools_defaults_form.tools_ncc_group.ncc_connect_cb,
"tools_ncccontour": self.ui.tools_defaults_form.tools_ncc_group.ncc_contour_cb,
"tools_nccrest": self.ui.tools_defaults_form.tools_ncc_group.ncc_rest_cb,
"tools_nccref": self.ui.tools_defaults_form.tools_ncc_group.reference_radio,
# CutOut Tool
"tools_cutouttooldia": self.ui.tools_defaults_form.tools_cutout_group.cutout_tooldia_entry,
@ -852,6 +853,7 @@ class App(QtCore.QObject):
"tools_nccconnect": True,
"tools_ncccontour": True,
"tools_nccrest": False,
"tools_nccref": 'itself',
"tools_cutouttooldia": 0.00393701,
"tools_cutoutkind": "single",
@ -2841,6 +2843,7 @@ class App(QtCore.QObject):
self.inform.emit(_("[ERROR_NOTCL] Failed to parse defaults file."))
return
self.defaults.update(defaults_from_file)
self.on_preferences_edited()
self.inform.emit(_("[success] Imported Defaults from %s") % filename)
def on_export_preferences(self):
@ -2875,6 +2878,10 @@ class App(QtCore.QObject):
f = open(filename, 'w')
defaults_file_content = f.read()
f.close()
except PermissionError:
self.inform.emit(_("[WARNING] Permission denied, saving not possible.\n"
"Most likely another app is holding the file open and not accessible."))
return
except IOError:
App.log.debug('Creating a new preferences file ...')
f = open(filename, 'w')
@ -4669,10 +4676,13 @@ class App(QtCore.QObject):
with open(filename, 'w') as f:
for line in my_gcode:
f.write(line)
except FileNotFoundError:
self.inform.emit(_("[WARNING] No such file or directory"))
return
except PermissionError:
self.inform.emit(_("[WARNING] Permission denied, saving not possible.\n"
"Most likely another app is holding the file open and not accessible."))
return
# Just for adding it to the recent files list.
if self.defaults["global_open_style"] is False:
@ -5691,13 +5701,12 @@ class App(QtCore.QObject):
self.plotcanvas.vispy_canvas.view.camera.pan_button_setting = self.defaults['global_pan_button']
self.pos_canvas = self.plotcanvas.vispy_canvas.translate_coords(event.pos)
self.pos = (self.pos_canvas[0], self.pos_canvas[1])
self.app_cursor.enabled = False
if self.grid_status() == True:
if self.grid_status():
self.pos = self.geo_editor.snap(self.pos_canvas[0], self.pos_canvas[1])
self.app_cursor.enabled = True
else:
self.pos = (self.pos_canvas[0], self.pos_canvas[1])
self.app_cursor.enabled = False
try:
modifiers = QtWidgets.QApplication.keyboardModifiers()
@ -5750,7 +5759,7 @@ class App(QtCore.QObject):
if self.rel_point1 is not None:
try: # May fail in case mouse not within axes
pos_canvas = self.plotcanvas.vispy_canvas.translate_coords(event.pos)
if self.grid_status():
if self.grid_status() == True:
pos = self.geo_editor.snap(pos_canvas[0], pos_canvas[1])
self.app_cursor.enabled = True
# Update cursor
@ -6154,6 +6163,12 @@ class App(QtCore.QObject):
face_color = kwargs['face_color']
else:
face_color = self.defaults['global_sel_fill']
if 'face_alpha' in kwargs:
face_alpha = kwargs['face_alpha']
else:
face_alpha = 0.3
x0, y0 = old_coords
x1, y1 = coords
pt1 = (x0, y0)
@ -6163,7 +6178,7 @@ class App(QtCore.QObject):
sel_rect = Polygon([pt1, pt2, pt3, pt4])
color_t = Color(face_color)
color_t.alpha = 0.3
color_t.alpha = face_alpha
self.move_tool.sel_shapes.add(sel_rect, color=color, face_color=color_t, update=True,
layer=0, tolerance=None)
@ -7109,8 +7124,14 @@ class App(QtCore.QObject):
# Parse the xml through a xml parser just to add line feeds
# and to make it look more pretty for the output
svgcode = parse_xml_string(svg_elem)
with open(filename, 'w') as fp:
fp.write(svgcode.toprettyxml())
try:
with open(filename, 'w') as fp:
fp.write(svgcode.toprettyxml())
except PermissionError:
self.inform.emit(_("[WARNING] Permission denied, saving not possible.\n"
"Most likely another app is holding the file open and not accessible."))
return 'fail'
if self.defaults["global_open_style"] is False:
self.file_opened.emit("SVG", filename)
self.file_saved.emit("SVG", filename)
@ -7213,8 +7234,13 @@ class App(QtCore.QObject):
# Parse the xml through a xml parser just to add line feeds
# and to make it look more pretty for the output
doc = parse_xml_string(svg_elem)
with open(filename, 'w') as fp:
fp.write(doc.toprettyxml())
try:
with open(filename, 'w') as fp:
fp.write(doc.toprettyxml())
except PermissionError:
self.inform.emit(_("[WARNING] Permission denied, saving not possible.\n"
"Most likely another app is holding the file open and not accessible."))
return 'fail'
self.progress.emit(100)
if self.defaults["global_open_style"] is False:
@ -7329,8 +7355,14 @@ class App(QtCore.QObject):
# Parse the xml through a xml parser just to add line feeds
# and to make it look more pretty for the output
doc = parse_xml_string(svg_elem)
with open(filename, 'w') as fp:
fp.write(doc.toprettyxml())
try:
with open(filename, 'w') as fp:
fp.write(doc.toprettyxml())
except PermissionError:
self.inform.emit(_("[WARNING] Permission denied, saving not possible.\n"
"Most likely another app is holding the file open and not accessible."))
return 'fail'
self.progress.emit(100)
if self.defaults["global_open_style"] is False:
self.file_opened.emit("SVG", filename)
@ -7371,15 +7403,20 @@ class App(QtCore.QObject):
file_string = StringIO(obj.source_file)
time_string = "{:%A, %d %B %Y at %H:%M}".format(datetime.now())
with open(filename, 'w') as file:
file.writelines('G04*\n')
file.writelines('G04 %s (RE)GENERATED BY FLATCAM v%s - www.flatcam.org - Version Date: %s*\n' %
(obj.kind.upper(), str(self.version), str(self.version_date)))
file.writelines('G04 Filename: %s*\n' % str(obj_name))
file.writelines('G04 Created on : %s*\n' % time_string)
try:
with open(filename, 'w') as file:
file.writelines('G04*\n')
file.writelines('G04 %s (RE)GENERATED BY FLATCAM v%s - www.flatcam.org - Version Date: %s*\n' %
(obj.kind.upper(), str(self.version), str(self.version_date)))
file.writelines('G04 Filename: %s*\n' % str(obj_name))
file.writelines('G04 Created on : %s*\n' % time_string)
for line in file_string:
file.writelines(line)
for line in file_string:
file.writelines(line)
except PermissionError:
self.inform.emit(_("[WARNING] Permission denied, saving not possible.\n"
"Most likely another app is holding the file open and not accessible."))
return 'fail'
def export_excellon(self, obj_name, filename, use_thread=True):
"""
@ -7481,8 +7518,14 @@ class App(QtCore.QObject):
exported_excellon += excellon_code
exported_excellon += footer
with open(filename, 'w') as fp:
fp.write(exported_excellon)
try:
with open(filename, 'w') as fp:
fp.write(exported_excellon)
except PermissionError:
self.inform.emit(_("[WARNING] Permission denied, saving not possible.\n"
"Most likely another app is holding the file open and not accessible."))
return 'fail'
if self.defaults["global_open_style"] is False:
self.file_opened.emit("Excellon", filename)
self.file_saved.emit("Excellon", filename)
@ -7598,8 +7641,14 @@ class App(QtCore.QObject):
exported_gerber += gerber_code
exported_gerber += footer
with open(filename, 'w') as fp:
fp.write(exported_gerber)
try:
with open(filename, 'w') as fp:
fp.write(exported_gerber)
except PermissionError:
self.inform.emit(_("[WARNING] Permission denied, saving not possible.\n"
"Most likely another app is holding the file open and not accessible."))
return 'fail'
if self.defaults["global_open_style"] is False:
self.file_opened.emit("Gerber", filename)
self.file_saved.emit("Gerber", filename)

View File

@ -24,8 +24,8 @@ if '_' not in builtins.__dict__:
_ = gettext.gettext
# Interrupts plotting process if FlatCAMObj has been deleted
class ObjectDeleted(Exception):
# Interrupts plotting process if FlatCAMObj has been deleted
pass
@ -364,10 +364,13 @@ class FlatCAMObj(QtCore.QObject):
@property
def drawing_tolerance(self):
return self._drawing_tolerance if self.units == 'MM' or not self.units else self._drawing_tolerance / 25.4
self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper()
tol = self._drawing_tolerance if self.units == 'MM' or not self.units else self._drawing_tolerance / 25.4
return tol
@drawing_tolerance.setter
def drawing_tolerance(self, value):
self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper()
self._drawing_tolerance = value if self.units == 'MM' or not self.units else value / 25.4
def clear(self, update=False):
@ -2666,9 +2669,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
job_obj.z_pdepth = float(self.options["z_pdepth"].replace(',', '.'))
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:
job_obj.feedrate_probe = float(self.options["feedrate_probe"])
@ -2678,11 +2679,8 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
job_obj.feedrate_rapid = float(self.options["feedrate_probe"].replace(',', '.'))
except ValueError:
self.app.inform.emit(
_(
'[ERROR_NOTCL] Wrong value format for self.defaults["feedrate_probe"] '
'or self.options["feedrate_probe"]'
)
)
_('[ERROR_NOTCL] Wrong value format for self.defaults["feedrate_probe"] '
'or self.options["feedrate_probe"]'))
# There could be more than one drill size...
# job_obj.tooldia = # TODO: duplicate variable!
@ -2736,10 +2734,8 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
else:
coords_xy = [float(eval(coord)) for coord in self.app.defaults["excellon_toolchangexy"].split(",")]
if len(coords_xy) < 2:
self.app.inform.emit(_(
"[ERROR]The Toolchange X,Y field in Edit -> Preferences has to be "
"in the format (x, y) \nbut now there is only one value, not two. "
))
self.app.inform.emit(_("[ERROR]The Toolchange X,Y field in Edit -> Preferences has to be "
"in the format (x, y) \nbut now there is only one value, not two. "))
return 'fail'
coords_xy[0] *= factor
coords_xy[1] *= factor
@ -2913,7 +2909,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
try:
geo_final.options[option] = geo.options[option]
except Exception as e:
log.warning("Failed to copy option.", option)
log.warning("Failed to copy option %s. Error: %s" % (str(option), str(e)))
# Expand lists
if type(geo) is list:
@ -3022,14 +3018,6 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
if "cnctooldia" not in self.options:
self.options["cnctooldia"] = self.app.defaults["geometry_cnctooldia"]
# try:
# self.options["cnctooldia"] = [
# float(eval(dia)) for dia in str(self.app.defaults["geometry_cnctooldia"]).split(",")
# ]
# except Exception as e:
# log.error("At least one tool diameter needed. Verify in Edit -> Preferences -> Geometry General -> "
# "Tool dia. %s" % str(e))
# return
self.options["startz"] = self.app.defaults["geometry_startz"]
@ -3143,14 +3131,14 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
self.ui.geo_tools_table.setCellWidget(row_no, 3, type_item)
self.ui.geo_tools_table.setCellWidget(row_no, 4, tool_type_item)
# ## REMEMBER: THIS COLUMN IS HIDDEN IN OBJECTUI.PY # ##
# ## REMEMBER: THIS COLUMN IS HIDDEN IN OBJECTUI.PY ###
self.ui.geo_tools_table.setItem(row_no, 5, tool_uid_item) # Tool unique ID
self.ui.geo_tools_table.setCellWidget(row_no, 6, plot_item)
try:
self.ui.tool_offset_entry.set_value(tooluid_value['offset_value'])
except Exception as e:
log.debug("build_ui() --> Could not set the 'offset_value' key in self.tools")
log.debug("build_ui() --> Could not set the 'offset_value' key in self.tools. Error: %s" % str(e))
# make the diameter column editable
for row in range(tool_idx):
@ -3436,31 +3424,14 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
self.ui.tool_offset_entry.get_value().replace(',', '.')
)
except ValueError:
self.app.inform.emit(_(
"[ERROR_NOTCL] Wrong value format entered, "
"use a number."
)
)
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
"use a number."))
return
def ui_connect(self):
# on any change to the widgets that matter it will be called self.gui_form_to_storage which will save the
# changes in geometry UI
for i in range(self.ui.grid3.count()):
# try:
# # works for CheckBoxes
# self.ui.grid3.itemAt(i).widget().stateChanged.connect(self.gui_form_to_storage)
# except Exception as e:
# # works for ComboBoxes
# try:
# self.ui.grid3.itemAt(i).widget().currentIndexChanged.connect(self.gui_form_to_storage)
# except Exception as e2:
# # works for Entry
# try:
# self.ui.grid3.itemAt(i).widget().editingFinished.connect(self.gui_form_to_storage)
# except Exception as e3:
# pass
current_widget = self.ui.grid3.itemAt(i).widget()
if isinstance(current_widget, FCCheckBox):
current_widget.stateChanged.connect(self.gui_form_to_storage)
@ -3494,20 +3465,6 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
# on any change to the widgets that matter it will be called self.gui_form_to_storage which will save the
# changes in geometry UI
for i in range(self.ui.grid3.count()):
# try:
# # works for CheckBoxes
# self.ui.grid3.itemAt(i).widget().stateChanged.disconnect(self.gui_form_to_storage)
# except Exception as e:
# # works for ComboBoxes
# try:
# self.ui.grid3.itemAt(i).widget().currentIndexChanged.disconnect(self.gui_form_to_storage)
# except Exception as e2:
# # works for Entry
# try:
# self.ui.grid3.itemAt(i).widget().editingFinished.disconnect(self.gui_form_to_storage)
# except Exception as e3:
# pass
current_widget = self.ui.grid3.itemAt(i).widget()
if isinstance(current_widget, FCCheckBox):
try:
@ -3673,14 +3630,10 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
self.ser_attrs.append('tools')
if change_message is False:
self.app.inform.emit(_(
"[success] Tool added in Tool Table."
))
self.app.inform.emit(_("[success] Tool added in Tool Table."))
else:
change_message = False
self.app.inform.emit(_(
"[WARNING_NOTCL] Default Tool added. Wrong value format entered."
))
self.app.inform.emit(_("[WARNING_NOTCL] Default Tool added. Wrong value format entered."))
self.build_ui()
# if there is no tool left in the Tools Table, enable the parameters GUI
@ -3712,9 +3665,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
max_uid += 1
self.tools[int(max_uid)] = deepcopy(self.tools[tooluid_copy])
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:
@ -3722,9 +3673,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
# 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:
@ -3746,14 +3695,12 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
# we do this HACK to make sure the tools attribute to be serialized is updated in the self.ser_attrs list
try:
self.ser_attrs.remove('tools')
except Exception as e:
except ValueError:
pass
self.ser_attrs.append('tools')
self.build_ui()
self.app.inform.emit(_(
"[success] Tool was copied in Tool Table."
))
self.app.inform.emit(_("[success] Tool was copied in Tool Table."))
def on_tool_edit(self, current_item):
@ -3767,10 +3714,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
try:
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, "
"use a number."
))
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
"use a number."))
return
tool_dia = float('%.4f' % d)
@ -3781,12 +3726,10 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
try:
self.ser_attrs.remove('tools')
self.ser_attrs.append('tools')
except TypeError:
except (TypeError, ValueError):
pass
self.app.inform.emit(_(
"[success] Tool was edited in Tool Table."
))
self.app.inform.emit(_("[success] Tool was edited in Tool Table."))
self.build_ui()
def on_tool_delete(self, all=None):
@ -3895,7 +3838,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
# populate the form with the data from the tool associated with the row parameter
try:
item = self.ui.geo_tools_table.item(current_row, 5)
if item is not None:
if type(item) is not None:
tooluid = int(item.text())
else:
return
@ -3913,7 +3856,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
else:
return
except Exception as e:
log.debug("Tool missing. Add a tool in Geo Tool Table. %s" % str(e))
log.debug("Tool missing in ui_update_v_shape(). Add a tool in Geo Tool Table. %s" % str(e))
return
try:
@ -3958,10 +3901,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
try:
vdia = float(self.ui.tipdia_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit(_(
"[ERROR_NOTCL] Wrong value format entered, "
"use a number."
))
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
"use a number."))
return
try:
@ -3971,10 +3912,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
try:
half_vangle = float(self.ui.tipangle_entry.get_value().replace(',', '.')) / 2
except ValueError:
self.app.inform.emit(_(
"[ERROR_NOTCL] Wrong value format entered, "
"use a number."
))
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
"use a number."))
return
row = self.ui.geo_tools_table.currentRow()
@ -4091,10 +4030,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
try:
offset_value_item = float(self.ui.tool_offset_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit(_(
"[ERROR_NOTCL] Wrong value format entered, "
"use a number."
))
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
"use a number."))
return
# this new dict will hold the actual useful data, another dict that is the value of key 'data'
@ -4212,6 +4149,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
else:
for x in self.ui.geo_tools_table.selectedItems():
r = []
txt = ''
# the last 2 columns for single-geo geometry are irrelevant and create problems reading
# so we don't read them
for column in range(0, self.ui.geo_tools_table.columnCount() - 2):
@ -4277,8 +4216,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."
self.app.inform.emit(_("[WARNING_NOTCL] This Geometry can't be processed because it is %s geometry."
) % str(self.special_group))
return
except AttributeError:
@ -4294,10 +4232,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
try:
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, "
"use a number."
))
self.app.inform.emit(_("[ERROR_NOTCL] Wrong Tool Dia value format entered, "
"use a number."))
return
tooluid = int(self.ui.geo_tools_table.item(x.row(), 5).text())
@ -4321,9 +4257,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
self.ui.geo_tools_table.clearSelection()
else:
self.app.inform.emit(_(
"[ERROR_NOTCL] Failed. No tool selected in the tool table ..."
))
self.app.inform.emit(_("[ERROR_NOTCL] Failed. No tool selected in the tool table ..."))
def mtool_gen_cncjob(self, segx=None, segy=None, use_thread=True):
"""
@ -4359,7 +4293,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
ymax = self.options['ymax']
except Exception as e:
log.debug("FlatCAMObj.FlatCAMGeometry.mtool_gen_cncjob() --> %s\n" % str(e))
msg = _("[ERROR] An internal error has ocurred. See shell.\n")
msg = _("[ERROR] An internal error has occurred. See shell.\n")
msg += _('FlatCAMObj.FlatCAMGeometry.mtool_gen_cncjob() --> %s') % str(e)
msg += traceback.format_exc()
self.app.inform.emit(msg)
@ -4369,7 +4303,6 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
# RUNNING ON SEPARATE THREAD!
def job_init_single_geometry(job_obj, app_obj):
log.debug("Creating a CNCJob out of a single-geometry")
assert isinstance(job_obj, FlatCAMCNCjob), \
"Initializer expected a FlatCAMCNCjob, got %s" % type(job_obj)
@ -4395,10 +4328,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
try:
job_obj.z_pdepth = float(self.options["z_pdepth"].replace(',', '.'))
except ValueError:
self.app.inform.emit(
_(
'[ERROR_NOTCL] Wrong value format for self.defaults["z_pdepth"] or self.options["z_pdepth"]'
))
self.app.inform.emit(_('[ERROR_NOTCL] Wrong value format for self.defaults["z_pdepth"] '
'or self.options["z_pdepth"]'))
try:
job_obj.feedrate_probe = float(self.options["feedrate_probe"])
@ -4407,11 +4338,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
try:
job_obj.feedrate_rapid = float(self.options["feedrate_probe"].replace(',', '.'))
except ValueError:
self.app.inform.emit(
_(
'[ERROR_NOTCL] Wrong value format for self.defaults["feedrate_probe"] '
'or self.options["feedrate_probe"]'
))
self.app.inform.emit(_('[ERROR_NOTCL] Wrong value format for self.defaults["feedrate_probe"] '
'or self.options["feedrate_probe"]'))
for tooluid_key in self.sel_tools:
tool_cnt += 1
@ -4507,20 +4435,15 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
try:
offset_value = float(self.ui.tool_offset_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit(_(
"[ERROR_NOTCL] Wrong value format entered, "
"use a number."
))
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
"use a number."))
return
if offset_value:
tool_offset = float(offset_value)
else:
self.app.inform.emit(
_(
"[WARNING] Tool Offset is selected in Tool Table but no value is provided.\n"
"Add a Tool Offset or change the Offset Type."
)
)
self.app.inform.emit(_("[WARNING] Tool Offset is selected in Tool Table but "
"no value is provided.\n"
"Add a Tool Offset or change the Offset Type."))
return
dia_cnc_dict.update({
'offset_value': tool_offset
@ -4543,7 +4466,9 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
app_obj.progress.emit(40)
tol = float(self.app.defaults['global_tolerance'])
# it seems that the tolerance needs to be a lot lower value than 0.01 and it was hardcoded initially
# to a value of 0.0005 which is 20 times less than 0.01
tol = float(self.app.defaults['global_tolerance']) / 20
res = job_obj.generate_from_geometry_2(
self, tooldia=tooldia_val, offset=tool_offset, tolerance=tol,
z_cut=z_cut, z_move=z_move,
@ -4582,7 +4507,6 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
# RUNNING ON SEPARATE THREAD!
def job_init_multi_geometry(job_obj, app_obj):
log.debug("Creating a CNCJob out of a multi-geometry")
assert isinstance(job_obj, FlatCAMCNCjob), \
"Initializer expected a FlatCAMCNCjob, got %s" % type(job_obj)
@ -4610,10 +4534,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
try:
job_obj.z_pdepth = float(self.options["z_pdepth"].replace(',', '.'))
except ValueError:
self.app.inform.emit(
_(
'[ERROR_NOTCL] Wrong value format for self.defaults["z_pdepth"] or self.options["z_pdepth"]'
))
self.app.inform.emit(_('[ERROR_NOTCL] Wrong value format for self.defaults["z_pdepth"] '
'or self.options["z_pdepth"]'))
try:
job_obj.feedrate_probe = float(self.options["feedrate_probe"])
@ -4622,11 +4544,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
try:
job_obj.feedrate_rapid = float(self.options["feedrate_probe"].replace(',', '.'))
except ValueError:
self.app.inform.emit(
_(
'[ERROR_NOTCL] Wrong value format for self.defaults["feedrate_probe"] '
'or self.options["feedrate_probe"]'
))
self.app.inform.emit(_('[ERROR_NOTCL] Wrong value format for self.defaults["feedrate_probe"] '
'or self.options["feedrate_probe"]'))
# make sure that trying to make a CNCJob from an empty file is not creating an app crash
if not self.solid_geometry:
@ -4635,9 +4554,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
if self.tools[tooluid_key]['solid_geometry'] is None:
a += 1
if a == len(self.tools):
self.app.inform.emit(_(
'[ERROR_NOTCL] Cancelled. Empty file, it has no geometry...'
))
self.app.inform.emit(_('[ERROR_NOTCL] Cancelled. Empty file, it has no geometry...'))
return 'fail'
for tooluid_key in self.sel_tools:
@ -4750,9 +4667,9 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
if offset_value:
tool_offset = float(offset_value)
else:
self.app.inform.emit(_(
"[WARNING] Tool Offset is selected in Tool Table but no value is provided.\n"
"Add a Tool Offset or change the Offset Type."))
self.app.inform.emit(_("[WARNING] Tool Offset is selected in Tool Table but "
"no value is provided.\n"
"Add a Tool Offset or change the Offset Type."))
return
dia_cnc_dict.update({
'offset_value': tool_offset
@ -4769,9 +4686,11 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
app_obj.progress.emit(40)
spindledir = self.app.defaults['geometry_spindledir']
tool_solid_geometry = self.tools[current_uid]['solid_geometry']
tol = float(self.app.defaults['global_tolerance'])
# it seems that the tolerance needs to be a lot lower value than 0.01 and it was hardcoded initially
# to a value of 0.0005 which is 20 times less than 0.01
tol = float(self.app.defaults['global_tolerance']) / 20
res = job_obj.generate_from_multitool_geometry(
tool_solid_geometry, tooldia=tooldia_val, offset=tool_offset,
tolerance=tol, z_cut=z_cut, z_move=z_move,
@ -4921,10 +4840,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
try:
job_obj.z_pdepth = float(self.options["z_pdepth"].replace(',', '.'))
except ValueError:
self.app.inform.emit(
_(
'[ERROR_NOTCL] Wrong value format for self.defaults["z_pdepth"] or self.options["z_pdepth"]'
))
self.app.inform.emit(_('[ERROR_NOTCL] Wrong value format for self.defaults["z_pdepth"] '
'or self.options["z_pdepth"]'))
try:
job_obj.feedrate_probe = float(self.options["feedrate_probe"])
@ -4933,18 +4850,17 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
try:
job_obj.feedrate_rapid = float(self.options["feedrate_probe"].replace(',', '.'))
except ValueError:
self.app.inform.emit(
_(
'[ERROR_NOTCL] Wrong value format for self.defaults["feedrate_probe"] '
'or self.options["feedrate_probe"]'
))
self.app.inform.emit(_('[ERROR_NOTCL] Wrong value format for self.defaults["feedrate_probe"] '
'or self.options["feedrate_probe"]'))
job_obj.options['xmin'] = self.options['xmin']
job_obj.options['ymin'] = self.options['ymin']
job_obj.options['xmax'] = self.options['xmax']
job_obj.options['ymax'] = self.options['ymax']
tol = float(self.app.defaults['global_tolerance'])
# it seems that the tolerance needs to be a lot lower value than 0.01 and it was hardcoded initially
# to a value of 0.0005 which is 20 times less than 0.01
tol = float(self.app.defaults['global_tolerance']) / 20
job_obj.generate_from_geometry_2(self, tooldia=tooldia, offset=offset, tolerance=tol,
z_cut=z_cut, z_move=z_move,
feedrate=feedrate, feedrate_z=feedrate_z, feedrate_rapid=feedrate_rapid,
@ -5887,12 +5803,15 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
with open(filename, 'w') as f:
for line in lines:
f.write(line)
except FileNotFoundError:
self.app.inform.emit(_(
"[WARNING_NOTCL] No such file or directory"
))
return
except PermissionError:
self.app.inform.emit(_("[WARNING] Permission denied, saving not possible.\n"
"Most likely another app is holding the file open and not accessible."))
return 'fail'
elif to_file is False:
# Just for adding it to the recent files list.
if self.app.defaults["global_open_style"] is False:

View File

@ -9,6 +9,26 @@ CAD program, and create G-Code for Isolation routing.
=================================================
10.08.2019
- added new feature in NCC Tool: now another object can be used as reference for the area extent to be cleared of copper
- fixed issue in the latest feature in NCC Tool: now it works also with reference objects made out of LineStrings (tool 'Path' in Geometry Editor)
- translation files updated for the new strings (Google Translate)
- RELEASE 8.93
9.08.2019
- added Exception handing for the case when the user is trying to save & overwrite a file already opened in another file
- finished added 'Area' type of Paint in Paint Tool
- fixed bug that created a choppy geometry for CNCJob when working in INCH
- fixed bug that did not asked the user to save the preferences after importing a new set of preferences, after the user is trying to close the Preferences tab window
7.08.2019
- replaced setFixedWidth calls with setMinimumWidth
- recoded the camlib.Geometry.isolation_geometry() function
- started to work on Paint Area in Paint Tool
6.08.2019
- fixed bug that crashed the app after creating a new geometry, if a new object is loaded and the new geometry is deleted and then trying to select the just loaded new object
@ -20,7 +40,7 @@ CAD program, and create G-Code for Isolation routing.
5.08.2019
- made sure that if using an negative Gerber isolation diameter, the resulting Geometry object will use a tool with positive diameter
- fixed bug that when isolating a Gerber file made out of a single polygon, an Recurrsion Exception was issued together with inability to create tbe isolation
- fixed bug that when isolating a Gerber file made out of a single polygon, an RecursionException was issued together with inability to create tbe isolation
- when applying a new language if there are any changes in the current project, the app will offer to save the project before the reboot
3.08.2019

View File

@ -555,19 +555,21 @@ class Geometry(object):
geo_iso = self.follow_geometry
else:
if corner is None:
if type(self.solid_geometry) is list and len(self.solid_geometry) == 1:
geo_iso = self.solid_geometry[0].buffer(offset, int(int(self.geo_steps_per_circle) / 4))
else:
try:
__ = iter(self.solid_geometry)
for el in self.solid_geometry:
geo_iso.append(el.buffer(offset, int(int(self.geo_steps_per_circle) / 4)))
except TypeError:
geo_iso = self.solid_geometry.buffer(offset, int(int(self.geo_steps_per_circle) / 4))
else:
if type(self.solid_geometry) is list and len(self.solid_geometry) == 1:
geo_iso = self.solid_geometry.buffer[0](offset, int(int(self.geo_steps_per_circle) / 4),
join_style=corner)
else:
try:
__ = iter(self.solid_geometry)
for el in self.solid_geometry:
geo_iso.append(el.buffer(offset, int(int(self.geo_steps_per_circle) / 4),
join_style=corner))
except TypeError:
geo_iso = self.solid_geometry.buffer(offset, int(int(self.geo_steps_per_circle) / 4),
join_style=corner)
# end of replaced block
if follow:
@ -5751,11 +5753,11 @@ class CNCjob(Geometry):
if pt != geo.coords[0] and pt == geo.coords[-1]:
geo.coords = list(geo.coords)[::-1]
#---------- Single depth/pass --------
# ---------- Single depth/pass --------
if not multidepth:
self.gcode += self.create_gcode_single_pass(geo, extracut, tolerance)
#--------- Multi-pass ---------
# --------- Multi-pass ---------
else:
self.gcode += self.create_gcode_multi_pass(geo, extracut, tolerance,
postproc=p, current_point=current_pt)
@ -5850,8 +5852,8 @@ class CNCjob(Geometry):
# 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 "
"for the current_geometry.\n"
"Raise the value (in module) and try again."))
"for the current_geometry.\n"
"Raise the value (in module) and try again."))
return 'fail'
# hack: make offset smaller by 0.0000000001 which is insignificant difference but allow the job
# to continue
@ -5899,7 +5901,7 @@ class CNCjob(Geometry):
self.xy_toolchange = [float(eval(a)) for a in toolchangexy.split(",")]
if len(self.xy_toolchange) < 2:
self.app.inform.emit(_("[ERROR]The Toolchange X,Y field in Edit -> Preferences has to be "
"in the format (x, y) \nbut now there is only one value, not two. "))
"in the format (x, y) \nbut now there is only one value, not two. "))
return 'fail'
except Exception as e:
log.debug("camlib.CNCJob.generate_from_geometry_2() --> %s" % str(e))
@ -5910,19 +5912,19 @@ class CNCjob(Geometry):
if self.z_cut is None:
self.app.inform.emit(_("[ERROR_NOTCL] Cut_Z parameter is None or zero. Most likely a bad combinations of "
"other parameters."))
"other parameters."))
return 'fail'
if self.z_cut > 0:
self.app.inform.emit(_("[WARNING] The Cut Z parameter has positive value. "
"It is the depth value to cut into material.\n"
"The Cut Z parameter needs to have a negative value, assuming it is a typo "
"therefore the app will convert the value to negative."
"Check the resulting CNC code (Gcode etc)."))
"It is the depth value to cut into material.\n"
"The Cut Z parameter needs to have a negative value, assuming it is a typo "
"therefore the app will convert the value to negative."
"Check the resulting CNC code (Gcode etc)."))
self.z_cut = -self.z_cut
elif self.z_cut == 0:
self.app.inform.emit(_("[WARNING] The Cut Z parameter is zero. "
"There will be no cut, skipping %s file") % geometry.options['name'])
"There will be no cut, skipping %s file") % geometry.options['name'])
return 'fail'
if self.z_move is None:
@ -5931,14 +5933,14 @@ class CNCjob(Geometry):
if self.z_move < 0:
self.app.inform.emit(_("[WARNING] The Travel Z parameter has negative value. "
"It is the height value to travel between cuts.\n"
"The Z Travel parameter needs to have a positive value, assuming it is a typo "
"therefore the app will convert the value to positive."
"Check the resulting CNC code (Gcode etc)."))
"It is the height value to travel between cuts.\n"
"The Z Travel parameter needs to have a positive value, assuming it is a typo "
"therefore the app will convert the value to positive."
"Check the resulting CNC code (Gcode etc)."))
self.z_move = -self.z_move
elif self.z_move == 0:
self.app.inform.emit(_("[WARNING] The Z Travel parameter is zero. "
"This is dangerous, skipping %s file") % self.options['name'])
"This is dangerous, skipping %s file") % self.options['name'])
return 'fail'
# ## Index first and last points in paths
@ -6015,11 +6017,11 @@ class CNCjob(Geometry):
if pt != geo.coords[0] and pt == geo.coords[-1]:
geo.coords = list(geo.coords)[::-1]
#---------- Single depth/pass --------
# ---------- Single depth/pass --------
if not multidepth:
self.gcode += self.create_gcode_single_pass(geo, extracut, tolerance)
#--------- Multi-pass ---------
# --------- Multi-pass ---------
else:
self.gcode += self.create_gcode_multi_pass(geo, extracut, tolerance,
postproc=p, current_point=current_pt)
@ -6240,7 +6242,8 @@ class CNCjob(Geometry):
gcode_multi_pass += self.linear2gcode(geometry, tolerance=tolerance, z_cut=depth, up=False)
else:
if geometry.is_ring:
gcode_multi_pass += self.linear2gcode_extra(geometry, tolerance=tolerance, z_cut=depth, up=False)
gcode_multi_pass += self.linear2gcode_extra(geometry, tolerance=tolerance, z_cut=depth,
up=False)
else:
gcode_multi_pass += self.linear2gcode(geometry, tolerance=tolerance, z_cut=depth, up=False)
@ -6414,7 +6417,6 @@ class CNCjob(Geometry):
current['G'] = int(gobj['G'])
if 'X' in gobj or 'Y' in gobj:
# TODO: I think there is a problem here, current['X] (and the rest of current[...] are not initialized
if 'X' in gobj:
x = gobj['X']
# current['X'] = x
@ -6505,6 +6507,9 @@ class CNCjob(Geometry):
:param color: Color specification.
:param alpha: Transparency specification.
:param tool_tolerance: Tolerance when drawing the toolshape.
:param obj
:param visible
:param kind
:return: None
"""
# units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper()
@ -6556,7 +6561,7 @@ class CNCjob(Geometry):
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)
visible=visible, layer=1 if geo['kind'][0] == 'C' else 2)
elif kind == 'travel':
if geo['kind'][0] == 'T':
obj.add_shape(shape=poly, color=color['T'][1], face_color=color['T'][0],

View File

@ -641,16 +641,16 @@ class TransformEditorTool(FlatCAMTool):
self.transform_lay.addWidget(title_label)
self.empty_label = QtWidgets.QLabel("")
self.empty_label.setFixedWidth(50)
self.empty_label.setMinimumWidth(50)
self.empty_label1 = QtWidgets.QLabel("")
self.empty_label1.setFixedWidth(70)
self.empty_label1.setMinimumWidth(70)
self.empty_label2 = QtWidgets.QLabel("")
self.empty_label2.setFixedWidth(70)
self.empty_label2.setMinimumWidth(70)
self.empty_label3 = QtWidgets.QLabel("")
self.empty_label3.setFixedWidth(70)
self.empty_label3.setMinimumWidth(70)
self.empty_label4 = QtWidgets.QLabel("")
self.empty_label4.setFixedWidth(70)
self.empty_label4.setMinimumWidth(70)
self.transform_lay.addWidget(self.empty_label)
# Rotate Title

View File

@ -2604,7 +2604,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.pad_array_size_label.setToolTip(
_("Specify how many pads to be in the array.")
)
self.pad_array_size_label.setFixedWidth(100)
self.pad_array_size_label.setMinimumWidth(100)
self.pad_array_size_entry = LengthEntry()
self.array_form.addRow(self.pad_array_size_label, self.pad_array_size_entry)
@ -2626,7 +2626,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
"- 'Y' - vertical axis or \n"
"- 'Angle' - a custom angle for the array inclination")
)
self.pad_axis_label.setFixedWidth(100)
self.pad_axis_label.setMinimumWidth(100)
self.pad_axis_radio = RadioSet([{'label': _('X'), 'value': 'X'},
{'label': _('Y'), 'value': 'Y'},
@ -2638,7 +2638,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.pad_pitch_label.setToolTip(
_("Pitch = Distance between elements of the array.")
)
self.pad_pitch_label.setFixedWidth(100)
self.pad_pitch_label.setMinimumWidth(100)
self.pad_pitch_entry = LengthEntry()
self.linear_form.addRow(self.pad_pitch_label, self.pad_pitch_entry)
@ -2650,7 +2650,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
"Min value is: -359.99 degrees.\n"
"Max value is: 360.00 degrees.")
)
self.linear_angle_label.setFixedWidth(100)
self.linear_angle_label.setMinimumWidth(100)
self.linear_angle_spinner = FCDoubleSpinner()
self.linear_angle_spinner.set_precision(2)
@ -2669,7 +2669,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
_("Direction for circular array."
"Can be CW = clockwise or CCW = counter clockwise.")
)
self.pad_direction_label.setFixedWidth(100)
self.pad_direction_label.setMinimumWidth(100)
self.circular_form = QtWidgets.QFormLayout()
self.circular_box.addLayout(self.circular_form)
@ -2683,7 +2683,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.pad_angle_label.setToolTip(
_("Angle at which each element in circular array is placed.")
)
self.pad_angle_label.setFixedWidth(100)
self.pad_angle_label.setMinimumWidth(100)
self.pad_angle_entry = LengthEntry()
self.circular_form.addRow(self.pad_angle_label, self.pad_angle_entry)
@ -4719,16 +4719,16 @@ class TransformEditorTool(FlatCAMTool):
self.transform_lay.addWidget(title_label)
self.empty_label = QtWidgets.QLabel("")
self.empty_label.setFixedWidth(50)
self.empty_label.setMinimumWidth(50)
self.empty_label1 = QtWidgets.QLabel("")
self.empty_label1.setFixedWidth(70)
self.empty_label1.setMinimumWidth(70)
self.empty_label2 = QtWidgets.QLabel("")
self.empty_label2.setFixedWidth(70)
self.empty_label2.setMinimumWidth(70)
self.empty_label3 = QtWidgets.QLabel("")
self.empty_label3.setFixedWidth(70)
self.empty_label3.setMinimumWidth(70)
self.empty_label4 = QtWidgets.QLabel("")
self.empty_label4.setFixedWidth(70)
self.empty_label4.setMinimumWidth(70)
self.transform_lay.addWidget(self.empty_label)
# Rotate Title
@ -4747,7 +4747,7 @@ class TransformEditorTool(FlatCAMTool):
"Positive numbers for CW motion.\n"
"Negative numbers for CCW motion.")
)
self.rotate_label.setFixedWidth(50)
self.rotate_label.setMinimumWidth(50)
self.rotate_entry = FCEntry()
# self.rotate_entry.setFixedWidth(60)
@ -4760,7 +4760,7 @@ class TransformEditorTool(FlatCAMTool):
"The point of reference is the middle of\n"
"the bounding box for all selected shapes.")
)
self.rotate_button.setFixedWidth(60)
self.rotate_button.setMinimumWidth(60)
form_child.addWidget(self.rotate_entry)
form_child.addWidget(self.rotate_button)
@ -4784,7 +4784,7 @@ class TransformEditorTool(FlatCAMTool):
_("Angle for Skew action, in degrees.\n"
"Float number between -360 and 359.")
)
self.skewx_label.setFixedWidth(50)
self.skewx_label.setMinimumWidth(50)
self.skewx_entry = FCEntry()
self.skewx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
# self.skewx_entry.setFixedWidth(60)
@ -4795,14 +4795,14 @@ class TransformEditorTool(FlatCAMTool):
_("Skew/shear the selected shape(s).\n"
"The point of reference is the middle of\n"
"the bounding box for all selected shapes."))
self.skewx_button.setFixedWidth(60)
self.skewx_button.setMinimumWidth(60)
self.skewy_label = QtWidgets.QLabel(_("Angle Y:"))
self.skewy_label.setToolTip(
_("Angle for Skew action, in degrees.\n"
"Float number between -360 and 359.")
)
self.skewy_label.setFixedWidth(50)
self.skewy_label.setMinimumWidth(50)
self.skewy_entry = FCEntry()
self.skewy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
# self.skewy_entry.setFixedWidth(60)
@ -4813,7 +4813,7 @@ class TransformEditorTool(FlatCAMTool):
_("Skew/shear the selected shape(s).\n"
"The point of reference is the middle of\n"
"the bounding box for all selected shapes."))
self.skewy_button.setFixedWidth(60)
self.skewy_button.setMinimumWidth(60)
form1_child_1.addWidget(self.skewx_entry)
form1_child_1.addWidget(self.skewx_button)
@ -4840,7 +4840,7 @@ class TransformEditorTool(FlatCAMTool):
self.scalex_label.setToolTip(
_("Factor for Scale action over X axis.")
)
self.scalex_label.setFixedWidth(50)
self.scalex_label.setMinimumWidth(50)
self.scalex_entry = FCEntry()
self.scalex_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
# self.scalex_entry.setFixedWidth(60)
@ -4851,13 +4851,13 @@ class TransformEditorTool(FlatCAMTool):
_("Scale the selected shape(s).\n"
"The point of reference depends on \n"
"the Scale reference checkbox state."))
self.scalex_button.setFixedWidth(60)
self.scalex_button.setMinimumWidth(60)
self.scaley_label = QtWidgets.QLabel(_("Factor Y:"))
self.scaley_label.setToolTip(
_("Factor for Scale action over Y axis.")
)
self.scaley_label.setFixedWidth(50)
self.scaley_label.setMinimumWidth(50)
self.scaley_entry = FCEntry()
self.scaley_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
# self.scaley_entry.setFixedWidth(60)
@ -4868,7 +4868,7 @@ class TransformEditorTool(FlatCAMTool):
_("Scale the selected shape(s).\n"
"The point of reference depends on \n"
"the Scale reference checkbox state."))
self.scaley_button.setFixedWidth(60)
self.scaley_button.setMinimumWidth(60)
self.scale_link_cb = FCCheckBox()
self.scale_link_cb.set_value(True)
@ -4876,7 +4876,7 @@ class TransformEditorTool(FlatCAMTool):
self.scale_link_cb.setToolTip(
_("Scale the selected shape(s)\n"
"using the Scale Factor X for both axis."))
self.scale_link_cb.setFixedWidth(50)
self.scale_link_cb.setMinimumWidth(50)
self.scale_zero_ref_cb = FCCheckBox()
self.scale_zero_ref_cb.set_value(True)
@ -4915,7 +4915,7 @@ class TransformEditorTool(FlatCAMTool):
self.offx_label.setToolTip(
_("Value for Offset action on X axis.")
)
self.offx_label.setFixedWidth(50)
self.offx_label.setMinimumWidth(50)
self.offx_entry = FCEntry()
self.offx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
# self.offx_entry.setFixedWidth(60)
@ -4927,13 +4927,13 @@ class TransformEditorTool(FlatCAMTool):
"The point of reference is the middle of\n"
"the bounding box for all selected shapes.\n")
)
self.offx_button.setFixedWidth(60)
self.offx_button.setMinimumWidth(60)
self.offy_label = QtWidgets.QLabel(_("Value Y:"))
self.offy_label.setToolTip(
_("Value for Offset action on Y axis.")
)
self.offy_label.setFixedWidth(50)
self.offy_label.setMinimumWidth(50)
self.offy_entry = FCEntry()
self.offy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
# self.offy_entry.setFixedWidth(60)
@ -4945,7 +4945,7 @@ class TransformEditorTool(FlatCAMTool):
"The point of reference is the middle of\n"
"the bounding box for all selected shapes.\n")
)
self.offy_button.setFixedWidth(60)
self.offy_button.setMinimumWidth(60)
form3_child_1.addWidget(self.offx_entry)
form3_child_1.addWidget(self.offx_button)
@ -4975,7 +4975,7 @@ class TransformEditorTool(FlatCAMTool):
_("Flip the selected shape(s) over the X axis.\n"
"Does not create a new shape.")
)
self.flipx_button.setFixedWidth(60)
self.flipx_button.setMinimumWidth(60)
self.flipy_button = FCButton()
self.flipy_button.set_value(_("Flip on Y"))
@ -4983,7 +4983,7 @@ class TransformEditorTool(FlatCAMTool):
_("Flip the selected shape(s) over the X axis.\n"
"Does not create a new shape.")
)
self.flipy_button.setFixedWidth(60)
self.flipy_button.setMinimumWidth(60)
self.flip_ref_cb = FCCheckBox()
self.flip_ref_cb.set_value(True)
@ -4999,7 +4999,7 @@ class TransformEditorTool(FlatCAMTool):
"Or enter the coords in format (x, y) in the\n"
"Point Entry field and click Flip on X(Y)")
)
self.flip_ref_cb.setFixedWidth(50)
self.flip_ref_cb.setMinimumWidth(50)
self.flip_ref_label = QtWidgets.QLabel(_("Point:"))
self.flip_ref_label.setToolTip(
@ -5007,7 +5007,7 @@ class TransformEditorTool(FlatCAMTool):
"The 'x' in (x, y) will be used when using Flip on X and\n"
"the 'y' in (x, y) will be used when using Flip on Y.")
)
self.flip_ref_label.setFixedWidth(50)
self.flip_ref_label.setMinimumWidth(50)
self.flip_ref_entry = EvalEntry2("(0, 0)")
self.flip_ref_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
# self.flip_ref_entry.setFixedWidth(60)
@ -5019,7 +5019,7 @@ class TransformEditorTool(FlatCAMTool):
"left click on canvas together with pressing\n"
"SHIFT key. Then click Add button to insert.")
)
self.flip_ref_button.setFixedWidth(60)
self.flip_ref_button.setMinimumWidth(60)
form4_child_hlay.addStretch()
form4_child_hlay.addWidget(self.flipx_button)

View File

@ -1656,12 +1656,12 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
self.buttonPreview = QtWidgets.QPushButton(_('Print Preview'))
self.buttonPrint = QtWidgets.QPushButton(_('Print Code'))
self.buttonFind = QtWidgets.QPushButton(_('Find in Code'))
self.buttonFind.setFixedWidth(100)
self.buttonPreview.setFixedWidth(100)
self.buttonFind.setMinimumWidth(100)
self.buttonPreview.setMinimumWidth(100)
self.entryFind = FCEntry()
self.entryFind.setMaximumWidth(200)
self.buttonReplace = QtWidgets.QPushButton(_('Replace With'))
self.buttonReplace.setFixedWidth(100)
self.buttonReplace.setMinimumWidth(100)
self.entryReplace = FCEntry()
self.entryReplace.setMaximumWidth(200)
self.sel_all_cb = QtWidgets.QCheckBox(_('All'))
@ -3255,9 +3255,9 @@ class ToolsPreferencesUI(QtWidgets.QWidget):
self.setLayout(self.layout)
self.tools_ncc_group = ToolsNCCPrefGroupUI()
self.tools_ncc_group.setMinimumWidth(200)
self.tools_ncc_group.setMinimumWidth(220)
self.tools_paint_group = ToolsPaintPrefGroupUI()
self.tools_paint_group.setMinimumWidth(200)
self.tools_paint_group.setMinimumWidth(220)
self.tools_cutout_group = ToolsCutoutPrefGroupUI()
self.tools_cutout_group.setMinimumWidth(220)
@ -3417,7 +3417,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
self.pf_color_alpha_slider.setSingleStep(1)
self.pf_color_alpha_spinner = FCSpinner()
self.pf_color_alpha_spinner.setFixedWidth(70)
self.pf_color_alpha_spinner.setMinimumWidth(70)
self.pf_color_alpha_spinner.setMinimum(0)
self.pf_color_alpha_spinner.setMaximum(255)
@ -3467,7 +3467,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
self.sf_color_alpha_slider.setSingleStep(1)
self.sf_color_alpha_spinner = FCSpinner()
self.sf_color_alpha_spinner.setFixedWidth(70)
self.sf_color_alpha_spinner.setMinimumWidth(70)
self.sf_color_alpha_spinner.setMinimum(0)
self.sf_color_alpha_spinner.setMaximum(255)
@ -3517,7 +3517,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
self.alt_sf_color_alpha_slider.setSingleStep(1)
self.alt_sf_color_alpha_spinner = FCSpinner()
self.alt_sf_color_alpha_spinner.setFixedWidth(70)
self.alt_sf_color_alpha_spinner.setMinimumWidth(70)
self.alt_sf_color_alpha_spinner.setMinimum(0)
self.alt_sf_color_alpha_spinner.setMaximum(255)
@ -4291,7 +4291,7 @@ class GerberExpPrefGroupUI(OptionsGroupUI):
self.format_whole_entry = IntEntry()
self.format_whole_entry.setMaxLength(1)
self.format_whole_entry.setAlignment(QtCore.Qt.AlignRight)
self.format_whole_entry.setFixedWidth(30)
self.format_whole_entry.setMinimumWidth(30)
self.format_whole_entry.setToolTip(
_("This numbers signify the number of digits in\n"
"the whole part of Gerber coordinates.")
@ -4305,7 +4305,7 @@ class GerberExpPrefGroupUI(OptionsGroupUI):
self.format_dec_entry = IntEntry()
self.format_dec_entry.setMaxLength(1)
self.format_dec_entry.setAlignment(QtCore.Qt.AlignRight)
self.format_dec_entry.setFixedWidth(30)
self.format_dec_entry.setMinimumWidth(30)
self.format_dec_entry.setToolTip(
_("This numbers signify the number of digits in\n"
"the decimal part of Gerber coordinates.")
@ -4439,7 +4439,7 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
self.excellon_format_upper_in_entry = IntEntry()
self.excellon_format_upper_in_entry.setMaxLength(1)
self.excellon_format_upper_in_entry.setAlignment(QtCore.Qt.AlignRight)
self.excellon_format_upper_in_entry.setFixedWidth(30)
self.excellon_format_upper_in_entry.setMinimumWidth(30)
self.excellon_format_upper_in_entry.setToolTip(
_("This numbers signify the number of digits in\n"
"the whole part of Excellon coordinates.")
@ -4453,7 +4453,7 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
self.excellon_format_lower_in_entry = IntEntry()
self.excellon_format_lower_in_entry.setMaxLength(1)
self.excellon_format_lower_in_entry.setAlignment(QtCore.Qt.AlignRight)
self.excellon_format_lower_in_entry.setFixedWidth(30)
self.excellon_format_lower_in_entry.setMinimumWidth(30)
self.excellon_format_lower_in_entry.setToolTip(
_("This numbers signify the number of digits in\n"
"the decimal part of Excellon coordinates.")
@ -4472,7 +4472,7 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
self.excellon_format_upper_mm_entry = IntEntry()
self.excellon_format_upper_mm_entry.setMaxLength(1)
self.excellon_format_upper_mm_entry.setAlignment(QtCore.Qt.AlignRight)
self.excellon_format_upper_mm_entry.setFixedWidth(30)
self.excellon_format_upper_mm_entry.setMinimumWidth(30)
self.excellon_format_upper_mm_entry.setToolTip(
_("This numbers signify the number of digits in\n"
"the whole part of Excellon coordinates.")
@ -4486,7 +4486,7 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
self.excellon_format_lower_mm_entry = IntEntry()
self.excellon_format_lower_mm_entry.setMaxLength(1)
self.excellon_format_lower_mm_entry.setAlignment(QtCore.Qt.AlignRight)
self.excellon_format_lower_mm_entry.setFixedWidth(30)
self.excellon_format_lower_mm_entry.setMinimumWidth(30)
self.excellon_format_lower_mm_entry.setToolTip(
_("This numbers signify the number of digits in\n"
"the decimal part of Excellon coordinates.")
@ -4778,7 +4778,7 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI):
# Adding the Excellon Format Defaults Button
self.excellon_defaults_button = QtWidgets.QPushButton()
self.excellon_defaults_button.setText(str(_("Defaults")))
self.excellon_defaults_button.setFixedWidth(80)
self.excellon_defaults_button.setMinimumWidth(80)
grid4.addWidget(self.excellon_defaults_button, 0, 0, QtCore.Qt.AlignRight)
self.layout.addStretch()
@ -4945,7 +4945,7 @@ class ExcellonExpPrefGroupUI(OptionsGroupUI):
self.format_whole_entry = IntEntry()
self.format_whole_entry.setMaxLength(1)
self.format_whole_entry.setAlignment(QtCore.Qt.AlignRight)
self.format_whole_entry.setFixedWidth(30)
self.format_whole_entry.setMinimumWidth(30)
self.format_whole_entry.setToolTip(
_("This numbers signify the number of digits in\n"
"the whole part of Excellon coordinates.")
@ -4959,7 +4959,7 @@ class ExcellonExpPrefGroupUI(OptionsGroupUI):
self.format_dec_entry = IntEntry()
self.format_dec_entry.setMaxLength(1)
self.format_dec_entry.setAlignment(QtCore.Qt.AlignRight)
self.format_dec_entry.setFixedWidth(30)
self.format_dec_entry.setMinimumWidth(30)
self.format_dec_entry.setToolTip(
_("This numbers signify the number of digits in\n"
"the decimal part of Excellon coordinates.")
@ -5074,7 +5074,7 @@ class ExcellonEditorPrefGroupUI(OptionsGroupUI):
self.drill_array_size_label.setToolTip(
_("Specify how many drills to be in the array.")
)
# self.drill_array_size_label.setFixedWidth(100)
# self.drill_array_size_label.setMinimumWidth(100)
self.drill_array_size_entry = LengthEntry()
@ -5092,7 +5092,7 @@ class ExcellonEditorPrefGroupUI(OptionsGroupUI):
"- 'Y' - vertical axis or \n"
"- 'Angle' - a custom angle for the array inclination")
)
# self.drill_axis_label.setFixedWidth(100)
# self.drill_axis_label.setMinimumWidth(100)
self.drill_axis_radio = RadioSet([{'label': _('X'), 'value': 'X'},
{'label': _('Y'), 'value': 'Y'},
{'label': _('Angle'), 'value': 'A'}])
@ -5105,7 +5105,7 @@ class ExcellonEditorPrefGroupUI(OptionsGroupUI):
self.drill_pitch_label.setToolTip(
_("Pitch = Distance between elements of the array.")
)
# self.drill_pitch_label.setFixedWidth(100)
# self.drill_pitch_label.setMinimumWidth(100)
self.drill_pitch_entry = LengthEntry()
grid0.addWidget(self.drill_pitch_label, 5, 0)
@ -5875,6 +5875,19 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
self.ncc_rest_cb = FCCheckBox()
grid0.addWidget(self.ncc_rest_cb, 6, 1)
# ## Reference
self.reference_radio = RadioSet([{'label': _('Itself'), 'value': 'itself'},
{'label': _('Box'), 'value': 'box'}])
reference_label = QtWidgets.QLabel(_("Reference:"))
reference_label.setToolTip(
_("When choosing the 'Itself' option the non copper clearing extent\n"
"is based on the object that is copper cleared.\n "
"Choosing the 'Box' option will do non copper clearing within the box\n"
"specified by another object different than the one that is copper cleared.")
)
grid0.addWidget(reference_label, 7, 0)
grid0.addWidget(self.reference_radio, 7, 1)
self.layout.addStretch()
@ -6126,8 +6139,8 @@ class ToolsPaintPrefGroupUI(OptionsGroupUI):
grid0.addWidget(selectlabel, 6, 0)
self.selectmethod_combo = RadioSet([
{"label": _("Single"), "value": "single"},
{"label": _("All"), "value": "all"},
# {"label": "Rectangle", "value": "rectangle"}
{"label": _("Area"), "value": "area"},
{"label": _("All"), "value": "all"}
])
grid0.addWidget(self.selectmethod_combo, 6, 1)

View File

@ -1634,7 +1634,7 @@ class Dialog_box(QtWidgets.QWidget):
self.ok = False
dialog_box = QtWidgets.QInputDialog()
dialog_box.setFixedWidth(290)
dialog_box.setMinimumWidth(290)
self.setWindowIcon(icon)
self.location, self.ok = dialog_box.getText(self, title, label, text="0, 0")

View File

@ -101,7 +101,7 @@ class ObjectUI(QtWidgets.QWidget):
self.scale_button.setToolTip(
_("Perform scaling operation.")
)
self.scale_button.setFixedWidth(70)
self.scale_button.setMinimumWidth(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(70)
self.offset_button.setMinimumWidth(70)
self.offset_grid.addWidget(self.offset_button, 0, 2)
layout.addStretch()
@ -148,7 +148,7 @@ class GerberObjectUI(ObjectUI):
self.custom_box.addLayout(grid0)
self.plot_options_label = QtWidgets.QLabel(_("<b>Plot Options:</b>"))
self.plot_options_label.setFixedWidth(90)
self.plot_options_label.setMinimumWidth(90)
grid0.addWidget(self.plot_options_label, 0, 0)
@ -157,7 +157,7 @@ class GerberObjectUI(ObjectUI):
self.solid_cb.setToolTip(
_("Solid color polygons.")
)
self.solid_cb.setFixedWidth(50)
self.solid_cb.setMinimumWidth(50)
grid0.addWidget(self.solid_cb, 0, 1)
# Multicolored CB
@ -165,7 +165,7 @@ class GerberObjectUI(ObjectUI):
self.multicolored_cb.setToolTip(
_("Draw polygons in different colors.")
)
self.multicolored_cb.setFixedWidth(55)
self.multicolored_cb.setMinimumWidth(55)
grid0.addWidget(self.multicolored_cb, 0, 2)
# Plot CB
@ -173,7 +173,7 @@ class GerberObjectUI(ObjectUI):
self.plot_cb.setToolTip(
_("Plot (show) this object.")
)
self.plot_cb.setFixedWidth(59)
self.plot_cb.setMinimumWidth(59)
grid0.addWidget(self.plot_cb, 0, 3)
# ## Object name
@ -193,7 +193,7 @@ class GerberObjectUI(ObjectUI):
self.apertures_table_label.setToolTip(
_("Apertures Table for the Gerber Object.")
)
self.apertures_table_label.setFixedWidth(90)
self.apertures_table_label.setMinimumWidth(90)
hlay_plot.addWidget(self.apertures_table_label)
@ -264,7 +264,7 @@ class GerberObjectUI(ObjectUI):
"feature, use a negative value for\n"
"this parameter.")
)
tdlabel.setFixedWidth(90)
tdlabel.setMinimumWidth(90)
grid1.addWidget(tdlabel, 0, 0)
self.iso_tool_dia_entry = LengthEntry()
grid1.addWidget(self.iso_tool_dia_entry, 0, 1)
@ -274,7 +274,7 @@ class GerberObjectUI(ObjectUI):
_("Width of the isolation gap in\n"
"number (integer) of tool widths.")
)
passlabel.setFixedWidth(90)
passlabel.setMinimumWidth(90)
grid1.addWidget(passlabel, 1, 0)
self.iso_width_entry = IntEntry()
grid1.addWidget(self.iso_width_entry, 1, 1)
@ -285,7 +285,7 @@ class GerberObjectUI(ObjectUI):
"Example:\n"
"A value here of 0.25 means an overlap of 25% from the tool diameter found above.")
)
overlabel.setFixedWidth(90)
overlabel.setMinimumWidth(90)
grid1.addWidget(overlabel, 2, 0)
self.iso_overlap_entry = FloatEntry()
grid1.addWidget(self.iso_overlap_entry, 2, 1)
@ -337,7 +337,7 @@ class GerberObjectUI(ObjectUI):
self.custom_box.addLayout(hlay_1)
self.padding_area_label = QtWidgets.QLabel('')
self.padding_area_label.setFixedWidth(90)
self.padding_area_label.setMinimumWidth(90)
hlay_1.addWidget(self.padding_area_label)
self.generate_iso_button = QtWidgets.QPushButton(_('FULL Geo'))
@ -346,7 +346,7 @@ class GerberObjectUI(ObjectUI):
"for isolation routing. It contains both\n"
"the interiors and exteriors geometry.")
)
self.generate_iso_button.setFixedWidth(90)
self.generate_iso_button.setMinimumWidth(90)
hlay_1.addWidget(self.generate_iso_button, alignment=Qt.AlignLeft)
# hlay_1.addStretch()
@ -357,7 +357,7 @@ class GerberObjectUI(ObjectUI):
"for isolation routing containing\n"
"only the exteriors geometry.")
)
# self.generate_ext_iso_button.setFixedWidth(100)
# self.generate_ext_iso_button.setMinimumWidth(100)
hlay_1.addWidget(self.generate_ext_iso_button)
self.generate_int_iso_button = QtWidgets.QPushButton(_('Int Geo'))
@ -366,7 +366,7 @@ class GerberObjectUI(ObjectUI):
"for isolation routing containing\n"
"only the interiors geometry.")
)
# self.generate_ext_iso_button.setFixedWidth(90)
# self.generate_ext_iso_button.setMinimumWidth(90)
hlay_1.addWidget(self.generate_int_iso_button)
# when the follow checkbox is checked then the exteriors and interiors isolation generation buttons
@ -383,7 +383,7 @@ class GerberObjectUI(ObjectUI):
_("Create a Geometry object with\n"
"toolpaths to cut all non-copper regions.")
)
self.clearcopper_label.setFixedWidth(90)
self.clearcopper_label.setMinimumWidth(90)
grid2.addWidget(self.clearcopper_label, 0, 0)
self.generate_ncc_button = QtWidgets.QPushButton(_('NCC Tool'))
@ -431,7 +431,7 @@ class GerberObjectUI(ObjectUI):
"objects with this minimum\n"
"distance.")
)
bmlabel.setFixedWidth(90)
bmlabel.setMinimumWidth(90)
grid4.addWidget(bmlabel, 0, 0)
self.noncopper_margin_entry = LengthEntry()
grid4.addWidget(self.noncopper_margin_entry, 0, 1)
@ -441,7 +441,7 @@ class GerberObjectUI(ObjectUI):
self.noncopper_rounded_cb.setToolTip(
_("Resulting geometry will have rounded corners.")
)
self.noncopper_rounded_cb.setFixedWidth(90)
self.noncopper_rounded_cb.setMinimumWidth(90)
grid4.addWidget(self.noncopper_rounded_cb, 1, 0)
self.generate_noncopper_button = QtWidgets.QPushButton(_('Generate Geo'))
@ -463,7 +463,7 @@ class GerberObjectUI(ObjectUI):
_("Distance of the edges of the box\n"
"to the nearest polygon.")
)
bbmargin.setFixedWidth(90)
bbmargin.setMinimumWidth(90)
grid5.addWidget(bbmargin, 0, 0)
self.bbmargin_entry = LengthEntry()
grid5.addWidget(self.bbmargin_entry, 0, 1)
@ -475,7 +475,7 @@ class GerberObjectUI(ObjectUI):
"their radius is equal to\n"
"the margin.")
)
self.bbrounded_cb.setFixedWidth(90)
self.bbrounded_cb.setMinimumWidth(90)
grid5.addWidget(self.bbrounded_cb, 1, 0)
self.generate_bb_button = QtWidgets.QPushButton(_('Generate Geo'))
@ -957,7 +957,7 @@ class GeometryObjectUI(ObjectUI):
self.tool_offset_entry = FloatEntry()
self.tool_offset_entry.setValidator(QtGui.QDoubleValidator(-9999.9999, 9999.9999, 4))
spacer_lbl = QtWidgets.QLabel(" ")
spacer_lbl.setFixedWidth(80)
spacer_lbl.setMinimumWidth(80)
self.grid1.addWidget(self.tool_offset_entry, 0, 1)
self.grid1.addWidget(spacer_lbl, 0, 2)

View File

@ -269,7 +269,7 @@ class ShapeCollectionVisual(CompoundVisual):
# Add data to process pool if pool exists
try:
self.results[key] = self.pool.map_async(_update_shape_buffers, [self.data[key]])
except:
except Exception as e:
self.data[key] = _update_shape_buffers(self.data[key])
if update:

View File

@ -58,7 +58,7 @@ class CutOut(FlatCAMTool):
"What is selected here will dictate the kind\n"
"of objects that will populate the 'Object' combobox.")
)
self.type_obj_combo_label.setFixedWidth(60)
self.type_obj_combo_label.setMinimumWidth(60)
form_layout.addRow(self.type_obj_combo_label, self.type_obj_combo)
# Object to be cutout
@ -158,7 +158,7 @@ class CutOut(FlatCAMTool):
"- 2tb - 2*top + 2*bottom\n"
"- 8 - 2*left + 2*right +2*top + 2*bottom")
)
gaps_label.setFixedWidth(60)
gaps_label.setMinimumWidth(60)
self.gaps = FCComboBox()
gaps_items = ['LR', 'TB', '4', '2LR', '2TB', '8']
@ -232,7 +232,7 @@ class CutOut(FlatCAMTool):
self.man_object_label.setToolTip(
_("Geometry object used to create the manual cutout.")
)
self.man_object_label.setFixedWidth(60)
self.man_object_label.setMinimumWidth(60)
# e_lab_0 = QtWidgets.QLabel('')
form_layout_3.addRow(self.man_object_label, self.man_object_combo)

View File

@ -55,7 +55,7 @@ class DblSidedTool(FlatCAMTool):
"the specified axis. Does not create a new \n"
"object, but modifies it.")
)
self.mirror_gerber_button.setFixedWidth(60)
self.mirror_gerber_button.setMinimumWidth(60)
# grid_lay.addRow("Bottom Layer:", self.object_combo)
grid_lay.addWidget(self.botlay_label, 0, 0)
@ -79,7 +79,7 @@ class DblSidedTool(FlatCAMTool):
"the specified axis. Does not create a new \n"
"object, but modifies it.")
)
self.mirror_exc_button.setFixedWidth(60)
self.mirror_exc_button.setMinimumWidth(60)
# grid_lay.addRow("Bottom Layer:", self.object_combo)
grid_lay.addWidget(self.excobj_label, 2, 0)
@ -103,7 +103,7 @@ class DblSidedTool(FlatCAMTool):
"the specified axis. Does not create a new \n"
"object, but modifies it.")
)
self.mirror_geo_button.setFixedWidth(60)
self.mirror_geo_button.setMinimumWidth(60)
# grid_lay.addRow("Bottom Layer:", self.object_combo)
grid_lay.addWidget(self.geoobj_label, 4, 0)
@ -164,15 +164,15 @@ class DblSidedTool(FlatCAMTool):
"The (x, y) coordinates are captured by pressing SHIFT key\n"
"and left mouse button click on canvas or you can enter the coords manually.")
)
self.add_point_button.setFixedWidth(60)
self.add_point_button.setMinimumWidth(60)
grid_lay2.addWidget(self.pb_label, 10, 0)
grid_lay2.addLayout(self.point_box_container, 11, 0)
grid_lay2.addWidget(self.add_point_button, 11, 1)
self.point_entry = EvalEntry()
self.point_box_container.addWidget(self.point_entry)
self.box_combo = QtWidgets.QComboBox()
self.box_combo.setModel(self.app.collection)
self.box_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
@ -214,7 +214,7 @@ class DblSidedTool(FlatCAMTool):
"- press SHIFT key and left mouse clicking on canvas. Then RMB click in the field and click Paste.\n"
"- by entering the coords manually in the format: (x1, y1), (x2, y2), ...")
)
self.add_drill_point_button.setFixedWidth(60)
self.add_drill_point_button.setMinimumWidth(60)
grid_lay3.addWidget(self.alignment_holes, 0, 0)
grid_lay3.addWidget(self.add_drill_point_button, 0, 1)
@ -255,7 +255,7 @@ class DblSidedTool(FlatCAMTool):
self.reset_button.setToolTip(
_("Resets all the fields.")
)
self.reset_button.setFixedWidth(60)
self.reset_button.setMinimumWidth(60)
hlay2.addWidget(self.reset_button)
self.layout.addStretch()

View File

@ -235,6 +235,51 @@ class NonCopperClear(FlatCAMTool, Gerber):
self.ncc_rest_cb = FCCheckBox()
grid3.addWidget(self.ncc_rest_cb, 6, 1)
# ## Reference
self.reference_radio = RadioSet([{'label': _('Itself'), 'value': 'itself'},
{'label': _('Box'), 'value': 'box'}])
self.reference_label = QtWidgets.QLabel(_("Reference:"))
self.reference_label.setToolTip(
_("- 'Itself': the non copper clearing extent\n"
"is based on the object that is copper cleared.\n "
"- 'Box': will do non copper clearing within the box\n"
"specified by the object selected in the Ref. Object combobox.")
)
grid3.addWidget(self.reference_label, 7, 0)
grid3.addWidget(self.reference_radio, 7, 1)
grid4 = QtWidgets.QGridLayout()
self.tools_box.addLayout(grid4)
self.box_combo_type_label = QtWidgets.QLabel(_("Ref. Type:"))
self.box_combo_type_label.setToolTip(
_("The type of FlatCAM object to be used as non copper clearing reference.\n"
"It can be Gerber, Excellon or Geometry.")
)
self.box_combo_type = QtWidgets.QComboBox()
self.box_combo_type.addItem(_("Gerber Reference Box Object"))
self.box_combo_type.addItem(_("Excellon Reference Box Object"))
self.box_combo_type.addItem(_("Geometry Reference Box Object"))
grid4.addWidget(self.box_combo_type_label, 0, 0)
grid4.addWidget(self.box_combo_type, 0, 1)
self.box_combo_label = QtWidgets.QLabel(_("Ref. Object:"))
self.box_combo_label.setToolTip(
_("The FlatCAM object to be used as non copper clearing reference.")
)
self.box_combo = QtWidgets.QComboBox()
self.box_combo.setModel(self.app.collection)
self.box_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.box_combo.setCurrentIndex(1)
grid4.addWidget(self.box_combo_label, 1, 0)
grid4.addWidget(self.box_combo, 1, 1)
self.box_combo.hide()
self.box_combo_label.hide()
self.box_combo_type.hide()
self.box_combo_type_label.hide()
self.generate_ncc_button = QtWidgets.QPushButton(_('Generate Geometry'))
self.generate_ncc_button.setToolTip(
_("Create the Geometry Object\n"
@ -251,6 +296,9 @@ class NonCopperClear(FlatCAMTool, Gerber):
self.obj_name = ""
self.ncc_obj = None
self.bound_obj_name = ""
self.bound_obj = None
self.tools_box.addStretch()
self.addtool_btn.clicked.connect(self.on_tool_add)
@ -258,6 +306,9 @@ class NonCopperClear(FlatCAMTool, Gerber):
self.deltool_btn.clicked.connect(self.on_tool_delete)
self.generate_ncc_button.clicked.connect(self.on_ncc)
self.box_combo_type.currentIndexChanged.connect(self.on_combo_box_type)
self.reference_radio.group_toggle_fn = self.on_toggle_reference
def install(self, icon=None, separator=None, **kwargs):
FlatCAMTool.install(self, icon, separator, shortcut='ALT+N', **kwargs)
@ -293,6 +344,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
self.ncc_connect_cb.set_value(self.app.defaults["tools_nccconnect"])
self.ncc_contour_cb.set_value(self.app.defaults["tools_ncccontour"])
self.ncc_rest_cb.set_value(self.app.defaults["tools_nccrest"])
self.reference_radio.set_value(self.app.defaults["tools_nccref"])
self.tools_table.setupContextMenu()
self.tools_table.addContextMenu(
@ -365,8 +417,12 @@ class NonCopperClear(FlatCAMTool, Gerber):
'solid_geometry': []
}
})
self.obj_name = ""
self.ncc_obj = None
self.bound_obj_name = ""
self.bound_obj = None
self.tool_type_item_options = ["C1", "C2", "C3", "C4", "B", "V"]
self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper()
@ -464,6 +520,23 @@ class NonCopperClear(FlatCAMTool, Gerber):
except (TypeError, AttributeError):
pass
def on_combo_box_type(self):
obj_type = self.box_combo_type.currentIndex()
self.box_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
self.box_combo.setCurrentIndex(0)
def on_toggle_reference(self):
if self.reference_radio.get_value() == "itself":
self.box_combo.hide()
self.box_combo_label.hide()
self.box_combo_type.hide()
self.box_combo_type_label.hide()
else:
self.box_combo.show()
self.box_combo_label.show()
self.box_combo_type.show()
self.box_combo_type_label.show()
def on_tool_add(self, dia=None, muted=None):
self.ui_disconnect()
@ -647,7 +720,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
"use a number."))
return
margin = margin if margin else self.app.defaults["tools_nccmargin"]
margin = margin if margin is not None else float(self.app.defaults["tools_nccmargin"])
connect = self.ncc_connect_cb.get_value()
connect = connect if connect else self.app.defaults["tools_nccconnect"]
@ -661,6 +734,23 @@ class NonCopperClear(FlatCAMTool, Gerber):
pol_method = self.ncc_method_radio.get_value()
pol_method = pol_method if pol_method else self.app.defaults["tools_nccmethod"]
if self.reference_radio.get_value() == 'itself':
self.bound_obj_name = self.object_combo.currentText()
# Get source object.
try:
self.bound_obj = self.app.collection.get_by_name(self.bound_obj_name)
except Exception as e:
self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % self.obj_name)
return "Could not retrieve object: %s" % self.obj_name
else:
self.bound_obj_name = self.box_combo.currentText()
# Get source object.
try:
self.bound_obj = self.app.collection.get_by_name(self.bound_obj_name)
except Exception as e:
self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % self.obj_name)
return "Could not retrieve object: %s" % self.obj_name
self.obj_name = self.object_combo.currentText()
# Get source object.
try:
@ -671,10 +761,15 @@ class NonCopperClear(FlatCAMTool, Gerber):
# Prepare non-copper polygons
try:
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."))
if not isinstance(self.bound_obj.solid_geometry, MultiPolygon):
env_obj = cascaded_union(self.bound_obj.solid_geometry)
env_obj = env_obj.convex_hull
else:
env_obj = self.bound_obj.solid_geometry.convex_hull
bounding_box = env_obj.buffer(distance=margin, join_style=base.JOIN_STYLE.mitre)
except Exception as e:
log.debug("NonCopperClear.on_ncc() --> %s" % str(e))
self.app.inform.emit(_("[ERROR_NOTCL] No object available."))
return
# calculate the empty area by subtracting the solid_geometry from the object bounding box geometry
@ -682,6 +777,10 @@ class NonCopperClear(FlatCAMTool, Gerber):
if type(empty) is Polygon:
empty = MultiPolygon([empty])
if empty.is_empty:
self.app.inform.emit(_("[ERROR_NOTCL] Could not get the extent of the area to be non copper cleared."))
return
# clear non copper using standard algorithm
if clearing_method is False:
self.clear_non_copper(

View File

@ -245,8 +245,8 @@ class ToolPaint(FlatCAMTool, Gerber):
# grid3 = QtWidgets.QGridLayout()
self.selectmethod_combo = RadioSet([
{"label": _("Single"), "value": "single"},
{"label": _("All"), "value": "all"},
# {"label": "Rectangle", "value": "rectangle"}
{"label": _("Area"), "value": "area"},
{"label": _("All"), "value": "all"}
])
grid3.addWidget(self.selectmethod_combo, 7, 1)
@ -269,6 +269,9 @@ class ToolPaint(FlatCAMTool, Gerber):
self.units = ''
self.paint_tools = {}
self.tooluid = 0
self.first_click = False
self.cursor_pos = None
# store here the default data for Geometry Data
self.default_data = {}
self.default_data.update({
@ -353,6 +356,10 @@ class ToolPaint(FlatCAMTool, Gerber):
self.addtool_btn.setDisabled(True)
self.deltool_btn.setDisabled(True)
self.tools_table.setContextMenuPolicy(Qt.NoContextMenu)
if self.selectmethod_combo.get_value() == 'area':
# disable rest-machining for single polygon painting
self.rest_cb.set_value(False)
self.rest_cb.setDisabled(True)
else:
self.rest_cb.setDisabled(False)
self.addtool_entry.setDisabled(False)
@ -433,7 +440,6 @@ class ToolPaint(FlatCAMTool, Gerber):
self.tools_table.setContextMenuPolicy(Qt.NoContextMenu)
def build_ui(self):
try:
# if connected, disconnect the signal from the slot on item_changed as it creates issues
self.tools_table.itemChanged.disconnect()
@ -787,7 +793,7 @@ class ToolPaint(FlatCAMTool, Gerber):
connect=connect,
contour=contour)
if select_method == "single":
elif select_method == "single":
self.app.inform.emit(_("[WARNING_NOTCL] Click inside the desired polygon."))
# use the first tool in the tool table; get the diameter
@ -815,6 +821,79 @@ class ToolPaint(FlatCAMTool, Gerber):
self.app.plotcanvas.vis_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
self.app.plotcanvas.vis_connect('mouse_press', doit)
elif select_method == "area":
self.app.inform.emit(_("[WARNING_NOTCL] Click the start point of the paint area."))
# use the first tool in the tool table; get the diameter
tooldia = float('%.4f' % float(self.tools_table.item(0, 1).text()))
# To be called after clicking on the plot.
def on_mouse_press(event):
# do paint single only for left mouse clicks
if event.button == 1:
if not self.first_click:
self.first_click = True
self.app.inform.emit(_("[WARNING_NOTCL] Click the end point of the paint area."))
self.cursor_pos = self.app.plotcanvas.vispy_canvas.translate_coords(event.pos)
if self.app.grid_status():
self.cursor_pos = self.app.geo_editor.snap(self.cursor_pos[0], self.cursor_pos[1])
else:
self.app.inform.emit(_("Done."))
self.first_click = False
self.app.delete_selection_shape()
curr_pos = self.app.plotcanvas.vispy_canvas.translate_coords(event.pos)
if self.app.grid_status():
curr_pos = self.app.geo_editor.snap(curr_pos[0], curr_pos[1])
x0, y0 = self.cursor_pos[0], self.cursor_pos[1]
x1, y1 = curr_pos[0], curr_pos[1]
pt1 = (x0, y0)
pt2 = (x1, y0)
pt3 = (x1, y1)
pt4 = (x0, y1)
sel_rect = Polygon([pt1, pt2, pt3, pt4])
self.paint_poly_area(obj=self.paint_obj,
sel_obj= sel_rect,
outname=o_name,
overlap=overlap,
connect=connect,
contour=contour)
self.app.plotcanvas.vis_disconnect('mouse_press', on_mouse_press)
self.app.plotcanvas.vis_disconnect('mouse_move', on_mouse_move)
self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
self.app.plotcanvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot)
self.app.plotcanvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
# called on mouse move
def on_mouse_move(event):
curr_pos = self.app.plotcanvas.vispy_canvas.translate_coords(event.pos)
self.app.app_cursor.enabled = False
if self.app.grid_status():
self.app.app_cursor.enabled = True
# Update cursor
curr_pos = self.app.geo_editor.snap(curr_pos[0], curr_pos[1])
self.app.app_cursor.set_data(np.asarray([(curr_pos[0], curr_pos[1])]),
symbol='++', edge_color='black', size=20)
if self.first_click:
self.app.delete_selection_shape()
self.app.draw_moving_selection_shape(old_coords=(self.cursor_pos[0], self.cursor_pos[1]),
coords=(curr_pos[0], curr_pos[1]),
face_alpha=0.0)
self.app.plotcanvas.vis_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
self.app.plotcanvas.vis_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
self.app.plotcanvas.vis_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
self.app.plotcanvas.vis_connect('mouse_press', on_mouse_press)
self.app.plotcanvas.vis_connect('mouse_move', on_mouse_move)
def paint_poly(self, obj, inside_pt, tooldia, overlap, outname=None, connect=True, contour=True):
"""
Paints a polygon selected by clicking on its interior.
@ -1276,5 +1355,323 @@ class ToolPaint(FlatCAMTool, Gerber):
# Background
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
def paint_poly_area(self, obj, sel_obj, overlap, outname=None, connect=True, contour=True):
"""
Paints all polygons in this object that are within the sel_obj object
:param obj: painted object
:param sel_obj: paint only what is inside this object bounds
:param overlap:
:param outname:
:param connect: Connect lines to avoid tool lifts.
:param contour: Paint around the edges.
:return:
"""
paint_method = self.paintmethod_combo.get_value()
try:
paint_margin = float(self.paintmargin_entry.get_value())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
paint_margin = float(self.paintmargin_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
"use a number."))
return
proc = self.app.proc_container.new(_("Painting polygon..."))
name = outname if outname else self.obj_name + "_paint"
over = overlap
conn = connect
cont = contour
def recurse(geometry, reset=True):
"""
Creates a list of non-iterable linear geometry objects.
Results are placed in self.flat_geometry
:param geometry: Shapely type or list or list of list of such.
:param reset: Clears the contents of self.flat_geometry.
"""
if geometry is None:
return
if reset:
self.flat_geometry = []
# ## If iterable, expand recursively.
try:
for geo in geometry:
if geo is not None:
recurse(geometry=geo, reset=False)
# ## Not iterable, do the actual indexing and add.
except TypeError:
if isinstance(geometry, LinearRing):
g = Polygon(geometry)
self.flat_geometry.append(g)
else:
self.flat_geometry.append(geometry)
return self.flat_geometry
# Initializes the new geometry object
def gen_paintarea(geo_obj, app_obj):
assert isinstance(geo_obj, FlatCAMGeometry), \
"Initializer expected a FlatCAMGeometry, got %s" % type(geo_obj)
tool_dia = None
sorted_tools = []
for row in range(self.tools_table.rowCount()):
sorted_tools.append(float(self.tools_table.item(row, 1).text()))
sorted_tools.sort(reverse=True)
geo_to_paint = []
for poly in obj.solid_geometry:
new_pol = poly.intersection(sel_obj)
geo_to_paint.append(new_pol)
try:
a, b, c, d = self.paint_bounds(geo_to_paint)
geo_obj.options['xmin'] = a
geo_obj.options['ymin'] = b
geo_obj.options['xmax'] = c
geo_obj.options['ymax'] = d
except Exception as e:
log.debug("ToolPaint.paint_poly.gen_paintarea() bounds error --> %s" % str(e))
return
total_geometry = []
current_uid = int(1)
geo_obj.solid_geometry = []
for tool_dia in sorted_tools:
# find the tooluid associated with the current tool_dia so we know where to add the tool solid_geometry
for k, v in self.paint_tools.items():
if float('%.4f' % v['tooldia']) == float('%.4f' % tool_dia):
current_uid = int(k)
break
for geo in recurse(geo_to_paint):
try:
# Polygons are the only really paintable geometries, lines in theory have no area to be painted
if not isinstance(geo, Polygon):
continue
poly_buf = geo.buffer(-paint_margin)
if paint_method == "seed":
# Type(cp) == FlatCAMRTreeStorage | None
cp = self.clear_polygon2(poly_buf,
tooldia=tool_dia,
steps_per_circle=self.app.defaults["geometry_circle_steps"],
overlap=over,
contour=cont,
connect=conn)
elif paint_method == "lines":
# Type(cp) == FlatCAMRTreeStorage | None
cp = self.clear_polygon3(poly_buf,
tooldia=tool_dia,
steps_per_circle=self.app.defaults["geometry_circle_steps"],
overlap=over,
contour=cont,
connect=conn)
else:
# Type(cp) == FlatCAMRTreeStorage | None
cp = self.clear_polygon(poly_buf,
tooldia=tool_dia,
steps_per_circle=self.app.defaults["geometry_circle_steps"],
overlap=over,
contour=cont,
connect=conn)
if cp is not None:
total_geometry += list(cp.get_objects())
except Exception as e:
log.debug("Could not Paint the polygons. %s" % str(e))
self.app.inform.emit(
_("[ERROR] Could not do Paint All. Try a different combination of parameters. "
"Or a different Method of paint\n%s") % str(e))
return
# add the solid_geometry to the current too in self.paint_tools dictionary and then reset the
# temporary list that stored that solid_geometry
self.paint_tools[current_uid]['solid_geometry'] = deepcopy(total_geometry)
self.paint_tools[current_uid]['data']['name'] = name
total_geometry[:] = []
geo_obj.options["cnctooldia"] = str(tool_dia)
# this turn on the FlatCAMCNCJob plot for multiple tools
geo_obj.multigeo = True
geo_obj.multitool = True
geo_obj.tools.clear()
geo_obj.tools = dict(self.paint_tools)
# test if at least one tool has solid_geometry. If no tool has solid_geometry we raise an Exception
has_solid_geo = 0
for tooluid in geo_obj.tools:
if geo_obj.tools[tooluid]['solid_geometry']:
has_solid_geo += 1
if has_solid_geo == 0:
self.app.inform.emit(_("[ERROR] There is no Painting Geometry in the file.\n"
"Usually it means that the tool diameter is too big for the painted geometry.\n"
"Change the painting parameters and try again."))
return
# Experimental...
# print("Indexing...", end=' ')
# geo_obj.make_index()
self.app.inform.emit(_("[success] Paint All Done."))
# Initializes the new geometry object
def gen_paintarea_rest_machining(geo_obj, app_obj):
assert isinstance(geo_obj, FlatCAMGeometry), \
"Initializer expected a FlatCAMGeometry, got %s" % type(geo_obj)
tool_dia = None
sorted_tools = []
for row in range(self.tools_table.rowCount()):
sorted_tools.append(float(self.tools_table.item(row, 1).text()))
sorted_tools.sort(reverse=True)
cleared_geo = []
current_uid = int(1)
geo_obj.solid_geometry = []
try:
a, b, c, d = obj.bounds()
geo_obj.options['xmin'] = a
geo_obj.options['ymin'] = b
geo_obj.options['xmax'] = c
geo_obj.options['ymax'] = d
except Exception as e:
log.debug("ToolPaint.paint_poly.gen_paintarea() bounds error --> %s" % str(e))
return
for tool_dia in sorted_tools:
for geo in recurse(obj.solid_geometry):
try:
geo = Polygon(geo) if not isinstance(geo, Polygon) else geo
poly_buf = geo.buffer(-paint_margin)
cp = None
if paint_method == "standard":
# Type(cp) == FlatCAMRTreeStorage | None
cp = self.clear_polygon(poly_buf, tooldia=tool_dia,
steps_per_circle=self.app.defaults["geometry_circle_steps"],
overlap=over, contour=cont, connect=conn)
elif paint_method == "seed":
# Type(cp) == FlatCAMRTreeStorage | None
cp = self.clear_polygon2(poly_buf, tooldia=tool_dia,
steps_per_circle=self.app.defaults["geometry_circle_steps"],
overlap=over, contour=cont, connect=conn)
elif paint_method == "lines":
# Type(cp) == FlatCAMRTreeStorage | None
cp = self.clear_polygon3(poly_buf, tooldia=tool_dia,
steps_per_circle=self.app.defaults["geometry_circle_steps"],
overlap=over, contour=cont, connect=conn)
if cp is not None:
cleared_geo += list(cp.get_objects())
except Exception as e:
log.debug("Could not Paint the polygons. %s" % str(e))
self.app.inform.emit(
_("[ERROR] Could not do Paint All. Try a different combination of parameters. "
"Or a different Method of paint\n%s") % str(e))
return
# find the tooluid associated with the current tool_dia so we know where to add the tool solid_geometry
for k, v in self.paint_tools.items():
if float('%.4f' % v['tooldia']) == float('%.4f' % tool_dia):
current_uid = int(k)
break
# add the solid_geometry to the current too in self.paint_tools dictionary and then reset the
# temporary list that stored that solid_geometry
self.paint_tools[current_uid]['solid_geometry'] = deepcopy(cleared_geo)
self.paint_tools[current_uid]['data']['name'] = name
cleared_geo[:] = []
geo_obj.options["cnctooldia"] = str(tool_dia)
# this turn on the FlatCAMCNCJob plot for multiple tools
geo_obj.multigeo = True
geo_obj.multitool = True
geo_obj.tools.clear()
geo_obj.tools = dict(self.paint_tools)
# test if at least one tool has solid_geometry. If no tool has solid_geometry we raise an Exception
has_solid_geo = 0
for tooluid in geo_obj.tools:
if geo_obj.tools[tooluid]['solid_geometry']:
has_solid_geo += 1
if has_solid_geo == 0:
self.app.inform.emit(_("[ERROR_NOTCL] There is no Painting Geometry in the file.\n"
"Usually it means that the tool diameter is too big for the painted geometry.\n"
"Change the painting parameters and try again."))
return
# Experimental...
# print("Indexing...", end=' ')
# geo_obj.make_index()
self.app.inform.emit(_("[success] Paint All with Rest-Machining done."))
def job_thread(app_obj):
try:
if self.rest_cb.isChecked():
app_obj.new_object("geometry", name, gen_paintarea_rest_machining)
else:
app_obj.new_object("geometry", name, gen_paintarea)
except Exception as e:
proc.done()
traceback.print_stack()
return
proc.done()
# focus on Selected Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
self.app.inform.emit(_("Polygon Paint started ..."))
# Promise object with the new name
self.app.collection.promise(name)
# Background
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
@staticmethod
def paint_bounds(geometry):
def bounds_rec(o):
if type(o) is list:
minx = Inf
miny = Inf
maxx = -Inf
maxy = -Inf
for k in o:
try:
minx_, miny_, maxx_, maxy_ = bounds_rec(k)
except Exception as e:
log.debug("ToolPaint.bounds() --> %s" % str(e))
return
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
return o.bounds
return bounds_rec(geometry)
def reset_fields(self):
self.object_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))

View File

@ -1396,6 +1396,10 @@ class SolderPaste(FlatCAMTool):
except FileNotFoundError:
self.app.inform.emit(_("[WARNING_NOTCL] No such file or directory"))
return
except PermissionError:
self.app.inform.emit(_("[WARNING] Permission denied, saving not possible.\n"
"Most likely another app is holding the file open and not accessible."))
return 'fail'
if self.app.defaults["global_open_style"] is False:
self.app.file_opened.emit("gcode", filename)

View File

@ -44,16 +44,16 @@ class ToolTransform(FlatCAMTool):
self.transform_lay.addWidget(title_label)
self.empty_label = QtWidgets.QLabel("")
self.empty_label.setFixedWidth(70)
self.empty_label.setMinimumWidth(70)
self.empty_label1 = QtWidgets.QLabel("")
self.empty_label1.setFixedWidth(70)
self.empty_label1.setMinimumWidth(70)
self.empty_label2 = QtWidgets.QLabel("")
self.empty_label2.setFixedWidth(70)
self.empty_label2.setMinimumWidth(70)
self.empty_label3 = QtWidgets.QLabel("")
self.empty_label3.setFixedWidth(70)
self.empty_label3.setMinimumWidth(70)
self.empty_label4 = QtWidgets.QLabel("")
self.empty_label4.setFixedWidth(70)
self.empty_label4.setMinimumWidth(70)
self.transform_lay.addWidget(self.empty_label)
# ## Rotate Title
@ -72,7 +72,7 @@ class ToolTransform(FlatCAMTool):
"Positive numbers for CW motion.\n"
"Negative numbers for CCW motion.")
)
self.rotate_label.setFixedWidth(70)
self.rotate_label.setMinimumWidth(70)
self.rotate_entry = FCEntry()
# self.rotate_entry.setFixedWidth(70)
@ -85,7 +85,7 @@ class ToolTransform(FlatCAMTool):
"The point of reference is the middle of\n"
"the bounding box for all selected objects.")
)
self.rotate_button.setFixedWidth(90)
self.rotate_button.setMinimumWidth(90)
form_child.addWidget(self.rotate_entry)
form_child.addWidget(self.rotate_button)
@ -109,7 +109,7 @@ class ToolTransform(FlatCAMTool):
_("Angle for Skew action, in degrees.\n"
"Float number between -360 and 359.")
)
self.skewx_label.setFixedWidth(70)
self.skewx_label.setMinimumWidth(70)
self.skewx_entry = FCEntry()
self.skewx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
# self.skewx_entry.setFixedWidth(70)
@ -120,14 +120,14 @@ class ToolTransform(FlatCAMTool):
_("Skew/shear the selected object(s).\n"
"The point of reference is the middle of\n"
"the bounding box for all selected objects."))
self.skewx_button.setFixedWidth(90)
self.skewx_button.setMinimumWidth(90)
self.skewy_label = QtWidgets.QLabel(_("Angle Y:"))
self.skewy_label.setToolTip(
_("Angle for Skew action, in degrees.\n"
"Float number between -360 and 359.")
)
self.skewy_label.setFixedWidth(70)
self.skewy_label.setMinimumWidth(70)
self.skewy_entry = FCEntry()
self.skewy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
# self.skewy_entry.setFixedWidth(70)
@ -138,7 +138,7 @@ class ToolTransform(FlatCAMTool):
_("Skew/shear the selected object(s).\n"
"The point of reference is the middle of\n"
"the bounding box for all selected objects."))
self.skewy_button.setFixedWidth(90)
self.skewy_button.setMinimumWidth(90)
form1_child_1.addWidget(self.skewx_entry)
form1_child_1.addWidget(self.skewx_button)
@ -165,7 +165,7 @@ class ToolTransform(FlatCAMTool):
self.scalex_label.setToolTip(
_("Factor for Scale action over X axis.")
)
self.scalex_label.setFixedWidth(70)
self.scalex_label.setMinimumWidth(70)
self.scalex_entry = FCEntry()
self.scalex_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
# self.scalex_entry.setFixedWidth(70)
@ -176,13 +176,13 @@ class ToolTransform(FlatCAMTool):
_("Scale the selected object(s).\n"
"The point of reference depends on \n"
"the Scale reference checkbox state."))
self.scalex_button.setFixedWidth(90)
self.scalex_button.setMinimumWidth(90)
self.scaley_label = QtWidgets.QLabel(_("Factor Y:"))
self.scaley_label.setToolTip(
_("Factor for Scale action over Y axis.")
)
self.scaley_label.setFixedWidth(70)
self.scaley_label.setMinimumWidth(70)
self.scaley_entry = FCEntry()
self.scaley_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
# self.scaley_entry.setFixedWidth(70)
@ -193,7 +193,7 @@ class ToolTransform(FlatCAMTool):
_("Scale the selected object(s).\n"
"The point of reference depends on \n"
"the Scale reference checkbox state."))
self.scaley_button.setFixedWidth(90)
self.scaley_button.setMinimumWidth(90)
self.scale_link_cb = FCCheckBox()
self.scale_link_cb.set_value(True)
@ -201,7 +201,7 @@ class ToolTransform(FlatCAMTool):
self.scale_link_cb.setToolTip(
_("Scale the selected object(s)\n"
"using the Scale Factor X for both axis."))
self.scale_link_cb.setFixedWidth(70)
self.scale_link_cb.setMinimumWidth(70)
self.scale_zero_ref_cb = FCCheckBox()
self.scale_zero_ref_cb.set_value(True)
@ -239,7 +239,7 @@ class ToolTransform(FlatCAMTool):
self.offx_label.setToolTip(
_("Value for Offset action on X axis.")
)
self.offx_label.setFixedWidth(70)
self.offx_label.setMinimumWidth(70)
self.offx_entry = FCEntry()
self.offx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
# self.offx_entry.setFixedWidth(70)
@ -250,13 +250,13 @@ class ToolTransform(FlatCAMTool):
_("Offset the selected object(s).\n"
"The point of reference is the middle of\n"
"the bounding box for all selected objects.\n"))
self.offx_button.setFixedWidth(90)
self.offx_button.setMinimumWidth(90)
self.offy_label = QtWidgets.QLabel(_("Value Y:"))
self.offy_label.setToolTip(
_("Value for Offset action on Y axis.")
)
self.offy_label.setFixedWidth(70)
self.offy_label.setMinimumWidth(70)
self.offy_entry = FCEntry()
self.offy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
# self.offy_entry.setFixedWidth(70)
@ -267,7 +267,7 @@ class ToolTransform(FlatCAMTool):
_("Offset the selected object(s).\n"
"The point of reference is the middle of\n"
"the bounding box for all selected objects.\n"))
self.offy_button.setFixedWidth(90)
self.offy_button.setMinimumWidth(90)
form3_child_1.addWidget(self.offx_entry)
form3_child_1.addWidget(self.offx_button)
@ -297,7 +297,7 @@ class ToolTransform(FlatCAMTool):
_("Flip the selected object(s) over the X axis.\n"
"Does not create a new object.\n ")
)
self.flipx_button.setFixedWidth(100)
self.flipx_button.setMinimumWidth(100)
self.flipy_button = FCButton()
self.flipy_button.set_value(_("Flip on Y"))
@ -305,7 +305,7 @@ class ToolTransform(FlatCAMTool):
_("Flip the selected object(s) over the X axis.\n"
"Does not create a new object.\n ")
)
self.flipy_button.setFixedWidth(90)
self.flipy_button.setMinimumWidth(90)
self.flip_ref_cb = FCCheckBox()
self.flip_ref_cb.set_value(True)
@ -320,7 +320,7 @@ class ToolTransform(FlatCAMTool):
"Then click Add button to insert coordinates.\n"
"Or enter the coords in format (x, y) in the\n"
"Point Entry field and click Flip on X(Y)"))
self.flip_ref_cb.setFixedWidth(70)
self.flip_ref_cb.setMinimumWidth(70)
self.flip_ref_label = QtWidgets.QLabel(_("Point:"))
self.flip_ref_label.setToolTip(
@ -328,7 +328,7 @@ class ToolTransform(FlatCAMTool):
"The 'x' in (x, y) will be used when using Flip on X and\n"
"the 'y' in (x, y) will be used when using Flip on Y and")
)
self.flip_ref_label.setFixedWidth(70)
self.flip_ref_label.setMinimumWidth(70)
self.flip_ref_entry = EvalEntry2("(0, 0)")
self.flip_ref_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
# self.flip_ref_entry.setFixedWidth(70)
@ -339,7 +339,7 @@ class ToolTransform(FlatCAMTool):
_("The point coordinates can be captured by\n"
"left click on canvas together with pressing\n"
"SHIFT key. Then click Add button to insert."))
self.flip_ref_button.setFixedWidth(90)
self.flip_ref_button.setMinimumWidth(90)
form4_child_hlay.addStretch()
form4_child_hlay.addWidget(self.flipx_button)

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff