- 2Sided Tool - fixed newly introduced issues in the Alignment section

- 2Sided Tool - modified the UI such that some of the fields will allow only numbers and some special characters ([,],(,),/,*,,,+,-,%)
- Cutout Tool - working on adding mouse bites for the Freeform cutout
- updated the translation files to the current state of the app
This commit is contained in:
Marius Stanciu 2020-08-29 16:10:00 +03:00 committed by Marius
parent 6c9c367540
commit f73f6fb5da
25 changed files with 81423 additions and 64480 deletions

View File

@ -7,6 +7,13 @@ CHANGELOG for FlatCAM beta
=================================================
29.08.2020
- 2Sided Tool - fixed newly introduced issues in the Alignment section
- 2Sided Tool - modified the UI such that some of the fields will allow only numbers and some special characters ([,],(,),/,*,,,+,-,%)
- Cutout Tool - working on adding mouse bites for the Freeform cutout
- updated the translation files to the current state of the app
28.08.2020
- Paint Tool - upgraded the UI and added the functionality that now adding a new tool is done by first searching the Tools DB for a suitable tool and if fails then it adds an default tool

View File

@ -286,7 +286,7 @@ class LengthEntry(QtWidgets.QLineEdit):
# Unit conversion table OUTPUT-INPUT
self.scales = {
'IN': {'IN': 1.0,
'MM': 1/25.4},
'MM': 1 / 25.4},
'MM': {'IN': 25.4,
'MM': 1.0}
}
@ -668,6 +668,7 @@ class NumericalEvalEntry(FCEntry):
"""
Will evaluate the input and return a value. Accepts only float numbers and formulas using the operators: /,*,+,-,%
"""
def __init__(self, border_color=None):
super().__init__(border_color=border_color)
@ -691,10 +692,11 @@ class NumericalEvalTupleEntry(EvalEntry):
"""
Will return a text value. Accepts only float numbers and formulas using the operators: /,*,+,-,%
"""
def __init__(self, border_color=None):
super().__init__(border_color=border_color)
regex = QtCore.QRegExp("[0-9\/\*\+\-\%\.\s\,\[\]]*")
regex = QtCore.QRegExp("[0-9\/\*\+\-\%\.\s\,\[\]\(\)]*")
validator = QtGui.QRegExpValidator(regex, self)
self.setValidator(validator)
@ -802,7 +804,6 @@ class FCSliderWithSpinner(QtWidgets.QFrame):
class FCSpinner(QtWidgets.QSpinBox):
returnPressed = QtCore.pyqtSignal()
confirmation_signal = QtCore.pyqtSignal(bool, float, float)
@ -1040,7 +1041,6 @@ class FCSliderWithDoubleSpinner(QtWidgets.QFrame):
class FCDoubleSpinner(QtWidgets.QDoubleSpinBox):
returnPressed = QtCore.pyqtSignal()
confirmation_signal = QtCore.pyqtSignal(bool, float, float)
@ -1694,7 +1694,6 @@ class FCButton(QtWidgets.QPushButton):
class FCLabel(QtWidgets.QLabel):
clicked = QtCore.pyqtSignal(bool)
right_clicked = QtCore.pyqtSignal(bool)
middle_clicked = QtCore.pyqtSignal(bool)
@ -2018,7 +2017,7 @@ class FCDetachableTab(QtWidgets.QTabWidget):
if str(tab_name) == str(self.tabText(index)):
self.protectTab(index)
# Make this tab the current tab
# Make this tab the current tab
if index > -1:
self.setCurrentIndex(insert_index) if self.use_old_index else self.setCurrentIndex(index)
@ -2101,7 +2100,6 @@ class FCDetachableTab(QtWidgets.QTabWidget):
# area to the side of the QTabBar) or there are not tabs
# currently attached...
if tabDropPos.y() < self.tabBar.height() or self.count() == 0:
# Close the detached tab and allow it to re-attach
# automatically
self.detachedTabs[name].close()
@ -2422,6 +2420,7 @@ class VerticalScrollArea(QtWidgets.QScrollArea):
scroll area that also expands horizontally to accommodate
its contents.
"""
def __init__(self, parent=None):
QtWidgets.QScrollArea.__init__(self, parent=parent)
self.setWidgetResizable(True)
@ -2537,7 +2536,6 @@ class OptionalHideInputSection:
class FCTable(QtWidgets.QTableWidget):
drag_drop_sig = QtCore.pyqtSignal(object, int)
lost_focus = QtCore.pyqtSignal()
@ -3011,7 +3009,7 @@ class _BrowserTextEdit(QTextEdit):
save_action.triggered.connect(lambda: self.save_log(app=self.app))
clear_action = QAction(_("Clear"), self)
clear_action.setShortcut(QKeySequence(Qt.Key_Delete)) # it's not working, the shortcut
clear_action.setShortcut(QKeySequence(Qt.Key_Delete)) # it's not working, the shortcut
self.menu.addAction(clear_action)
clear_action.triggered.connect(self.clear)
@ -3507,8 +3505,8 @@ class FCZeroAxes(QtWidgets.QFrame):
class RotatedToolButton(QtWidgets.QToolButton):
def __init__(self, orientation = "east", *args, **kwargs):
super(RotatedToolButton,self).__init__(*args, **kwargs)
def __init__(self, orientation="east", *args, **kwargs):
super(RotatedToolButton, self).__init__(*args, **kwargs)
self.orientation = orientation
def paintEvent(self, event):
@ -3560,8 +3558,8 @@ class RotatedToolButton(QtWidgets.QToolButton):
class RotatedButton(QtWidgets.QPushButton):
def __init__(self, orientation = "west", *args, **kwargs):
super(RotatedButton,self).__init__(*args, **kwargs)
def __init__(self, orientation="west", *args, **kwargs):
super(RotatedButton, self).__init__(*args, **kwargs)
self.orientation = orientation
def paintEvent(self, event):

View File

@ -10,7 +10,7 @@ from appTool import AppTool
from appGUI.GUIElements import FCDoubleSpinner, FCCheckBox, RadioSet, FCComboBox, OptionalInputSection, FCButton, \
FCLabel
from shapely.geometry import box, MultiPolygon, Polygon, LineString, LinearRing, MultiLineString
from shapely.geometry import box, MultiPolygon, Polygon, LineString, LinearRing, MultiLineString, Point
from shapely.ops import cascaded_union, unary_union, linemerge
import shapely.affinity as affinity
@ -187,52 +187,52 @@ class CutOut(AppTool):
self.ui.dia.set_value(float(self.app.defaults["tools_cutout_tooldia"]))
self.default_data.update({
"plot": True,
"plot": True,
"cutz": float(self.app.defaults["geometry_cutz"]),
"multidepth": self.app.defaults["geometry_multidepth"],
"depthperpass": float(self.app.defaults["geometry_depthperpass"]),
"cutz": float(self.app.defaults["geometry_cutz"]),
"multidepth": self.app.defaults["geometry_multidepth"],
"depthperpass": float(self.app.defaults["geometry_depthperpass"]),
"vtipdia": float(self.app.defaults["geometry_vtipdia"]),
"vtipangle": float(self.app.defaults["geometry_vtipangle"]),
"travelz": float(self.app.defaults["geometry_travelz"]),
"feedrate": float(self.app.defaults["geometry_feedrate"]),
"feedrate_z": float(self.app.defaults["geometry_feedrate_z"]),
"feedrate_rapid": float(self.app.defaults["geometry_feedrate_rapid"]),
"spindlespeed": self.app.defaults["geometry_spindlespeed"],
"dwell": self.app.defaults["geometry_dwell"],
"dwelltime": float(self.app.defaults["geometry_dwelltime"]),
"spindledir": self.app.defaults["geometry_spindledir"],
"ppname_g": self.app.defaults["geometry_ppname_g"],
"extracut": self.app.defaults["geometry_extracut"],
"extracut_length": float(self.app.defaults["geometry_extracut_length"]),
"toolchange": self.app.defaults["geometry_toolchange"],
"toolchangexy": self.app.defaults["geometry_toolchangexy"],
"toolchangez": float(self.app.defaults["geometry_toolchangez"]),
"startz": self.app.defaults["geometry_startz"],
"endz": float(self.app.defaults["geometry_endz"]),
"area_exclusion": self.app.defaults["geometry_area_exclusion"],
"area_shape": self.app.defaults["geometry_area_shape"],
"area_strategy": self.app.defaults["geometry_area_strategy"],
"area_overz": float(self.app.defaults["geometry_area_overz"]),
"optimization_type": self.app.defaults["geometry_optimization_type"],
"vtipdia": float(self.app.defaults["geometry_vtipdia"]),
"vtipangle": float(self.app.defaults["geometry_vtipangle"]),
"travelz": float(self.app.defaults["geometry_travelz"]),
"feedrate": float(self.app.defaults["geometry_feedrate"]),
"feedrate_z": float(self.app.defaults["geometry_feedrate_z"]),
"feedrate_rapid": float(self.app.defaults["geometry_feedrate_rapid"]),
"spindlespeed": self.app.defaults["geometry_spindlespeed"],
"dwell": self.app.defaults["geometry_dwell"],
"dwelltime": float(self.app.defaults["geometry_dwelltime"]),
"spindledir": self.app.defaults["geometry_spindledir"],
"ppname_g": self.app.defaults["geometry_ppname_g"],
"extracut": self.app.defaults["geometry_extracut"],
"extracut_length": float(self.app.defaults["geometry_extracut_length"]),
"toolchange": self.app.defaults["geometry_toolchange"],
"toolchangexy": self.app.defaults["geometry_toolchangexy"],
"toolchangez": float(self.app.defaults["geometry_toolchangez"]),
"startz": self.app.defaults["geometry_startz"],
"endz": float(self.app.defaults["geometry_endz"]),
"area_exclusion": self.app.defaults["geometry_area_exclusion"],
"area_shape": self.app.defaults["geometry_area_shape"],
"area_strategy": self.app.defaults["geometry_area_strategy"],
"area_overz": float(self.app.defaults["geometry_area_overz"]),
"optimization_type": self.app.defaults["geometry_optimization_type"],
# Cutout
"tools_cutout_tooldia": self.app.defaults["tools_cutout_tooldia"],
"tools_cutout_kind": self.app.defaults["tools_cutout_kind"],
"tools_cutout_margin": float(self.app.defaults["tools_cutout_margin"]),
"tools_cutout_z": float(self.app.defaults["tools_cutout_z"]),
"tools_cutout_depthperpass": float(self.app.defaults["tools_cutout_depthperpass"]),
"tools_cutout_mdepth": self.app.defaults["tools_cutout_mdepth"],
"tools_cutout_gapsize": float(self.app.defaults["tools_cutout_gapsize"]),
"tools_cutout_gaps_ff": self.app.defaults["tools_cutout_gaps_ff"],
"tools_cutout_convexshape": self.app.defaults["tools_cutout_convexshape"],
"tools_cutout_tooldia": self.app.defaults["tools_cutout_tooldia"],
"tools_cutout_kind": self.app.defaults["tools_cutout_kind"],
"tools_cutout_margin": float(self.app.defaults["tools_cutout_margin"]),
"tools_cutout_z": float(self.app.defaults["tools_cutout_z"]),
"tools_cutout_depthperpass": float(self.app.defaults["tools_cutout_depthperpass"]),
"tools_cutout_mdepth": self.app.defaults["tools_cutout_mdepth"],
"tools_cutout_gapsize": float(self.app.defaults["tools_cutout_gapsize"]),
"tools_cutout_gaps_ff": self.app.defaults["tools_cutout_gaps_ff"],
"tools_cutout_convexshape": self.app.defaults["tools_cutout_convexshape"],
"tools_cutout_big_cursor": self.app.defaults["tools_cutout_big_cursor"],
"tools_cutout_gap_type": self.app.defaults["tools_cutout_gap_type"],
"tools_cutout_gap_depth": float(self.app.defaults["tools_cutout_gap_depth"]),
"tools_cutout_mb_dia": float(self.app.defaults["tools_cutout_mb_dia"]),
"tools_cutout_mb_spacing": float(self.app.defaults["tools_cutout_mb_spacing"]),
"tools_cutout_big_cursor": self.app.defaults["tools_cutout_big_cursor"],
"tools_cutout_gap_type": self.app.defaults["tools_cutout_gap_type"],
"tools_cutout_gap_depth": float(self.app.defaults["tools_cutout_gap_depth"]),
"tools_cutout_mb_dia": float(self.app.defaults["tools_cutout_mb_dia"]),
"tools_cutout_mb_spacing": float(self.app.defaults["tools_cutout_mb_spacing"]),
})
tool_dia = float(self.app.defaults["tools_cutout_tooldia"])
@ -366,14 +366,14 @@ class CutOut(AppTool):
new_tdia = deepcopy(updated_tooldia) if updated_tooldia is not None else deepcopy(truncated_tooldia)
self.cut_tool_dict.update({
'tooldia': new_tdia,
'offset': deepcopy(offset),
'offset_value': deepcopy(offset_val),
'type': deepcopy(typ),
'tool_type': deepcopy(tool_type),
'data': deepcopy(new_tools_dict),
'solid_geometry': []
})
'tooldia': new_tdia,
'offset': deepcopy(offset),
'offset_value': deepcopy(offset_val),
'type': deepcopy(typ),
'tool_type': deepcopy(tool_type),
'data': deepcopy(new_tools_dict),
'solid_geometry': []
})
self.update_ui(new_tools_dict)
@ -381,65 +381,67 @@ class CutOut(AppTool):
self.app.inform.emit('[success] %s' % _("Updated tool from Tools Database."))
def on_tool_default_add(self, dia=None, muted=None):
dia = dia
self.default_data.update({
"plot": True,
"plot": True,
"cutz": float(self.app.defaults["geometry_cutz"]),
"multidepth": self.app.defaults["geometry_multidepth"],
"depthperpass": float(self.app.defaults["geometry_depthperpass"]),
"cutz": float(self.app.defaults["geometry_cutz"]),
"multidepth": self.app.defaults["geometry_multidepth"],
"depthperpass": float(self.app.defaults["geometry_depthperpass"]),
"vtipdia": float(self.app.defaults["geometry_vtipdia"]),
"vtipangle": float(self.app.defaults["geometry_vtipangle"]),
"travelz": float(self.app.defaults["geometry_travelz"]),
"feedrate": float(self.app.defaults["geometry_feedrate"]),
"feedrate_z": float(self.app.defaults["geometry_feedrate_z"]),
"feedrate_rapid": float(self.app.defaults["geometry_feedrate_rapid"]),
"spindlespeed": self.app.defaults["geometry_spindlespeed"],
"dwell": self.app.defaults["geometry_dwell"],
"dwelltime": float(self.app.defaults["geometry_dwelltime"]),
"spindledir": self.app.defaults["geometry_spindledir"],
"ppname_g": self.app.defaults["geometry_ppname_g"],
"extracut": self.app.defaults["geometry_extracut"],
"extracut_length": float(self.app.defaults["geometry_extracut_length"]),
"toolchange": self.app.defaults["geometry_toolchange"],
"toolchangexy": self.app.defaults["geometry_toolchangexy"],
"toolchangez": float(self.app.defaults["geometry_toolchangez"]),
"startz": self.app.defaults["geometry_startz"],
"endz": float(self.app.defaults["geometry_endz"]),
"area_exclusion": self.app.defaults["geometry_area_exclusion"],
"area_shape": self.app.defaults["geometry_area_shape"],
"area_strategy": self.app.defaults["geometry_area_strategy"],
"area_overz": float(self.app.defaults["geometry_area_overz"]),
"optimization_type": self.app.defaults["geometry_optimization_type"],
"vtipdia": float(self.app.defaults["geometry_vtipdia"]),
"vtipangle": float(self.app.defaults["geometry_vtipangle"]),
"travelz": float(self.app.defaults["geometry_travelz"]),
"feedrate": float(self.app.defaults["geometry_feedrate"]),
"feedrate_z": float(self.app.defaults["geometry_feedrate_z"]),
"feedrate_rapid": float(self.app.defaults["geometry_feedrate_rapid"]),
"spindlespeed": self.app.defaults["geometry_spindlespeed"],
"dwell": self.app.defaults["geometry_dwell"],
"dwelltime": float(self.app.defaults["geometry_dwelltime"]),
"spindledir": self.app.defaults["geometry_spindledir"],
"ppname_g": self.app.defaults["geometry_ppname_g"],
"extracut": self.app.defaults["geometry_extracut"],
"extracut_length": float(self.app.defaults["geometry_extracut_length"]),
"toolchange": self.app.defaults["geometry_toolchange"],
"toolchangexy": self.app.defaults["geometry_toolchangexy"],
"toolchangez": float(self.app.defaults["geometry_toolchangez"]),
"startz": self.app.defaults["geometry_startz"],
"endz": float(self.app.defaults["geometry_endz"]),
"area_exclusion": self.app.defaults["geometry_area_exclusion"],
"area_shape": self.app.defaults["geometry_area_shape"],
"area_strategy": self.app.defaults["geometry_area_strategy"],
"area_overz": float(self.app.defaults["geometry_area_overz"]),
"optimization_type": self.app.defaults["geometry_optimization_type"],
# Cutout
"tools_cutout_tooldia": self.app.defaults["tools_cutout_tooldia"],
"tools_cutout_kind": self.app.defaults["tools_cutout_kind"],
"tools_cutout_margin": float(self.app.defaults["tools_cutout_margin"]),
"tools_cutout_z": float(self.app.defaults["tools_cutout_z"]),
"tools_cutout_depthperpass": float(self.app.defaults["tools_cutout_depthperpass"]),
"tools_cutout_mdepth": self.app.defaults["tools_cutout_mdepth"],
"tools_cutout_gapsize": float(self.app.defaults["tools_cutout_gapsize"]),
"tools_cutout_gaps_ff": self.app.defaults["tools_cutout_gaps_ff"],
"tools_cutout_convexshape": self.app.defaults["tools_cutout_convexshape"],
"tools_cutout_tooldia": self.app.defaults["tools_cutout_tooldia"],
"tools_cutout_kind": self.app.defaults["tools_cutout_kind"],
"tools_cutout_margin": float(self.app.defaults["tools_cutout_margin"]),
"tools_cutout_z": float(self.app.defaults["tools_cutout_z"]),
"tools_cutout_depthperpass": float(self.app.defaults["tools_cutout_depthperpass"]),
"tools_cutout_mdepth": self.app.defaults["tools_cutout_mdepth"],
"tools_cutout_gapsize": float(self.app.defaults["tools_cutout_gapsize"]),
"tools_cutout_gaps_ff": self.app.defaults["tools_cutout_gaps_ff"],
"tools_cutout_convexshape": self.app.defaults["tools_cutout_convexshape"],
"tools_cutout_big_cursor": self.app.defaults["tools_cutout_big_cursor"],
"tools_cutout_gap_type": self.app.defaults["tools_cutout_gap_type"],
"tools_cutout_gap_depth": float(self.app.defaults["tools_cutout_gap_depth"]),
"tools_cutout_mb_dia": float(self.app.defaults["tools_cutout_mb_dia"]),
"tools_cutout_mb_spacing": float(self.app.defaults["tools_cutout_mb_spacing"]),
"tools_cutout_big_cursor": self.app.defaults["tools_cutout_big_cursor"],
"tools_cutout_gap_type": self.app.defaults["tools_cutout_gap_type"],
"tools_cutout_gap_depth": float(self.app.defaults["tools_cutout_gap_depth"]),
"tools_cutout_mb_dia": float(self.app.defaults["tools_cutout_mb_dia"]),
"tools_cutout_mb_spacing": float(self.app.defaults["tools_cutout_mb_spacing"]),
})
self.cut_tool_dict.update({
'tooldia': str(self.app.defaults["tools_cutout_tooldia"]),
'offset': 'Path',
'offset_value': 0.0,
'type': _('Rough'),
'tool_type': 'C1',
'data': deepcopy(self.default_data),
'solid_geometry': []
})
'tooldia': str(self.app.defaults["tools_cutout_tooldia"]),
'offset': 'Path',
'offset_value': 0.0,
'type': _('Rough'),
'tool_type': 'C1',
'data': deepcopy(self.default_data),
'solid_geometry': []
})
self.update_ui(self.default_data)
@ -490,13 +492,13 @@ class CutOut(AppTool):
truncated_tooldia = self.app.dec_format(tooldia, self.decimals)
self.cutout_tools.update({
1: {
'tooldia': truncated_tooldia,
'offset': tool['offset'],
'offset_value': tool['offset_value'],
'type': tool['type'],
'tool_type': tool['tool_type'],
'data': deepcopy(tool['data']),
'solid_geometry': []
'tooldia': truncated_tooldia,
'offset': tool['offset'],
'offset_value': tool['offset_value'],
'type': tool['type'],
'tool_type': tool['tool_type'],
'data': deepcopy(tool['data']),
'solid_geometry': []
}
})
self.cutout_tools[1]['data']['name'] = '_cutout'
@ -554,7 +556,6 @@ class CutOut(AppTool):
return
margin = float(self.ui.margin.get_value())
gapsize = float(self.ui.gapsize.get_value())
try:
gaps = self.ui.gaps.get_value()
@ -575,225 +576,274 @@ class CutOut(AppTool):
"and after that perform Cutout."))
return
convex_box = self.ui.convex_box_cb.get_value()
def cutout_handler(geom, gapsize):
proc_geometry = []
rest_geometry = []
r_temp_geo = []
initial_geo = deepcopy(geom)
gapsize = gapsize / 2 + (dia / 2)
# Get min and max data for each object as we just cut rectangles across X or Y
xxmin, yymin, xxmax, yymax = CutOut.recursive_bounds(geom)
def geo_init(geo_obj, app_obj):
solid_geo = []
gaps_solid_geo = None
px = 0.5 * (xxmin + xxmax) + margin
py = 0.5 * (yymin + yymax) + margin
lenx = (xxmax - xxmin) + (margin * 2)
leny = (yymax - yymin) + (margin * 2)
if cutout_obj.kind == 'gerber':
if isinstance(cutout_obj.solid_geometry, list):
cutout_obj.solid_geometry = MultiPolygon(cutout_obj.solid_geometry)
try:
if convex_box:
object_geo = cutout_obj.solid_geometry.convex_hull
else:
object_geo = cutout_obj.solid_geometry
except Exception as err:
log.debug("CutOut.on_freeform_cutout().geo_init() --> %s" % str(err))
object_geo = cutout_obj.solid_geometry
if gaps == 'None':
pass
else:
object_geo = cutout_obj.solid_geometry
if gaps == '8' or gaps == '2LR':
points = (
xxmin - gapsize, # botleft_x
py - gapsize + leny / 4, # botleft_y
xxmax + gapsize, # topright_x
py + gapsize + leny / 4 # topright_y
)
geom = self.subtract_poly_from_geo(geom, points)
r_temp_geo.append(
self.intersect_geo(initial_geo, box(points[0], points[1], points[2], points[3]))
)
def cutout_handler(geom):
proc_geometry = []
rest_geometry = []
r_temp_geo = []
initial_geo = deepcopy(geom)
points = (
xxmin - gapsize,
py - gapsize - leny / 4,
xxmax + gapsize,
py + gapsize - leny / 4
)
geom = self.subtract_poly_from_geo(geom, points)
r_temp_geo.append(
self.intersect_geo(initial_geo, box(points[0], points[1], points[2], points[3]))
)
# Get min and max data for each object as we just cut rectangles across X or Y
xxmin, yymin, xxmax, yymax = CutOut.recursive_bounds(geom)
if gaps == '8' or gaps == '2TB':
points = (
px - gapsize + lenx / 4,
yymin - gapsize,
px + gapsize + lenx / 4,
yymax + gapsize
)
geom = self.subtract_poly_from_geo(geom, points)
r_temp_geo.append(
self.intersect_geo(initial_geo, box(points[0], points[1], points[2], points[3]))
)
px = 0.5 * (xxmin + xxmax) + margin
py = 0.5 * (yymin + yymax) + margin
lenx = (xxmax - xxmin) + (margin * 2)
leny = (yymax - yymin) + (margin * 2)
points = (
px - gapsize - lenx / 4,
yymin - gapsize,
px + gapsize - lenx / 4,
yymax + gapsize
)
geom = self.subtract_poly_from_geo(geom, points)
r_temp_geo.append(
self.intersect_geo(initial_geo, box(points[0], points[1], points[2], points[3]))
)
if gaps == 'None':
pass
else:
if gaps == '8' or gaps == '2LR':
points = (
xxmin - gapsize, # botleft_x
py - gapsize + leny / 4, # botleft_y
xxmax + gapsize, # topright_x
py + gapsize + leny / 4 # topright_y
)
geom = self.subtract_poly_from_geo(geom, points)
r_temp_geo.append(
self.intersect_geo(initial_geo, box(points[0], points[1], points[2], points[3]))
)
if gaps == '4' or gaps == 'LR':
points = (
xxmin - gapsize,
py - gapsize,
xxmax + gapsize,
py + gapsize
)
geom = self.subtract_poly_from_geo(geom, points)
r_temp_geo.append(
self.intersect_geo(initial_geo, box(points[0], points[1], points[2], points[3]))
)
points = (
xxmin - gapsize,
py - gapsize - leny / 4,
xxmax + gapsize,
py + gapsize - leny / 4
)
geom = self.subtract_poly_from_geo(geom, points)
r_temp_geo.append(
self.intersect_geo(initial_geo, box(points[0], points[1], points[2], points[3]))
)
if gaps == '4' or gaps == 'TB':
points = (
px - gapsize,
yymin - gapsize,
px + gapsize,
yymax + gapsize
)
geom = self.subtract_poly_from_geo(geom, points)
r_temp_geo.append(
self.intersect_geo(initial_geo, box(points[0], points[1], points[2], points[3]))
)
if gaps == '8' or gaps == '2TB':
points = (
px - gapsize + lenx / 4,
yymin - gapsize,
px + gapsize + lenx / 4,
yymax + gapsize
)
geom = self.subtract_poly_from_geo(geom, points)
r_temp_geo.append(
self.intersect_geo(initial_geo, box(points[0], points[1], points[2], points[3]))
)
points = (
px - gapsize - lenx / 4,
yymin - gapsize,
px + gapsize - lenx / 4,
yymax + gapsize
)
geom = self.subtract_poly_from_geo(geom, points)
r_temp_geo.append(
self.intersect_geo(initial_geo, box(points[0], points[1], points[2], points[3]))
)
if gaps == '4' or gaps == 'LR':
points = (
xxmin - gapsize,
py - gapsize,
xxmax + gapsize,
py + gapsize
)
geom = self.subtract_poly_from_geo(geom, points)
r_temp_geo.append(
self.intersect_geo(initial_geo, box(points[0], points[1], points[2], points[3]))
)
if gaps == '4' or gaps == 'TB':
points = (
px - gapsize,
yymin - gapsize,
px + gapsize,
yymax + gapsize
)
geom = self.subtract_poly_from_geo(geom, points)
r_temp_geo.append(
self.intersect_geo(initial_geo, box(points[0], points[1], points[2], points[3]))
)
try:
for g in geom:
if g and not g.is_empty:
proc_geometry.append(g)
except TypeError:
if geom and not geom.is_empty:
proc_geometry.append(geom)
r_temp_geo = CutOut.flatten(r_temp_geo)
for g in r_temp_geo:
try:
for g in geom:
if g and not g.is_empty:
rest_geometry.append(g)
proc_geometry.append(g)
except TypeError:
if geom and not geom.is_empty:
proc_geometry.append(geom)
return proc_geometry, rest_geometry
r_temp_geo = CutOut.flatten(r_temp_geo)
for g in r_temp_geo:
if g and not g.is_empty:
rest_geometry.append(g)
if kind == 'single':
object_geo = unary_union(object_geo)
return proc_geometry, rest_geometry
with self.app.proc_container.new("Generating Cutout ..."):
outname = cutout_obj.options["name"] + "_cutout"
self.app.collection.promise(outname)
outname_exc = cutout_obj.options["name"] + "_mouse_bites"
if self.ui.gaptype_radio.get_value() == 'mb':
self.app.collection.promise(outname_exc)
def job_thread(app_obj):
solid_geo = []
gaps_solid_geo = []
mouse_bites_geo = []
convex_box = self.ui.convex_box_cb.get_value()
gapsize = self.ui.gapsize.get_value()
gapsize = gapsize / 2 + (dia / 2)
# for geo in object_geo:
if cutout_obj.kind == 'gerber':
if isinstance(object_geo, MultiPolygon):
x0, y0, x1, y1 = object_geo.bounds
object_geo = box(x0, y0, x1, y1)
if margin >= 0:
geo_buf = object_geo.buffer(margin + abs(dia / 2))
else:
geo_buf = object_geo.buffer(margin - abs(dia / 2))
if isinstance(cutout_obj.solid_geometry, list):
cutout_obj.solid_geometry = MultiPolygon(cutout_obj.solid_geometry)
geo = geo_buf.exterior
else:
geo = object_geo
solid_geo, rest_geo = cutout_handler(geom=geo)
if self.ui.gaptype_radio.get_value() == 'bt' and self.ui.thin_depth_entry.get_value() > 0:
gaps_solid_geo = rest_geo
else:
try:
__ = iter(object_geo)
except TypeError:
object_geo = [object_geo]
for geom_struct in object_geo:
if cutout_obj.kind == 'gerber':
if margin >= 0:
geom_struct = (geom_struct.buffer(margin + abs(dia / 2))).exterior
try:
if convex_box:
object_geo = cutout_obj.solid_geometry.convex_hull
else:
geom_struct_buff = geom_struct.buffer(-margin + abs(dia / 2))
geom_struct = geom_struct_buff.interiors
object_geo = cutout_obj.solid_geometry
except Exception as err:
log.debug("CutOut.on_freeform_cutout().geo_init() --> %s" % str(err))
object_geo = cutout_obj.solid_geometry
else:
object_geo = cutout_obj.solid_geometry
c_geo, r_geo = cutout_handler(geom=geom_struct)
solid_geo += c_geo
if kind == 'single':
object_geo = unary_union(object_geo)
# for geo in object_geo:
if cutout_obj.kind == 'gerber':
if isinstance(object_geo, MultiPolygon):
x0, y0, x1, y1 = object_geo.bounds
object_geo = box(x0, y0, x1, y1)
if margin >= 0:
geo_buf = object_geo.buffer(margin + abs(dia / 2))
else:
geo_buf = object_geo.buffer(margin - abs(dia / 2))
geo = geo_buf.exterior
else:
geo = object_geo
solid_geo, rest_geo = cutout_handler(geom=geo, gapsize=gapsize)
if self.ui.gaptype_radio.get_value() == 'bt' and self.ui.thin_depth_entry.get_value() > 0:
gaps_solid_geo += r_geo
gaps_solid_geo = rest_geo
if self.ui.gaptype_radio.get_value() == 'mb':
mouse_bites_geo = rest_geo
else:
try:
__ = iter(object_geo)
except TypeError:
object_geo = [object_geo]
if not solid_geo:
app_obj.inform.emit('[ERROR_NOTCL] %s' % _("Failed."))
return "fail"
for geom_struct in object_geo:
if cutout_obj.kind == 'gerber':
if margin >= 0:
geom_struct = (geom_struct.buffer(margin + abs(dia / 2))).exterior
else:
geom_struct_buff = geom_struct.buffer(-margin + abs(dia / 2))
geom_struct = geom_struct_buff.interiors
solid_geo = linemerge(solid_geo)
geo_obj.solid_geometry = deepcopy(solid_geo)
c_geo, r_geo = cutout_handler(geom=geom_struct, gapsize=gapsize)
solid_geo += c_geo
if self.ui.gaptype_radio.get_value() == 'bt' and self.ui.thin_depth_entry.get_value() > 0:
gaps_solid_geo += r_geo
if self.ui.gaptype_radio.get_value() == 'mb':
mouse_bites_geo += r_geo
if not solid_geo:
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed."))
return "fail"
xmin, ymin, xmax, ymax = CutOut.recursive_bounds(geo_obj.solid_geometry)
geo_obj.options['xmin'] = xmin
geo_obj.options['ymin'] = ymin
geo_obj.options['xmax'] = xmax
geo_obj.options['ymax'] = ymax
geo_obj.options['cnctooldia'] = str(dia)
geo_obj.options['cutz'] = self.ui.cutz_entry.get_value()
geo_obj.options['multidepth'] = self.ui.mpass_cb.get_value()
geo_obj.options['depthperpass'] = self.ui.maxdepth_entry.get_value()
solid_geo = linemerge(solid_geo)
geo_obj.multigeo = True
# list of Shapely Points to mark the drill points centers
holes = []
print(mouse_bites_geo)
geo_obj.tools.update({
1: self.cut_tool_dict
})
geo_obj.tools[1]['tooldia'] = str(dia)
geo_obj.tools[1]['solid_geometry'] = geo_obj.solid_geometry
def geo_init(geo_obj, app_object):
geo_obj.solid_geometry = deepcopy(solid_geo)
geo_obj.tools[1]['data']['name'] = outname
geo_obj.tools[1]['data']['cutz'] = self.ui.cutz_entry.get_value()
geo_obj.tools[1]['data']['multidepth'] = self.ui.mpass_cb.get_value()
geo_obj.tools[1]['data']['depthperpass'] = self.ui.maxdepth_entry.get_value()
xmin, ymin, xmax, ymax = CutOut.recursive_bounds(geo_obj.solid_geometry)
geo_obj.options['xmin'] = xmin
geo_obj.options['ymin'] = ymin
geo_obj.options['xmax'] = xmax
geo_obj.options['ymax'] = ymax
geo_obj.options['cnctooldia'] = str(dia)
geo_obj.options['cutz'] = self.ui.cutz_entry.get_value()
geo_obj.options['multidepth'] = self.ui.mpass_cb.get_value()
geo_obj.options['depthperpass'] = self.ui.maxdepth_entry.get_value()
if gaps_solid_geo is not None:
geo_obj.tools.update({
9999: self.cut_tool_dict
})
geo_obj.tools[9999]['tooldia'] = str(dia)
geo_obj.tools[9999]['solid_geometry'] = gaps_solid_geo
geo_obj.multigeo = True
geo_obj.tools[9999]['data']['name'] = outname
geo_obj.tools[9999]['data']['cutz'] = self.ui.thin_depth_entry.get_value()
geo_obj.tools[9999]['data']['multidepth'] = self.ui.mpass_cb.get_value()
geo_obj.tools[9999]['data']['depthperpass'] = self.ui.maxdepth_entry.get_value()
# plot this tool in a different color
geo_obj.tools[9999]['data']['override_color'] = "#29a3a3fa"
geo_obj.tools.update({
1: self.cut_tool_dict
})
geo_obj.tools[1]['tooldia'] = str(dia)
geo_obj.tools[1]['solid_geometry'] = geo_obj.solid_geometry
outname = cutout_obj.options["name"] + "_cutout"
ret = self.app.app_obj.new_object('geometry', outname, geo_init)
geo_obj.tools[1]['data']['name'] = outname
geo_obj.tools[1]['data']['cutz'] = self.ui.cutz_entry.get_value()
geo_obj.tools[1]['data']['multidepth'] = self.ui.mpass_cb.get_value()
geo_obj.tools[1]['data']['depthperpass'] = self.ui.maxdepth_entry.get_value()
if ret == 'fail':
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed."))
return
if gaps_solid_geo:
geo_obj.tools.update({
9999: self.cut_tool_dict
})
geo_obj.tools[9999]['tooldia'] = str(dia)
geo_obj.tools[9999]['solid_geometry'] = gaps_solid_geo
# cutout_obj.plot(plot_tool=1)
self.app.inform.emit('[success] %s' % _("Any form CutOut operation finished."))
# self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
self.app.should_we_save = True
geo_obj.tools[9999]['data']['name'] = outname
geo_obj.tools[9999]['data']['cutz'] = self.ui.thin_depth_entry.get_value()
geo_obj.tools[9999]['data']['multidepth'] = self.ui.mpass_cb.get_value()
geo_obj.tools[9999]['data']['depthperpass'] = self.ui.maxdepth_entry.get_value()
# plot this tool in a different color
geo_obj.tools[9999]['data']['override_color'] = "#29a3a3fa"
def excellon_init(exc_obj, app_o):
if not holes:
return 'fail'
mb_dia = self.ui.mb_dia_entry.get_value()
tools = {}
tools[1] = {}
tools[1]["tooldia"] = mb_dia
tools[1]['drills'] = holes
tools[1]['solid_geometry'] = []
exc_obj.tools = tools
exc_obj.create_geometry()
exc_obj.source_file = app_o.export_excellon(obj_name=exc_obj.options['name'], local_use=exc_obj,
filename=None, use_thread=False)
# calculate the bounds
xmin, ymin, xmax, ymax = CutOut.recursive_bounds(exc_obj.solid_geometry)
exc_obj.options['xmin'] = xmin
exc_obj.options['ymin'] = ymin
exc_obj.options['xmax'] = xmax
exc_obj.options['ymax'] = ymax
try:
ret = app_obj.app_obj.new_object('geometry', outname, geo_init)
if ret == 'fail':
app_obj.inform.emit('[ERROR_NOTCL] %s' % _("Failed."))
return
if self.ui.gaptype_radio.get_value() == 'mb':
ret = app_obj.app_obj.new_object('excellon', outname_exc, excellon_init)
if ret == 'fail':
app_obj.inform.emit('[ERROR_NOTCL] %s' % _("Mouse bites failed."))
return
# cutout_obj.plot(plot_tool=1)
app_obj.inform.emit('[success] %s' % _("Any form CutOut operation finished."))
# self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
app_obj.should_we_save = True
except Exception as ee:
log.debug(str(ee))
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
def on_rectangular_cutout(self):
log.debug("Cutout.on_rectangular_cutout() was launched ...")
@ -875,7 +925,7 @@ class CutOut(AppTool):
xmin - gapsize, # botleft_x
py - gapsize + leny / 4, # botleft_y
xmax + gapsize, # topright_x
py + gapsize + leny / 4 # topright_y
py + gapsize + leny / 4 # topright_y
)
geom = self.subtract_poly_from_geo(geom, points)
points = (
@ -1660,14 +1710,13 @@ class CutOut(AppTool):
if second_geo.intersects(geo):
results.append(second_geo.intersection(geo))
return CutOut.flatten(results)
return CutOut.flatten(results)
def reset_fields(self):
self.ui.obj_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
class CutoutUI:
toolName = _("Cutout PCB")
def __init__(self, layout, app):
@ -1898,9 +1947,9 @@ class CutoutUI:
self.gaptype_radio = RadioSet(
[
{'label': _('Bridge'), 'value': 'b'},
{'label': _('Thin'), 'value': 'bt'},
{'label': "M-Bites", 'value': 'mb'}
{'label': _('Bridge'), 'value': 'b'},
{'label': _('Thin'), 'value': 'bt'},
{'label': "M-Bites", 'value': 'mb'}
],
stretch=True
)

View File

@ -2,7 +2,7 @@
from PyQt5 import QtWidgets, QtCore, QtGui
from appTool import AppTool
from appGUI.GUIElements import RadioSet, FCDoubleSpinner, EvalEntry, FCEntry, FCButton, FCComboBox, FCCheckBox
from appGUI.GUIElements import RadioSet, FCDoubleSpinner, FCButton, FCComboBox, NumericalEvalTupleEntry
from numpy import Inf
@ -156,27 +156,19 @@ class DblSidedTool(AppTool):
try:
px, py = self.ui.point_entry.get_value()
except TypeError:
self.app.inform.emit('[WARNING_NOTCL] %s' % _("'Point' reference is selected and 'Point' coordinates "
"are missing. Add them and retry."))
msg = '[WARNING_NOTCL] %s' % \
_("'Point' reference is selected and 'Point' coordinates are missing. Add them and retry.")
self.app.inform.emit(msg)
return
else:
selection_index = self.ui.box_combo.currentIndex()
model_index = self.app.collection.index(selection_index, 0, self.ui.gerber_object_combo.rootModelIndex())
model_index = self.app.collection.index(selection_index, 0, self.ui.object_combo.rootModelIndex())
try:
bb_obj = model_index.internalPointer().obj
except AttributeError:
model_index = self.app.collection.index(selection_index, 0, self.ui.exc_object_combo.rootModelIndex())
try:
bb_obj = model_index.internalPointer().obj
except AttributeError:
model_index = self.app.collection.index(selection_index, 0,
self.ui.geo_object_combo.rootModelIndex())
try:
bb_obj = model_index.internalPointer().obj
except AttributeError:
self.app.inform.emit(
'[WARNING_NOTCL] %s' % _("There is no Box reference object loaded. Load one and retry."))
return
msg = '[WARNING_NOTCL] %s' % _("There is no Box reference object loaded. Load one and retry.")
self.app.inform.emit(msg)
return
xmin, ymin, xmax, ymax = bb_obj.bounds()
px = 0.5 * (xmin + xmax)
@ -184,10 +176,10 @@ class DblSidedTool(AppTool):
xscale, yscale = {"X": (1.0, -1.0), "Y": (-1.0, 1.0)}[axis]
dia = float(self.drill_dia.get_value())
dia = self.ui.drill_dia.get_value()
if dia == '':
self.app.inform.emit('[WARNING_NOTCL] %s' %
_("No value or wrong format in Drill Dia entry. Add it and retry."))
msg = '[WARNING_NOTCL] %s' % _("No value or wrong format in Drill Dia entry. Add it and retry.")
self.app.inform.emit(msg)
return
tools = {}
@ -198,8 +190,8 @@ class DblSidedTool(AppTool):
# holes = self.alignment_holes.get_value()
holes = eval('[{}]'.format(self.ui.alignment_holes.text()))
if not holes:
self.app.inform.emit('[WARNING_NOTCL] %s' % _("There are no Alignment Drill Coordinates to use. "
"Add them and retry."))
msg = '[WARNING_NOTCL] %s' % _("There are no Alignment Drill Coordinates to use. Add them and retry.")
self.app.inform.emit(msg)
return
for hole in holes:
@ -629,7 +621,7 @@ class DsidedUI:
grid0.addWidget(self.ymax_entry, 10, 1)
# Center point value
self.center_entry = FCEntry()
self.center_entry = NumericalEvalTupleEntry(border_color='#0069A9')
self.center_entry.setPlaceholderText(_("Center point coordinates"))
self.center_btn = FCButton('%s:' % _("Centroid"))
@ -714,7 +706,7 @@ class DsidedUI:
grid1.addWidget(self.axis_location, 4, 1, 1, 2)
# ## Point/Box
self.point_entry = EvalEntry()
self.point_entry = NumericalEvalTupleEntry(border_color='#0069A9')
self.point_entry.setPlaceholderText(_("Point coordinates"))
# Add a reference
@ -876,7 +868,7 @@ class DsidedUI:
"It can be modified in the Mirror Parameters -> Reference section")
)
self.align_ref_label_val = EvalEntry()
self.align_ref_label_val = NumericalEvalTupleEntry(border_color='#0069A9')
self.align_ref_label_val.setToolTip(
_("The reference point used to create the second alignment drill\n"
"from the first alignment drill, by doing mirror.\n"
@ -900,7 +892,7 @@ class DsidedUI:
"- one drill in mirror position over the axis selected above in the 'Align Axis'.")
)
self.alignment_holes = EvalEntry()
self.alignment_holes = NumericalEvalTupleEntry(border_color='#0069A9')
self.alignment_holes.setPlaceholderText(_("Drill coordinates"))
grid5.addWidget(self.ah_label, 0, 0, 1, 2)

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

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